FBX SDK的使用:读取Mesh
读取顶点数据
要将一个Mesh渲染出来,必须要有顶点的位置,法线,UV等顶点属性,和三角面的顶点索引数组。在提取这些数据之前,先理解FBX SDK里面的几个概念:
- Control Point 顶点的位置,就是x,y,z坐标;
- Polygon Vertex Control Point的索引,Control Point存在一个数组里面,根据这个索引去获取它的值;
- Polygon 表示一个面,可以是三角面,或多边形面,这里只出去三角面,游戏面也都是三角面;
法线,UV等都是FbxMesh的几何元素,可以通过FbxMesh::GetElementNormal(), FbxMesh::GetElementUV()接口去获取,我们知道可能有多层UV,这个接口可以传一个int参数去表示获取的是第几层UV,法线也是同理。这里我们只读取第0层法线和UV。
法线,UV的读取和位置相似,都是先获取一个索引,然后通过索引去数组里面查找对应的值,但是索引和数组要根据映射和引用模式去获取。
MappingMode,对于法线和UV一般使用下面两种模式
- eByControlPoint:和control point的索引相同,就是上面的Polygon Vertex;
- eByPolygonVertex:顶点的索引,不好解释,直接看下面的代码
ReferenceMode,数据放在DirectArray里面,IndexArray存储DirectArray的索引
- eDirect:直接通过DirectArray读取
- eIndex:兼容旧版本,同eIndexToDirect
- eIndexToDirect:先通过IndexArray获取索引,再通过DirectArray获取值
这里读取出来的数据,顶点数组的长度和索引数组的长度相同,不做顶点去重。切线和副切线的读取方式和法线相同。顶点索引使用的是polygonIdx * 3 + i,读取来的数据是直接在OpenGL使用的,因此顶点数据不需要做转换,如果是在DX使用,需要做转换,可以参考 FBX SDK的使用:基础知识 这篇文章。
struct Vertex
{float position[3];float normal[3];float texcoord[2];
};void ReadMesh(FbxMesh* pMesh, Vertex** pVertices, unsigned int** pIndices)
{int numTriangles = pMesh->GetPolygonCount(); // 三角面数量int numVertices = numTriangles * 3; Vertex* vertices = new Vertex[numVertices];unsigned int* indices = new unsigned int[numVertices];*pVertices = vertices;*pIndices = indices;int vertexIdx = 0;FbxVector4* lCtrPoints = pMesh->GetControlPoints();for (int polygonIdx = 0; polygonIdx < numTriangles; polygonIdx++){// 只处理三角形int lPolygonSize = pMesh->GetPolygonSize(polygonIdx);if (lPolygonSize != 3){delete[] vertices;delete[] indices;printf("only process triangle, polygon size is %d \n", lPolygonSize);return nullptr;}for (int i = 0; i < lPolygonSize; i++){Vertex lVertex;// 读取位置int controlPointIndex = pMesh->GetPolygonVertex(polygonIdx, i);FbxVector4 lCtrPoint = lCtrPoints[controlPointIndex];lVertex.position[0] = lCtrPoint.mData[0];lVertex.position[1] = lCtrPoint.mData[1];lVertex.position[2] = lCtrPoint.mData[2];// 读取法线ReadNormal(pMesh, controlPointIndex, vertexIdx, lVertex);// 读取UVReadUV(pMesh, controlPointIndex, vertexIdx, lVertex);// 读取材质,下面的SubMesh使用ReadMat(pMesh, polygonIdx, lVertex);indices[vertexIdx] = vertexIdx;vertices[vertexIdx] = lVertex;vertexIdx++;}}
}// 读取法线
void FbxLoader::ReadNormal(FbxMesh* pMesh, int pCpIdx, int pVertexIdx, Vertex& pVertex)
{FbxLayerElementNormal* lLayerNormal = pMesh->GetElementNormal(0); // 第0层的法线FbxLayerElement::EReferenceMode lERefMode = lLayerNormal->GetReferenceMode();FbxLayerElement::EMappingMode lEMapMode = lLayerNormal->GetMappingMode();int lNormalIdx = 0;if (lEMapMode == FbxLayerElement::EMappingMode::eByControlPoint){lNormalIdx = pCpIdx;}else if (lEMapMode == FbxLayerElement::EMappingMode::eByPolygonVertex){lNormalIdx = pVertexIdx;}FbxVector4 lNormal;if (lERefMode == FbxLayerElement::EReferenceMode::eDirect){lNormal = lLayerNormal->GetDirectArray().GetAt(lNormalIdx);}else if (lEMapMode == FbxLayerElement::EReferenceMode::eIndexToDirect){int lIndex = lLayerNormal->GetIndexArray().GetAt(lNormalIdx);lNormal = lLayerNormal->GetDirectArray().GetAt(lIndex);}pVertex.normal[0] = lNormal.mData[0];pVertex.normal[1] = lNormal.mData[1];pVertex.normal[2] = lNormal.mData[2];
}// 读取UV
void ReadUV(FbxMesh* pMesh, int pCpIdx, int pVertexIdx, Vertex& pVertex)
{FbxLayerElementUV* lLayerUV = pMesh->GetElementUV(0); // 第0层的UVint lUVIdx = 0;switch (lLayerUV->GetMappingMode()){case FbxLayerElement::EMappingMode::eByControlPoint:lUVIdx = pCpIdx;break;case FbxLayerElement::EMappingMode::eByPolygonVertex:lUVIdx = pVertexIdx;break;default:return;}FbxVector4 lUV;int index = 0;switch (lLayerUV->GetReferenceMode()){case FbxLayerElement::EReferenceMode::eDirect:lUV = lLayerUV->GetDirectArray().GetAt(lUVIdx);break;case FbxLayerElement::EReferenceMode::eIndexToDirect:index = lLayerUV->GetIndexArray().GetAt(lUVIdx);lUV = lLayerUV->GetDirectArray().GetAt(index);break;default:return;}pVertex.texcoord[0] = lUV.mData[0];pVertex.texcoord[1] = lUV.mData[1];
}
SubMesh
首先SubMesh是顶点索引数组的一部分,作用是一个Mesh的不同部分可以使用不同的材质,比如眼镜的镜框和镜片,对应3DMax里面材质ID的功能,给不同的Polygon设置不同的材质ID,通过Multi/Sub-Object材质给不同的材质ID指定不同的材质。因此,处理SubMesh主要是收集具有相同材质ID的顶点索引。
int FbxLoader::ReadMat(FbxMesh* pMesh, int pPolygonIdx, Vertex& pVertex)
{FbxLayerElementMaterial* leMat = pMesh->GetElementMaterial();// 材质一般是ePolygon映射,这里默认。 int matIdx = leMat->GetIndexArray().GetAt(pPolygonIdx);int index = 0;FbxSurfaceMaterial* surfaceMat = nullptr;switch (leMat->GetReferenceMode()){case FbxLayerElement::EReferenceMode::eDirect:surfaceMat = leMat->mDirectArray->GetAt(matIdx); // GetDirectArray()是私有方法,直接访问字段break;case FbxLayerElement::EReferenceMode::eIndexToDirect:index = leMat->GetIndexArray().GetAt(matIdx);surfaceMat = leMat->mDirectArray->GetAt(index);break;default:return -1;}// 处理材质..........// 返回材质索引return matIdx;
}
SubMesh目前我的代码还没有支持,就先这样了。
相关文章:
FBX SDK的使用:读取Mesh
读取顶点数据 要将一个Mesh渲染出来,必须要有顶点的位置,法线,UV等顶点属性,和三角面的顶点索引数组。在提取这些数据之前,先理解FBX SDK里面的几个概念: Control Point 顶点的位置,就是x,y,z…...
EtherCAT主站IGH-- 49 -- 搭建xenomai系统及自己的IGH主站
EtherCAT主站IGH-- 49 -- 搭建xenomai系统及自己的IGH主站 0 Ubuntu18.04系统IGH博客、视频欣赏链接一 移植xenomai系统1,下载安装工具包2,下载linux内核及xenomai2.1,下载linux内核2.2,下载xenomai2.3,下载补丁ipipe2.4,解压缩包3,打补丁4,配置内核5,编译内核6,安装编译好的内…...
Java控制台登录系统示例代码
实现一个简单的登录系统需要包括用户输入用户名和密码、验证用户信息等功能。以下是一个简单的Java控制台登录系统示例代码。这个系统使用一个简单的用户信息存储方式(如数组或哈希表),并提供基本的登录验证功能。 示例代码 import java.ut…...
S4 HANA明确税金汇差科目(OBYY)
本文主要介绍在S4 HANA OP中明确税金汇差科目(OBYY)相关设置。具体请参照如下内容: 1. 明确税金汇差科目(OBYY) 以上配置点定义了在外币挂账时,当凭证抬头汇率和税金行项目汇率不一致时,造成的差异金额进入哪个科目。此类情况只发生在FB60/F…...
Web-3.0(Solidity)基础教程
Solidity 是 以太坊智能合约编程语言,用于编写 去中心化应用(DApp)。如果你想开发 Web3.0 应用,Solidity 是必学的。 Remix - Ethereum IDE(在线编写 Solidity) 特性Remix IDEHardhat适用场景适合 初学者 …...
深入理解linux中的文件(上)
1.前置知识: (1)文章 内容 属性 (2)访问文件之前,都必须打开它(打开文件,等价于把文件加载到内存中) 如果不打开文件,文件就在磁盘中 (3&am…...
背包问题和单调栈
背包问题(动态规划) 动态五步曲 dp数组及下标索引的含义递推公式dp数组如何初始化遍历顺序打印dp数组 01背包:n种物品,有一个,二维数组遍历顺序可以颠倒,(滚动数组)一维数组遍历顺序不可颠倒…...
Airflow:深入理解Apache Airflow Task
Apache Airflow是一个开源工作流管理平台,支持以编程方式编写、调度和监控工作流。由于其灵活性、可扩展性和强大的社区支持,它已迅速成为编排复杂数据管道的首选工具。在这篇博文中,我们将深入研究Apache Airflow 中的任务概念,探…...
WebSocket——环境搭建与多环境配置
一、前言:为什么要使用多环境配置? 在开发过程中,我们通常会遇到多个不同的环境,比如开发环境(Dev)、测试环境(Test)、生产环境(Prod)等。每个环境的配置和需…...
93,【1】buuctf web [网鼎杯 2020 朱雀组]phpweb
进入靶场 页面一直在刷新 在 PHP 中,date() 函数是一个非常常用的处理日期和时间的函数,所以应该用到了 再看看警告的那句话 Warning: date(): It is not safe to rely on the systems timezone settings. You are *required* to use the date.timez…...
ChatGPT怎么回事?
纯属发现,调侃一下~ 这段时间deepseek不是特别火吗,尤其是它的推理功能,突发奇想,想用deepseek回答一些问题,回答一个问题之后就回复服务器繁忙(估计还在被攻击吧~_~) 然后就转向了GPT…...
机器学习day7
自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测,对预测结果计算精确度和召回率及F1分数 代码 import numpy as np import torch import torch.nn as nn import torch.optim as optimizer import matplotlib.pyp…...
本地部署DeepSeek教程(Mac版本)
第一步、下载 Ollama 官网地址:Ollama 点击 Download 下载 我这里是 macOS 环境 以 macOS 环境为主 下载完成后是一个压缩包,双击解压之后移到应用程序: 打开后会提示你到命令行中运行一下命令,附上截图: 若遇…...
2月3日星期一今日早报简报微语报早读
2月3日星期一,农历正月初六,早报#微语早读。 1、多个景区发布公告:售票数量已达上限,请游客合理安排行程; 2、2025春节档总票房破70亿,《哪吒之魔童闹海》破31亿; 3、美宣布对中国商品加征10…...
202周日复盘(159)本周回顾
1、当日总结。 定价相关内容,学习与思考。 第一性原理,分析游戏成本的构成。 ------------- 2、周总结 大思路,细节设计都有进展,每天都挖坑与加工。 a 学习游戏思想 任天堂游戏研发四大标准,创新,直…...
Linux基础 ——tmux vim 以及基本的shell语法
Linux 基础 ACWING y总的Linux基础课,看讲义作作笔记。 tmux tmux 可以干嘛? tmux可以分屏多开窗口,可以进行多个任务,断线,不会自动杀掉正在进行的进程。 tmux – session(会话,多个) – window(多个…...
error: RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054
Descriptions: Solutions:...
WPF进阶 | WPF 动画特效揭秘:实现炫酷的界面交互效果
WPF进阶 | WPF 动画特效揭秘:实现炫酷的界面交互效果 前言一、WPF 动画基础概念1.1 什么是 WPF 动画1.2 动画的基本类型1.3 动画的核心元素 二、线性动画详解2.1 DoubleAnimation 的使用2.2 ColorAnimation 实现颜色渐变 三、关键帧动画深入3.1 DoubleAnimationUsin…...
DeepSeek 遭 DDoS 攻击背后:DDoS 攻击的 “千层套路” 与安全防御 “金钟罩”
当算力博弈升级为网络战争:拆解DDoS攻击背后的技术攻防战——从DeepSeek遇袭看全球网络安全新趋势 在数字化浪潮席卷全球的当下,网络已然成为人类社会运转的关键基础设施,深刻融入经济、生活、政务等各个领域。从金融交易的实时清算…...
本地部署DeepSeek-R1模型(新手保姆教程)
背景 最近deepseek太火了,无数的媒体都在报道,很多人争相着想本地部署试验一下。本文就简单教学一下,怎么本地部署。 首先大家要知道,使用deepseek有三种方式: 1.网页端或者是手机app直接使用 2.使用代码调用API …...
Scratch 《像素战场》系列综合游戏:像素战场游戏Ⅰ~Ⅲ 介绍
资源下载 Scratch《像素战场》系列综合游戏合集:像素战场游戏Ⅰ~Ⅲ压缩包 https://download.csdn.net/download/leyang0910/90332765 游戏操作介绍 Scratch 《像素战场Ⅰ》操作规则: 这是一款与朋友一起玩的 1v1 游戏。先赢得6轮胜利! WA…...
手机连接WIFI可以上网,笔记本电脑连接WIFI却不能上网? 解决方法?
原因:DNS受污染了 解决办法 step 1:清空域名解析记录(清空DNS) ipconfig /flushdns (Windows cmd命令行输入) step 2:重新从DHCP 获取IP ipconfig /release(释放当前IP地址) ipconfig /renew &…...
DRM系列七:Drm之CREATE_DUMB
本系列文章基于linux 5.15 DRM驱动的显存由GEM(Graphics execution management)管理。 一、创建流程 创建buf时,user层提供需要buf的width,height以及bpp(bite per pixel),然后调用drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &…...
Windows图形界面(GUI)-QT-C/C++ - QT Stacked Widget
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 一、概述 二、使用场景 1. 多步表单 2. 选项卡界面 3. 状态机界面 三、常见样式 四、属性设置 1. 页面管理 2. 布局管理 3. 信号与槽 五、内容处理 1. 添加页面 2. 移除页面 3.…...
二叉树——429,515,116
今天继续做关于二叉树层序遍历的相关题目,一共有三道题,思路都借鉴于最基础的二叉树的层序遍历。 LeetCode429.N叉树的层序遍历 这道题不再是二叉树了,变成了N叉树,也就是该树每一个节点的子节点数量不确定,可能为2&a…...
使用mybatisPlus插件生成代码步骤及注意事项
使用mybatisPlus插件可以很方便的生成与数据库对应的PO对象,以及对应的controller、service、ImplService、mapper代码,生成这种代码的方式有很多,包括mybatis-plus提供的代码生成器,以及idea提供的代码生成器,无论哪一…...
Apache Hudi数据湖技术应用在网络打车系统中的系统架构设计、软硬件配置、软件技术栈、具体实现流程和关键代码
网络打车系统利用Hudi数据湖技术成功地解决了其大规模数据处理和分析的难题,提高了数据处理效率和准确性,为公司的业务发展提供了有力的支持。 Apache Hudi数据湖技术的一个典型应用案例是网络打车系统的数据处理场景,具体如下: 大…...
TryHackMe: TryPwnMe Two
TryExecMe2 限制了直接进行系统调用,即syscall sysenter int 0x80,但是这样的限制是十分好绕过的,我们只需要通过异或生成syscall构造read再次写入shellcode即可 构造read shellcode asm(""" mov rdx, 0x100 mov r15, rdi…...
熵采样在分类任务中的应用
熵采样在分类任务中的应用 在机器学习的分类任务里,数据的标注成本常常制约着模型性能的提升。主动学习中的熵采样策略,为解决这一难题提供了新的思路。本文将带你深入了解熵采样在分类任务中的原理、应用及优势。 一、熵采样的原理(优化版) 熵,源于信息论,是对不确定…...
SmartPipe完成新一轮核心算法升级
1. 增加对低质量轴段的修正 由于三维图纸导出造成某些轴段精度较差,部分管路段的轴线段不满足G1连续,SmartPipe采用算法对这种情况进行了修正,保证轴段在一定精度范围内光滑连续。 2. 优化对中文路径的处理 SmartPipeBatch批处理版本优化…...
