1089 lines
32 KiB
C++
1089 lines
32 KiB
C++
#include "dl_model_loader.h"
|
||
|
||
#ifndef DL_DISABLE_RENDER
|
||
#define TINYOBJLOADER_IMPLEMENTATION
|
||
#include <tiny_obj_loader.h>
|
||
#include <stb_image_write.h>
|
||
#define TINYGLTF3_IMPLEMENTATION
|
||
//#define TINYGLTF3_ENABLE_FS // enable file I/O
|
||
//#define TINYGLTF3_ENABLE_STB_IMAGE // enable image decoding
|
||
#include <tiny_gltf_v3.h>
|
||
|
||
#include "math/dl_transform.h"
|
||
|
||
namespace dl
|
||
{
|
||
ModelLoader* g_modelLoader;
|
||
|
||
std::vector<ModelObject*> ModelLoader::_load_obj(std::string_view path_name)
|
||
{
|
||
std::string str_path = std::string{ path_name };
|
||
std::string str_base = String::EraseFilename(path_name);
|
||
|
||
const char* filename = str_path.c_str();
|
||
const char* basepath = str_base.c_str();
|
||
bool triangulate = true;
|
||
|
||
log_info("开始加载obj文件:{},{}", filename, basepath);
|
||
|
||
tinyobj::attrib_t attrib; // 所有的数据放在这里
|
||
std::vector<tinyobj::shape_t> shapes;// 子模型
|
||
std::vector<tinyobj::material_t> materials;
|
||
|
||
std::string warn;
|
||
std::string err;
|
||
|
||
bool b_read = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename,
|
||
basepath, triangulate);
|
||
|
||
// 打印错误
|
||
if (!warn.empty())
|
||
log_warn("{}", warn);
|
||
|
||
if (!err.empty())
|
||
log_warn("{}", err);
|
||
|
||
if (!b_read)
|
||
{
|
||
log_err("读取obj文件失败:{}", path_name);
|
||
return {};
|
||
}
|
||
log_info("顶点数:{}", attrib.vertices.size() / 3);
|
||
log_info("法线数:{}", attrib.normals.size() / 3);
|
||
log_info("UV数:{}", attrib.texcoords.size() / 2);
|
||
log_info("子模型数:{}", shapes.size());
|
||
log_info("材质数:{}", materials.size());
|
||
|
||
// 读取配置
|
||
bool is_axis_up_z = false;
|
||
{
|
||
Json::DisableLog l;
|
||
json j = Json::ParseFile(String::ReplaceExtension(path_name, "json"));
|
||
if (!j.is_null())
|
||
{
|
||
std::string str;
|
||
if (Json::Get(j, "axis_up", str))
|
||
{
|
||
if (str == "z")
|
||
is_axis_up_z = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// obj只有一个ModelObject
|
||
std::vector<ModelObject*> ret;
|
||
ModelObject* model_object = new ModelObject;
|
||
std::vector<Vertex>& mo_vertices = model_object->_allVertex;
|
||
std::vector<uint32_t>& mo_indices = model_object->_allIndex;
|
||
std::vector<ModelMaterial>& mo_material = model_object->_allMaterial;
|
||
ret.push_back(model_object);
|
||
|
||
//------------------获取材质-------------------
|
||
mo_material.resize(materials.size());
|
||
for (size_t i = 0; i < materials.size(); ++i)
|
||
{
|
||
tinyobj::material_t m = materials[i];
|
||
log_info("材质:{},{}", i, m.name);
|
||
|
||
ModelMaterial& material = mo_material[i];
|
||
material._materialName = m.name;
|
||
|
||
material._material._baseColorFactor = { m.diffuse[0], m.diffuse[1], m.diffuse[2], 1.0f };
|
||
// todo
|
||
//material._material._metallic = m.metallic;
|
||
//material._material._roughness = m.roughness;
|
||
|
||
if (!m.diffuse_texname.empty())
|
||
material._pathTexDiffuse = str_base + m.diffuse_texname;
|
||
if (!m.normal_texname.empty())// 注意这里凹凸贴图(bump_texname)更常见
|
||
material._pathTexNormal = str_base + m.normal_texname;
|
||
}
|
||
|
||
//------------------获取模型-------------------
|
||
// 按 材质 放入面的顶点
|
||
std::vector<std::vector<tinyobj::index_t>> all_sub;
|
||
all_sub.resize(1 + materials.size());// 0为默认
|
||
|
||
for (size_t i = 0; i < shapes.size(); i++)
|
||
{// 每一个子shape
|
||
tinyobj::shape_t& shape = shapes[i];
|
||
size_t num_index = shape.mesh.indices.size();
|
||
size_t num_face = shape.mesh.num_face_vertices.size();
|
||
log_info("读取子模型:{},{}", i, shape.name);
|
||
log_info("索引数:{};面数:{}", num_index, num_face);
|
||
|
||
//当前mesh下标(每个面递增3)
|
||
size_t index_offset = 0;
|
||
//每一个面
|
||
for (size_t j = 0; j < num_face; ++j)
|
||
{
|
||
int index_mat = shape.mesh.material_ids[j];//每个面的材质
|
||
std::vector<tinyobj::index_t>& sub_idx = all_sub[1 + index_mat];
|
||
sub_idx.push_back(shape.mesh.indices[index_offset + 0]);
|
||
if (!is_axis_up_z)
|
||
{
|
||
sub_idx.push_back(shape.mesh.indices[index_offset + 1]);
|
||
sub_idx.push_back(shape.mesh.indices[index_offset + 2]);
|
||
}
|
||
else
|
||
{
|
||
sub_idx.push_back(shape.mesh.indices[index_offset + 2]);
|
||
sub_idx.push_back(shape.mesh.indices[index_offset + 1]);
|
||
}
|
||
index_offset += 3;
|
||
}
|
||
}
|
||
//生成子模型,并填入顶点
|
||
std::unordered_map<tinyobj::index_t, size_t, hash_idx, equal_idx>
|
||
uniqueVertices;//避免重复插入顶点
|
||
size_t i = 0;
|
||
size_t j = 0;
|
||
ModelNodeRt node;
|
||
for (std::vector<tinyobj::index_t>& sub_idx : all_sub)
|
||
{
|
||
ModelDrawCall sub;
|
||
sub._range._offsetIndex = i;
|
||
sub._range._countTriangle = sub_idx.size() / 3;
|
||
sub._indexMaterial = j - 1;
|
||
++j;
|
||
// todo node._allSub.push_back(sub);
|
||
|
||
for (tinyobj::index_t& idx : sub_idx)
|
||
{
|
||
auto iter = uniqueVertices.find(idx);
|
||
if (iter == uniqueVertices.end())
|
||
{
|
||
Vertex v;
|
||
//v
|
||
v._pos[0] = attrib.vertices[idx.vertex_index * 3 + 0];
|
||
if (!is_axis_up_z)
|
||
{
|
||
v._pos[1] = attrib.vertices[idx.vertex_index * 3 + 1];
|
||
v._pos[2] = attrib.vertices[idx.vertex_index * 3 + 2];
|
||
}
|
||
else
|
||
{
|
||
v._pos[1] = attrib.vertices[idx.vertex_index * 3 + 2];
|
||
v._pos[2] = attrib.vertices[idx.vertex_index * 3 + 1];
|
||
}
|
||
|
||
// vt
|
||
v._uv0[0] = attrib.texcoords[idx.texcoord_index * 2 + 0];
|
||
v._uv0[1] = attrib.texcoords[idx.texcoord_index * 2 + 1];
|
||
v._uv0[1] = 1.0f - v._uv0[1];
|
||
|
||
uniqueVertices[idx] = mo_vertices.size();
|
||
mo_indices.push_back((uint32_t)mo_vertices.size());
|
||
mo_vertices.push_back(v);
|
||
}
|
||
else
|
||
{
|
||
mo_indices.push_back((uint32_t)iter->second);
|
||
}
|
||
++i;
|
||
}
|
||
}
|
||
model_object->_treeNode.push_back(node);
|
||
log_msg("解析obj模型完成:v_{},i_{}", mo_vertices.size(), mo_indices.size());
|
||
return ret;
|
||
}
|
||
|
||
ModelObject* ModelLoader::_create_model_object(ModelData& data)
|
||
{
|
||
ModelObject* obj = new ModelObject;
|
||
obj->_allMaterial = data._allMaterial;
|
||
obj->_allAnimation = data._allAnimation;
|
||
obj->_allSkin = data._allSkin;
|
||
obj->_treeNodeParent.resize(data._allNode.GetSize());
|
||
obj->_allSub = data._allSub;
|
||
obj->_allIndex = data._allIndex;
|
||
if (obj->_allSkin.empty())
|
||
obj->_allVertex = data._allVertex;
|
||
else
|
||
obj->_allVertexBone = data._allVertexSkin;
|
||
|
||
// index -> vector_index
|
||
std::unordered_map<int, int> map_index;
|
||
|
||
data._allNode.ForEach([&](ModelNode* node) {
|
||
ModelNodeRt node_rt;
|
||
node_rt._index = node->_index;
|
||
node_rt._indexSkin = node->_indexSkin;
|
||
node_rt._trans2Parent = node->_trans2Parent;
|
||
node_rt._aabb = node->_mesh._aabb;
|
||
// parent_id 重映射
|
||
if (!node->_parent)
|
||
node_rt._indexParent = -1;
|
||
else
|
||
{
|
||
auto iter = map_index.find(node->_parent->_index);
|
||
if (iter == map_index.end())
|
||
node_rt._indexParent = -1;
|
||
else
|
||
node_rt._indexParent = iter->second;
|
||
}
|
||
|
||
map_index[node_rt._index] = (int)obj->_treeNode.size();
|
||
obj->_treeNode.push_back(node_rt);
|
||
});
|
||
|
||
return obj;
|
||
}
|
||
|
||
std::vector<ModelObject*> ModelLoader::_load_gltf(
|
||
std::string_view path_name, std::string_view res_name, bool is_glb)
|
||
{
|
||
tinygltf::Model model;
|
||
tinygltf::TinyGLTF loader;
|
||
std::string err;
|
||
std::string warn;
|
||
|
||
log_info("开始加载gltf文件:{},{}", res_name, path_name);
|
||
|
||
bool load_ok;
|
||
if (is_glb)
|
||
load_ok = loader.LoadBinaryFromFile(&model, &err, &warn, std::string{ path_name });
|
||
else
|
||
load_ok = loader.LoadASCIIFromFile(&model, &err, &warn, std::string{ path_name });
|
||
|
||
|
||
if (!warn.empty())
|
||
log_warn("{}", warn);
|
||
|
||
if (!err.empty())
|
||
log_err("{}", err);
|
||
|
||
if (!load_ok)
|
||
{
|
||
log_err("解析gltf格式失败:{}", err);
|
||
return {};
|
||
}
|
||
log_info("子模型数:{}", model.meshes.size());
|
||
log_info("材质数:{}", model.materials.size());
|
||
|
||
|
||
ModelData model_data;
|
||
|
||
_gltf_load_material(model, res_name, model_data._allMaterial);
|
||
_gltf_load_scene(model, model_data);
|
||
|
||
ModelObject* obj = _create_model_object(model_data);
|
||
std::vector<ModelObject*> ret;
|
||
ret.push_back(obj);
|
||
|
||
return ret;
|
||
}
|
||
|
||
Position4 _factor_cast(const std::vector<double>& vec)
|
||
{
|
||
return { (float)vec[0], (float)vec[1], (float)vec[2], 1.0f };
|
||
}
|
||
|
||
void ModelLoader::_gltf_load_material(const tinygltf::Model& model,
|
||
std::string_view res_name,
|
||
std::vector<ModelMaterial>& vec_material)
|
||
{
|
||
vec_material.resize(model.materials.size());
|
||
for (size_t i = 0; i < model.materials.size(); ++i)
|
||
{
|
||
const tinygltf::Material& m = model.materials[i];
|
||
log_info("材质:{},{}", i, m.name);
|
||
const tinygltf::PbrMetallicRoughness& pbr = m.pbrMetallicRoughness;
|
||
|
||
ModelMaterial& material = vec_material[i];
|
||
material._materialName = m.name;
|
||
|
||
material._material._baseColorFactor = _factor_cast(pbr.baseColorFactor);
|
||
material._material._emissiveFactor = _factor_cast(m.emissiveFactor);
|
||
|
||
// 纹理导出
|
||
// 存在复用的情况
|
||
std::unordered_map<int, std::string> map_path;
|
||
auto fn_texture = [&](int index, std::string_view tag) {
|
||
if (index == -1)
|
||
return std::string{};
|
||
auto iter = map_path.find(index);
|
||
if (iter != map_path.end())
|
||
return iter->second;
|
||
const tinygltf::Image& img = model.images[index];
|
||
if (!img.uri.empty())
|
||
return img.uri;
|
||
|
||
std::string path_name = fmt::format("{}{}{}#{}.png",
|
||
File::PathTemp(), res_name, tag, i);
|
||
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
path_name = CodeCvt::Utf8ToMultiByte(path_name);
|
||
#endif
|
||
stbi_write_png(path_name.c_str(),
|
||
img.width, img.height,
|
||
4, img.image.data(), 0);
|
||
|
||
map_path[index] = path_name;
|
||
return path_name;
|
||
};
|
||
material._pathTexDiffuse = fn_texture(pbr.baseColorTexture.index, "");
|
||
material._pathTexPhysical = fn_texture(pbr.metallicRoughnessTexture.index, "_physical");
|
||
material._pathTexNormal = fn_texture(m.normalTexture.index, "_normal");
|
||
material._pathTexAo = fn_texture(m.occlusionTexture.index, "_ao");
|
||
material._pathTexEmissvie = fn_texture(m.emissiveTexture.index, "_emissvie");
|
||
|
||
material._material._baseColorTextureSet = material._pathTexDiffuse.empty() ?
|
||
-1 : pbr.baseColorTexture.texCoord;
|
||
material._material._physicalDescriptorTextureSet = material._pathTexPhysical.empty() ?
|
||
-1 : pbr.metallicRoughnessTexture.texCoord;
|
||
material._material._normalTextureSet = material._pathTexNormal.empty() ?
|
||
-1 : m.normalTexture.texCoord;
|
||
material._material._occlusionTextureSet = material._pathTexAo.empty() ?
|
||
-1 : m.occlusionTexture.texCoord;
|
||
material._material._emissiveTextureSet = material._pathTexEmissvie.empty() ?
|
||
-1 : m.emissiveTexture.texCoord;
|
||
|
||
material._material._metallicFactor = (float)pbr.metallicFactor;
|
||
material._material._roughnessFactor = (float)pbr.roughnessFactor;
|
||
material._material._alphaMask = m.alphaMode == "MASK" ? 1.0f : 0.0f;
|
||
{
|
||
auto iter = m.additionalValues.find("alphaCutoff");
|
||
if (iter != m.additionalValues.end())
|
||
material._material._alphaMaskCutoff = (float)iter->second.Factor();
|
||
}
|
||
|
||
material._material._metallicFactor = (float)pbr.metallicFactor;
|
||
{
|
||
auto iter = m.additionalValues.find("alphaCutoff");
|
||
if (iter != m.additionalValues.end())
|
||
material._material._alphaMaskCutoff = (float)iter->second.Factor();
|
||
}
|
||
material._material._emissiveStrength = 1.0f;
|
||
{
|
||
auto iter = m.extensions.find("KHR_materials_emissive_strength");
|
||
if (iter != m.extensions.end())
|
||
{
|
||
if (iter->second.Has("emissiveStrength"))
|
||
{
|
||
auto value = iter->second.Get("emissiveStrength");
|
||
material._material._emissiveStrength = (float)value.Get<double>();
|
||
}
|
||
}
|
||
}
|
||
memset(material._material._pad, 0, sizeof(material._material._pad));
|
||
}
|
||
}
|
||
|
||
void ModelLoader::_gltf_get_node_prop(const tinygltf::Model& model, const tinygltf::Node& node,
|
||
size_t& vertex_count, size_t& index_count)
|
||
{
|
||
if (node.children.size() > 0)
|
||
{
|
||
for (size_t i = 0; i < node.children.size(); ++i)
|
||
{
|
||
_gltf_get_node_prop(model, model.nodes[node.children[i]], vertex_count, index_count);
|
||
}
|
||
}
|
||
if (node.mesh > -1)
|
||
{
|
||
const tinygltf::Mesh& mesh = model.meshes[node.mesh];
|
||
for (size_t i = 0; i < mesh.primitives.size(); ++i)
|
||
{
|
||
const tinygltf::Primitive& primitive = mesh.primitives[i];
|
||
vertex_count += model.accessors[primitive.attributes.find("POSITION")->second].count;
|
||
if (primitive.indices > -1)
|
||
{
|
||
index_count += model.accessors[primitive.indices].count;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void ModelLoader::_gltf_load_scene(const tinygltf::Model& model, ModelData& data)
|
||
{
|
||
assert(model.scenes.size() == 1);
|
||
// 只读一个场景
|
||
const tinygltf::Scene& scene = model.scenes[model.defaultScene > -1 ? model.defaultScene : 0];
|
||
|
||
// 统计顶点和索引
|
||
LoaderInfo loader_info{};
|
||
size_t vertex_count = 0;
|
||
size_t index_count = 0;
|
||
for (size_t i = 0; i < scene.nodes.size(); i++)
|
||
{
|
||
_gltf_get_node_prop(model, model.nodes[scene.nodes[i]], vertex_count, index_count);
|
||
}
|
||
//
|
||
data._allIndex.resize(index_count);
|
||
if (model.skins.empty())
|
||
data._allVertex.resize(index_count);
|
||
else
|
||
data._allVertexSkin.resize(index_count);
|
||
|
||
// 创建根
|
||
ModelNode* node_root = new ModelNode;
|
||
node_root->_index = 0;
|
||
node_root->_trans2Parent._mat =
|
||
Mat::CreateRotation(AXIS_Z, (float)Math::PI(1, 2)) *
|
||
Mat::CreateRotation(AXIS_X, (float)Math::PI(1, 2));
|
||
|
||
data._allNode.AddRoot(node_root);
|
||
// 读取节点
|
||
for (size_t i = 0; i < scene.nodes.size(); i++)
|
||
{
|
||
_gltf_load_node(model, scene.nodes[i], node_root, data);
|
||
}
|
||
|
||
// 读取动画
|
||
if (model.animations.size() > 0)
|
||
{
|
||
_gltf_load_animation(model, data);
|
||
}
|
||
// 读取蒙皮
|
||
_gltf_load_skin(model, data);
|
||
|
||
// 若是静态模型,则转为世界坐标系
|
||
if (data._allSkin.empty())
|
||
{
|
||
for (auto& iter : data._allNode)
|
||
{
|
||
ModelNode* node = iter->GetValue();
|
||
|
||
Matrix4 mat_world = _get_node_mat_world(node);
|
||
Matrix4 mat_n = Mat::GetInverse(mat_world);
|
||
mat_n.Transpose();
|
||
Matrix3 mat_t = Mat::ToMatrix3(mat_world);
|
||
|
||
for (auto& pri : node->_mesh._primitives)
|
||
{
|
||
// 是否多个pri会引用同一个顶点,有待验证
|
||
for (size_t i = pri._vertexIndex; i < pri._vertexIndex + pri._vertexCount; ++i)
|
||
{
|
||
Vertex& v = data._allVertex[i];
|
||
v._pos = Mat::ApplyDot(mat_world, v._pos);
|
||
v._normal = Mat::ApplyDir(mat_n, v._normal);
|
||
v._tangent = mat_t * v._tangent;
|
||
Math::Normalize(v._normal);
|
||
Math::Normalize(v._tangent);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// pri -> all_sub
|
||
for (auto& iter : data._allNode)
|
||
{
|
||
for (ModelPrimitive& pri : iter->GetValue()->_mesh._primitives)
|
||
{
|
||
ModelDrawCall sub;
|
||
sub._indexMaterial = pri._indexMaterial;
|
||
sub._range._offsetIndex = pri._indexBeg;
|
||
sub._range._countTriangle = pri._indexCount / 3;
|
||
data._allSub.push_back(sub);
|
||
}
|
||
}
|
||
// 子网格合并
|
||
if (true)
|
||
{
|
||
// 排序
|
||
std::map<size_t, ModelDrawCall*> map_pri;
|
||
for (ModelDrawCall& iter : data._allSub)
|
||
{
|
||
map_pri[iter._range._offsetIndex] = &iter;
|
||
}
|
||
// 合并网格连续,且材质相同的项
|
||
std::vector<ModelDrawCall> vec_sub;
|
||
ModelDrawCall* pre = nullptr;
|
||
//
|
||
for (auto& iter : map_pri)
|
||
{
|
||
ModelDrawCall* sub = iter.second;
|
||
|
||
if (pre
|
||
&& sub->_indexMaterial == pre->_indexMaterial
|
||
&& (sub->_range._offsetIndex
|
||
== pre->_range._offsetIndex + pre->_range._countTriangle * 3))
|
||
{// 是连续的
|
||
pre->_range._countTriangle += sub->_range._countTriangle;
|
||
}
|
||
else
|
||
{
|
||
vec_sub.push_back(*sub);
|
||
pre = &vec_sub.back();
|
||
}
|
||
}
|
||
log_info("子网格合并:{}->{}", map_pri.size(), vec_sub.size());
|
||
for (ModelDrawCall& sub : vec_sub)
|
||
{
|
||
log_info("合并后网格,起始:{},三角数:{},材质:{}",
|
||
sub._range._offsetIndex, sub._range._countTriangle, sub._indexMaterial);
|
||
}
|
||
|
||
std::swap(data._allSub, vec_sub);
|
||
int pause = 3;
|
||
}
|
||
}
|
||
|
||
void ModelLoader::_gltf_load_node(const tinygltf::Model& model, int node,
|
||
ModelNode* model_node_parent, ModelData& data)
|
||
{
|
||
const tinygltf::Node& nn = model.nodes[node];
|
||
|
||
ModelNode* model_node = new ModelNode;
|
||
model_node->_parent = model_node_parent;
|
||
model_node->_index = node + 1;
|
||
model_node->_indexSkin = nn.skin;
|
||
|
||
//
|
||
if (nn.translation.size() == 3)
|
||
{
|
||
model_node->_trans2Parent._t = _vec3_cast(nn.translation);
|
||
}
|
||
if (nn.rotation.size() == 4)
|
||
{
|
||
model_node->_trans2Parent._r = _quat_cast(nn.rotation);
|
||
}
|
||
if (nn.scale.size() == 3)
|
||
{
|
||
model_node->_trans2Parent._s = _vec3_cast(nn.scale);
|
||
}
|
||
if (nn.matrix.size() == 16)
|
||
{
|
||
model_node->_trans2Parent._mat = _mat4_cast(nn.matrix);
|
||
}
|
||
// gltf R(up:+y forward:+z)
|
||
// dl R(up:+z forward:+x)
|
||
|
||
// 构建树
|
||
data._allNode.Add(model_node, model_node_parent);
|
||
|
||
// 子节点加载
|
||
for (size_t i = 0; i < nn.children.size(); ++i)
|
||
{
|
||
_gltf_load_node(model, nn.children[i], model_node, data);
|
||
}
|
||
|
||
// 读取mesh
|
||
if (nn.mesh > -1)
|
||
{
|
||
const tinygltf::Mesh& mesh = model.meshes[nn.mesh];
|
||
ModelMesh model_mesh;
|
||
|
||
for (size_t j = 0; j < mesh.primitives.size(); j++)
|
||
{
|
||
const tinygltf::Primitive& primitive = mesh.primitives[j];
|
||
uint32_t vertex_start = (uint32_t)data._vertexPos;
|
||
uint32_t index_start = (uint32_t)data._indexPos;
|
||
uint32_t vertex_count = 0;
|
||
uint32_t index_count = 0;
|
||
Position3 pos_min{};
|
||
Position3 pos_max{};
|
||
bool has_skin = false;
|
||
bool has_index = primitive.indices > -1;
|
||
// Vertices
|
||
{
|
||
const float* buffer_pos = nullptr;
|
||
const float* buffer_normal = nullptr;
|
||
const float* buffer_uv0 = nullptr;
|
||
const float* buffer_uv1 = nullptr;
|
||
const float* buffer_color = nullptr;
|
||
const void* buffer_joint = nullptr;
|
||
const float* buffer_weight = nullptr;
|
||
|
||
int stride_pos;
|
||
int stride_normal;
|
||
int stride_uv0;
|
||
int stride_uv1;
|
||
int stride_color0;
|
||
int stride_joint;
|
||
int stride_weight;
|
||
|
||
int joint_comp_type;
|
||
|
||
// 位置属性必须存在
|
||
assert(primitive.attributes.find("POSITION") != primitive.attributes.end());
|
||
|
||
const tinygltf::Accessor& accessor_pos = model.accessors[primitive.attributes.find("POSITION")->second];
|
||
const tinygltf::BufferView& view_pos = model.bufferViews[accessor_pos.bufferView];
|
||
buffer_pos = reinterpret_cast<const float*>(&(model.buffers[view_pos.buffer].data[accessor_pos.byteOffset + view_pos.byteOffset]));
|
||
pos_min = _vec3_cast(accessor_pos.minValues);
|
||
pos_max = _vec3_cast(accessor_pos.maxValues);
|
||
vertex_count = static_cast<uint32_t>(accessor_pos.count);
|
||
stride_pos = accessor_pos.ByteStride(view_pos) ? (accessor_pos.ByteStride(view_pos) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3);
|
||
|
||
if (primitive.attributes.find("NORMAL") != primitive.attributes.end())
|
||
{
|
||
const tinygltf::Accessor& normAccessor = model.accessors[primitive.attributes.find("NORMAL")->second];
|
||
const tinygltf::BufferView& normView = model.bufferViews[normAccessor.bufferView];
|
||
buffer_normal = reinterpret_cast<const float*>(&(model.buffers[normView.buffer].data[normAccessor.byteOffset + normView.byteOffset]));
|
||
stride_normal = normAccessor.ByteStride(normView) ? (normAccessor.ByteStride(normView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3);
|
||
}
|
||
|
||
// UVs
|
||
if (primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end())
|
||
{
|
||
const tinygltf::Accessor& uvAccessor = model.accessors[primitive.attributes.find("TEXCOORD_0")->second];
|
||
const tinygltf::BufferView& uvView = model.bufferViews[uvAccessor.bufferView];
|
||
buffer_uv0 = reinterpret_cast<const float*>(&(model.buffers[uvView.buffer].data[uvAccessor.byteOffset + uvView.byteOffset]));
|
||
stride_uv0 = uvAccessor.ByteStride(uvView) ? (uvAccessor.ByteStride(uvView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC2);
|
||
}
|
||
if (primitive.attributes.find("TEXCOORD_1") != primitive.attributes.end())
|
||
{
|
||
const tinygltf::Accessor& uvAccessor = model.accessors[primitive.attributes.find("TEXCOORD_1")->second];
|
||
const tinygltf::BufferView& uvView = model.bufferViews[uvAccessor.bufferView];
|
||
buffer_uv1 = reinterpret_cast<const float*>(&(model.buffers[uvView.buffer].data[uvAccessor.byteOffset + uvView.byteOffset]));
|
||
stride_uv1 = uvAccessor.ByteStride(uvView) ? (uvAccessor.ByteStride(uvView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC2);
|
||
}
|
||
|
||
// Vertex colors
|
||
if (primitive.attributes.find("COLOR_0") != primitive.attributes.end())
|
||
{
|
||
const tinygltf::Accessor& accessor = model.accessors[primitive.attributes.find("COLOR_0")->second];
|
||
const tinygltf::BufferView& view = model.bufferViews[accessor.bufferView];
|
||
buffer_color = reinterpret_cast<const float*>(&(model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||
stride_color0 = accessor.ByteStride(view) ? (accessor.ByteStride(view) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3);
|
||
}
|
||
|
||
// Skinning
|
||
// Joints
|
||
if (primitive.attributes.find("JOINTS_0") != primitive.attributes.end())
|
||
{
|
||
const tinygltf::Accessor& jointAccessor = model.accessors[primitive.attributes.find("JOINTS_0")->second];
|
||
const tinygltf::BufferView& jointView = model.bufferViews[jointAccessor.bufferView];
|
||
buffer_joint = &(model.buffers[jointView.buffer].data[jointAccessor.byteOffset + jointView.byteOffset]);
|
||
joint_comp_type = jointAccessor.componentType;
|
||
stride_joint = jointAccessor.ByteStride(jointView) ? (jointAccessor.ByteStride(jointView) / tinygltf::GetComponentSizeInBytes(joint_comp_type)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC4);
|
||
}
|
||
|
||
if (primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end())
|
||
{
|
||
const tinygltf::Accessor& weightAccessor = model.accessors[primitive.attributes.find("WEIGHTS_0")->second];
|
||
const tinygltf::BufferView& weightView = model.bufferViews[weightAccessor.bufferView];
|
||
buffer_weight = reinterpret_cast<const float*>(&(model.buffers[weightView.buffer].data[weightAccessor.byteOffset + weightView.byteOffset]));
|
||
stride_weight = weightAccessor.ByteStride(weightView) ? (weightAccessor.ByteStride(weightView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC4);
|
||
}
|
||
|
||
has_skin = (buffer_joint && buffer_weight);
|
||
|
||
for (size_t i = 0; i < accessor_pos.count; ++i)
|
||
{
|
||
if (has_skin)
|
||
{
|
||
VertexBone& v = data._allVertexSkin[data._vertexPos];
|
||
v._pos = _vec3_cast(&buffer_pos[i * stride_pos]);
|
||
if (!buffer_normal)
|
||
v._normal = {};
|
||
else
|
||
v._normal = _vec3_cast(&buffer_normal[i * stride_normal]);
|
||
if (!buffer_uv0)
|
||
v._uv0 = {};
|
||
else
|
||
v._uv0 = _vec2_cast(&buffer_uv0[i * stride_uv0]);
|
||
if (!buffer_uv1)
|
||
v._uv1 = {};
|
||
else
|
||
v._uv1 = _vec2_cast(&buffer_uv1[i * stride_uv1]);
|
||
if (!buffer_color)
|
||
v._color = { 1, 1, 1, 1 };
|
||
else
|
||
v._color = _vec4_cast(&buffer_color[i * stride_color0]);
|
||
|
||
switch (joint_comp_type)
|
||
{
|
||
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
||
{
|
||
const uint16_t* buf = static_cast<const uint16_t*>(buffer_joint);
|
||
for (size_t j = 0; j < NUM_BONE; ++j)
|
||
v._joint0[j] = buf[i * stride_joint + j];
|
||
break;
|
||
}
|
||
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
||
{
|
||
const uint8_t* buf = static_cast<const uint8_t*>(buffer_joint);
|
||
for (size_t j = 0; j < NUM_BONE; ++j)
|
||
v._joint0[j] = (uint16_t)buf[i * stride_joint + j];
|
||
break;
|
||
}
|
||
default:
|
||
assert(0);
|
||
break;
|
||
}
|
||
v._weight0 = *(Position4*)(&buffer_weight[i * stride_weight]);
|
||
if (Every::Zero(v._weight0))
|
||
{
|
||
v._weight0[0] = 1.0_f;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Vertex& v = data._allVertex[data._vertexPos];
|
||
v._pos = _vec3_cast(&buffer_pos[i * stride_pos]);
|
||
if (!buffer_normal)
|
||
v._normal = {};
|
||
else
|
||
v._normal = _vec3_cast(&buffer_normal[i * stride_normal]);
|
||
if (!buffer_uv0)
|
||
v._uv0 = {};
|
||
else
|
||
v._uv0 = _vec2_cast(&buffer_uv0[i * stride_uv0]);
|
||
if (!buffer_uv1)
|
||
v._uv1 = {};
|
||
else
|
||
v._uv1 = _vec2_cast(&buffer_uv1[i * stride_uv1]);
|
||
if (!buffer_color)
|
||
v._color = { 1, 1, 1, 1 };
|
||
else
|
||
v._color = _vec4_cast(&buffer_color[i * stride_color0]);
|
||
}
|
||
++data._vertexPos;
|
||
}
|
||
}
|
||
// Indices
|
||
if (has_index)
|
||
{
|
||
const tinygltf::Accessor& accessor = model.accessors[primitive.indices > -1 ? primitive.indices : 0];
|
||
const tinygltf::BufferView& bufferView = model.bufferViews[accessor.bufferView];
|
||
const tinygltf::Buffer& buffer = model.buffers[bufferView.buffer];
|
||
|
||
index_count = static_cast<uint32_t>(accessor.count);
|
||
const void* data_ptr = &(buffer.data[accessor.byteOffset + bufferView.byteOffset]);
|
||
|
||
switch (accessor.componentType)
|
||
{
|
||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT:
|
||
{
|
||
const uint32_t* buf = static_cast<const uint32_t*>(data_ptr);
|
||
for (size_t index = 0; index < accessor.count; index++)
|
||
{
|
||
data._allIndex[data._indexPos] = buf[index] + vertex_start;
|
||
++data._indexPos;
|
||
}
|
||
break;
|
||
}
|
||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT:
|
||
{
|
||
const uint16_t* buf = static_cast<const uint16_t*>(data_ptr);
|
||
for (size_t index = 0; index < accessor.count; index++)
|
||
{
|
||
data._allIndex[data._indexPos] = buf[index] + vertex_start;
|
||
++data._indexPos;
|
||
}
|
||
break;
|
||
}
|
||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE:
|
||
{
|
||
const uint8_t* buf = static_cast<const uint8_t*>(data_ptr);
|
||
for (size_t index = 0; index < accessor.count; index++)
|
||
{
|
||
data._allIndex[data._indexPos] = buf[index] + vertex_start;
|
||
++data._indexPos;
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
assert(0);
|
||
return;
|
||
}
|
||
}
|
||
ModelPrimitive model_primitive;
|
||
model_primitive._indexBeg = index_start;
|
||
model_primitive._indexCount = index_count;
|
||
model_primitive._vertexIndex = vertex_start;
|
||
model_primitive._vertexCount = vertex_count;
|
||
model_primitive._indexMaterial = primitive.material;
|
||
model_primitive._bHasIndices = index_count > 0;
|
||
model_primitive._aabb = BoundingBox(pos_min, pos_max);
|
||
model_mesh._primitives.push_back(model_primitive);
|
||
}
|
||
// Mesh BB from BBs of primitives
|
||
for (ModelPrimitive& p : model_mesh._primitives)
|
||
{
|
||
model_mesh._aabb.Add(p._aabb);
|
||
}
|
||
model_node->_mesh = model_mesh;
|
||
}
|
||
}
|
||
|
||
void ModelLoader::_gltf_load_skin(const tinygltf::Model& gltfModel, ModelData& data)
|
||
{
|
||
for (const tinygltf::Skin& source : gltfModel.skins)
|
||
{
|
||
ModelSkin skin;
|
||
skin.name = source.name;
|
||
|
||
// Find skeleton root node
|
||
if (source.skeleton > -1)
|
||
{
|
||
skin.skeletonRoot = source.skeleton + 1;
|
||
}
|
||
|
||
// Find joint nodes
|
||
for (int jointIndex : source.joints)
|
||
{
|
||
ModelNode* node = _find_node(data, jointIndex + 1);
|
||
if (node)
|
||
{
|
||
skin._allJoint.push_back(jointIndex + 1);
|
||
}
|
||
}
|
||
|
||
// Get inverse bind matrices from buffer
|
||
if (source.inverseBindMatrices > -1)
|
||
{
|
||
const tinygltf::Accessor& accessor = gltfModel.accessors[source.inverseBindMatrices];
|
||
const tinygltf::BufferView& bufferView = gltfModel.bufferViews[accessor.bufferView];
|
||
const tinygltf::Buffer& buffer = gltfModel.buffers[bufferView.buffer];
|
||
skin._allOffsetMatrix.resize(accessor.count);
|
||
memcpy(skin._allOffsetMatrix.data(), &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(Matrix4));
|
||
}
|
||
data._allSkin.push_back(skin);
|
||
}
|
||
}
|
||
|
||
void ModelLoader::_gltf_load_animation(const tinygltf::Model& model, ModelData& data)
|
||
{
|
||
for (const tinygltf::Animation& anim : model.animations)
|
||
{
|
||
ModelAnimation animation{};
|
||
animation._name = anim.name;
|
||
if (anim.name.empty())
|
||
{
|
||
animation._name = std::to_string(data._allAnimation.size());
|
||
}
|
||
|
||
// Samplers
|
||
for (auto& samp : anim.samplers) {
|
||
ModelAnimationSampler sampler{};
|
||
|
||
if (samp.interpolation == "LINEAR") {
|
||
sampler.interpolation = ModelAnimationSampler::InterpolationType::LINEAR;
|
||
}
|
||
if (samp.interpolation == "STEP") {
|
||
sampler.interpolation = ModelAnimationSampler::InterpolationType::STEP;
|
||
}
|
||
if (samp.interpolation == "CUBICSPLINE") {
|
||
sampler.interpolation = ModelAnimationSampler::InterpolationType::CUBICSPLINE;
|
||
}
|
||
|
||
// Read sampler input time values
|
||
{
|
||
const tinygltf::Accessor& accessor = model.accessors[samp.input];
|
||
const tinygltf::BufferView& bufferView = model.bufferViews[accessor.bufferView];
|
||
const tinygltf::Buffer& buffer = model.buffers[bufferView.buffer];
|
||
|
||
assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
|
||
|
||
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
||
const float* buf = static_cast<const float*>(dataPtr);
|
||
for (size_t index = 0; index < accessor.count; index++) {
|
||
sampler.inputs.push_back(buf[index]);
|
||
}
|
||
|
||
for (auto input : sampler.inputs) {
|
||
if (input < animation.start) {
|
||
animation.start = input;
|
||
};
|
||
if (input > animation.end) {
|
||
animation.end = input;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Read sampler output T/R/S values
|
||
{
|
||
const tinygltf::Accessor& accessor = model.accessors[samp.output];
|
||
const tinygltf::BufferView& bufferView = model.bufferViews[accessor.bufferView];
|
||
const tinygltf::Buffer& buffer = model.buffers[bufferView.buffer];
|
||
|
||
assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
|
||
|
||
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
||
|
||
switch (accessor.type) {
|
||
case TINYGLTF_TYPE_VEC3: {
|
||
const Position3* buf = (Position3*)dataPtr;
|
||
for (size_t index = 0; index < accessor.count; index++) {
|
||
sampler.outputsVec4.push_back(toLonger(buf[index], 0.0f));
|
||
sampler.outputs.push_back(buf[index][0]);
|
||
sampler.outputs.push_back(buf[index][1]);
|
||
sampler.outputs.push_back(buf[index][2]);
|
||
}
|
||
break;
|
||
}
|
||
case TINYGLTF_TYPE_VEC4: {
|
||
const Position4* buf = (Position4*)dataPtr;
|
||
for (size_t index = 0; index < accessor.count; index++) {
|
||
sampler.outputsVec4.push_back(buf[index]);
|
||
sampler.outputs.push_back(buf[index][0]);
|
||
sampler.outputs.push_back(buf[index][1]);
|
||
sampler.outputs.push_back(buf[index][2]);
|
||
sampler.outputs.push_back(buf[index][3]);
|
||
}
|
||
break;
|
||
}
|
||
default: {
|
||
std::cout << "unknown type" << std::endl;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
animation._allSampler.push_back(sampler);
|
||
}
|
||
|
||
// Channels
|
||
for (auto& source : anim.channels)
|
||
{
|
||
ModelAnimationChannel channel{};
|
||
|
||
if (source.target_path == "rotation")
|
||
{
|
||
channel.path = ModelAnimationChannel::PathType::ROTATION;
|
||
}
|
||
if (source.target_path == "translation")
|
||
{
|
||
channel.path = ModelAnimationChannel::PathType::TRANSLATION;
|
||
}
|
||
if (source.target_path == "scale")
|
||
{
|
||
channel.path = ModelAnimationChannel::PathType::SCALE;
|
||
}
|
||
if (source.target_path == "weights")
|
||
{
|
||
std::cout << "weights not yet supported, skipping channel" << std::endl;
|
||
continue;
|
||
}
|
||
channel.samplerIndex = source.sampler;
|
||
channel.indexNode = source.target_node + 1;
|
||
auto node = _find_node(data, source.target_node + 1);
|
||
if (!node)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
animation._allChannel.push_back(channel);
|
||
}
|
||
|
||
// 四元数顺序修正(gltf 为 xyzw)
|
||
for (const ModelAnimationChannel& channel : animation._allChannel)
|
||
{
|
||
ModelAnimationSampler& sampler = animation._allSampler[channel.samplerIndex];
|
||
if (channel.path == ModelAnimationChannel::ROTATION)
|
||
{
|
||
for (auto& iter : sampler.outputsVec4)
|
||
{
|
||
std::swap(iter.x, iter.w);
|
||
std::swap(iter.y, iter.w);
|
||
std::swap(iter.z, iter.w);
|
||
}
|
||
}
|
||
}
|
||
|
||
data._allAnimation.push_back(animation);
|
||
}
|
||
}
|
||
|
||
dl::ModelNode* ModelLoader::_find_node(ModelData& data, uint32_t index)
|
||
{
|
||
for (auto& iter : data._allNode)
|
||
{
|
||
if (iter->GetValue()->_index == index)
|
||
return iter->GetValue();
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
dl::Matrix4 ModelLoader::_get_node_mat_world(const ModelNode* node)
|
||
{
|
||
Matrix4 mat = node->_trans2Parent.GetMatrix();
|
||
ModelNode* parent = node->_parent;
|
||
while (parent)
|
||
{
|
||
mat = parent->_trans2Parent.GetMatrix() * mat;
|
||
parent = parent->_parent;
|
||
}
|
||
return mat;
|
||
}
|
||
|
||
dl::Position3 ModelLoader::_vec3_cast(const std::vector<double>& vec3)
|
||
{
|
||
if (vec3.size() != 3)
|
||
{
|
||
assert(0);
|
||
return {};
|
||
}
|
||
Position3 ret;
|
||
for (size_t i = 0; i < 3; ++i)
|
||
{
|
||
ret[i] = (Float)vec3[i];
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
|
||
dl::Position4 ModelLoader::_vec4_cast(const std::vector<double>& vec4)
|
||
{
|
||
if (vec4.size() != 4)
|
||
{
|
||
assert(0);
|
||
return {};
|
||
}
|
||
Position4 ret;
|
||
for (size_t i = 0; i < 4; ++i)
|
||
{
|
||
ret[i] = (Float)vec4[i];
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
dl::Position4 ModelLoader::_quat_cast(const std::vector<double>& vec4)
|
||
{
|
||
if (vec4.size() != 4)
|
||
{
|
||
assert(0);
|
||
return {};
|
||
}
|
||
Position4 ret;
|
||
ret[0] = (float)vec4[3];
|
||
ret[1] = (float)vec4[0];
|
||
ret[2] = (float)vec4[1];
|
||
ret[3] = (float)vec4[2];
|
||
return ret;
|
||
}
|
||
|
||
dl::Position2 ModelLoader::_vec2_cast(const float* p)
|
||
{
|
||
return { p[0], p[1] };
|
||
}
|
||
dl::Position3 ModelLoader::_vec3_cast(const float* p)
|
||
{
|
||
return { p[0], p[1], p[2] };
|
||
}
|
||
|
||
dl::Position4 ModelLoader::_vec4_cast(const float* p)
|
||
{
|
||
return { p[0], p[1], p[2], p[3] };
|
||
}
|
||
|
||
dl::Matrix4 ModelLoader::_mat4_cast(const std::vector<double>& matrix)
|
||
{
|
||
if (matrix.size() != 16)
|
||
{
|
||
assert(0);
|
||
return Mat::CreateIdentity();
|
||
}
|
||
Matrix4 ret;
|
||
for (size_t i = 0; i < 16; ++i)
|
||
{
|
||
ret.At(i) = (Float)matrix[i];
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
dl::Matrix4 ModelLoader::_mat4_cast(float* p)
|
||
{
|
||
Matrix4 ret;
|
||
for (size_t i = 0; i < 16; ++i)
|
||
{
|
||
ret.At(i) = (Float)p[i];
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
}
|
||
#endif |