990 lines
33 KiB
C++
990 lines
33 KiB
C++
#include "dl_model_loader.h"
|
||
|
||
#ifndef DL_DISABLE_RENDER
|
||
#define TINYOBJLOADER_IMPLEMENTATION
|
||
#include <tiny_obj_loader.h>
|
||
#define TINYGLTF_IMPLEMENTATION
|
||
#include <tiny_gltf.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::Parse(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<ModelObjectMaterial>& 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);
|
||
|
||
ModelObjectMaterial& material = mo_material[i];
|
||
material._materialName = m.name;
|
||
|
||
material._material._diffuseAlbedo = { m.diffuse[0], m.diffuse[1], m.diffuse[2], 1.0f };
|
||
material._material._fresnelR0 = { m.specular[0], m.specular[1], m.specular[2] };
|
||
material._material._roughness = ShininessToRoughness(m.shininess);
|
||
|
||
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;
|
||
for (std::vector<tinyobj::index_t>& sub_idx : all_sub)
|
||
{
|
||
ModelObjectSub sub;
|
||
sub._range._offsetIndex = i;
|
||
sub._range._countTriangle = sub_idx.size() / 3;
|
||
sub._indexMaterial = j - 1;
|
||
++j;
|
||
model_object->_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._uv[0] = attrib.texcoords[idx.texcoord_index * 2 + 0];
|
||
v._uv[1] = attrib.texcoords[idx.texcoord_index * 2 + 1];
|
||
v._uv[1] = 1.0f - v._uv[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;
|
||
}
|
||
}
|
||
log_msg("解析obj模型完成:v_{},i_{}", mo_vertices.size(), mo_indices.size());
|
||
return ret;
|
||
}
|
||
|
||
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());
|
||
|
||
// 只生成一个ModelObject(animation可能是节点动画,暂不支持)
|
||
bool has_bone = !model.skins.empty();
|
||
|
||
std::vector<ModelObject*> ret;
|
||
ModelObject* model_object = new ModelObject;
|
||
std::vector<Vertex>& mo_vertices = model_object->_allVertex;
|
||
std::vector<VertexBone>& mo_vertices_bone = model_object->_allVertexBone;
|
||
std::vector<uint32_t>& mo_indices = model_object->_allIndex;
|
||
std::vector<ModelObjectMaterial>& mo_material = model_object->_allMaterial;
|
||
ret.push_back(model_object);
|
||
|
||
_gltf_load_material(model, res_name, mo_material);
|
||
_gltf_load_scene(model, model_object->_modelData);
|
||
|
||
//子网格合并
|
||
if (false)
|
||
{//合并网格连续,且材质相同的项
|
||
std::vector<ModelObjectSub> vec_sub;
|
||
ModelObjectSub* pre = nullptr;
|
||
//
|
||
for (auto iter = model_object->_allSub.begin();
|
||
iter != model_object->_allSub.end(); ++iter)
|
||
{
|
||
ModelObjectSub& sub = *iter;
|
||
|
||
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("子网格合并:{}->{}", model_object->_allSub.size(), vec_sub.size());
|
||
for (ModelObjectSub& sub : vec_sub)
|
||
{
|
||
log_info("合并后网格,起始:{},三角数:{},材质:{}",
|
||
sub._range._offsetIndex, sub._range._countTriangle, sub._indexMaterial);
|
||
}
|
||
|
||
std::swap(model_object->_allSub, vec_sub);
|
||
int pause = 3;
|
||
}
|
||
|
||
if (!has_bone)
|
||
{
|
||
assert(mo_vertices_bone.empty());
|
||
}
|
||
else
|
||
{
|
||
assert(mo_vertices.empty());
|
||
}
|
||
|
||
|
||
log_msg("解析gltf模型完成:v_{},i_{}",
|
||
!has_bone ? mo_vertices.size() : mo_vertices_bone.size(),
|
||
mo_indices.size());
|
||
return ret;
|
||
}
|
||
|
||
void ModelLoader::_gltf_load_material(const tinygltf::Model& model,
|
||
std::string_view res_name,
|
||
std::vector<ModelObjectMaterial>& mo_material)
|
||
{
|
||
mo_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;
|
||
|
||
ModelObjectMaterial& material = mo_material[i];
|
||
material._materialName = m.name;
|
||
|
||
material._material._diffuseAlbedo = {
|
||
(float)pbr.baseColorFactor[0],
|
||
(float)pbr.baseColorFactor[1],
|
||
(float)pbr.baseColorFactor[2], 1.0f };
|
||
material._material._fresnelR0 = {
|
||
(float)pbr.metallicFactor, (float)pbr.metallicFactor, (float)pbr.metallicFactor };
|
||
material._material._roughness = (float)pbr.roughnessFactor;
|
||
|
||
// 纹理导出
|
||
auto fn_texture = [&](int index, std::string_view tag) {
|
||
if (index == -1)
|
||
return std::string{};
|
||
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);
|
||
stbi_write_png(CodeCvt::Utf8ToMultiByte(path_name).c_str(),
|
||
img.width, img.height,
|
||
4, img.image.data(), 0);
|
||
|
||
return path_name;
|
||
};
|
||
material._pathTexDiffuse = fn_texture(pbr.baseColorTexture.index, "");
|
||
material._pathTexNormal = fn_texture(m.normalTexture.index, "_normal");
|
||
}
|
||
//for (tinygltf::Material& mat : gltfModel.materials) {
|
||
// vkglTF::Material material{};
|
||
// material.doubleSided = mat.doubleSided;
|
||
// if (mat.values.find("baseColorTexture") != mat.values.end()) {
|
||
// material.baseColorTexture = &textures[mat.values["baseColorTexture"].TextureIndex()];
|
||
// material.texCoordSets.baseColor = mat.values["baseColorTexture"].TextureTexCoord();
|
||
// }
|
||
// if (mat.values.find("metallicRoughnessTexture") != mat.values.end()) {
|
||
// material.metallicRoughnessTexture = &textures[mat.values["metallicRoughnessTexture"].TextureIndex()];
|
||
// material.texCoordSets.metallicRoughness = mat.values["metallicRoughnessTexture"].TextureTexCoord();
|
||
// }
|
||
// if (mat.values.find("roughnessFactor") != mat.values.end()) {
|
||
// material.roughnessFactor = static_cast<float>(mat.values["roughnessFactor"].Factor());
|
||
// }
|
||
// if (mat.values.find("metallicFactor") != mat.values.end()) {
|
||
// material.metallicFactor = static_cast<float>(mat.values["metallicFactor"].Factor());
|
||
// }
|
||
// if (mat.values.find("baseColorFactor") != mat.values.end()) {
|
||
// material.baseColorFactor = glm::make_vec4(mat.values["baseColorFactor"].ColorFactor().data());
|
||
// }
|
||
// if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) {
|
||
// material.normalTexture = &textures[mat.additionalValues["normalTexture"].TextureIndex()];
|
||
// material.texCoordSets.normal = mat.additionalValues["normalTexture"].TextureTexCoord();
|
||
// }
|
||
// if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) {
|
||
// material.emissiveTexture = &textures[mat.additionalValues["emissiveTexture"].TextureIndex()];
|
||
// material.texCoordSets.emissive = mat.additionalValues["emissiveTexture"].TextureTexCoord();
|
||
// }
|
||
// if (mat.additionalValues.find("occlusionTexture") != mat.additionalValues.end()) {
|
||
// material.occlusionTexture = &textures[mat.additionalValues["occlusionTexture"].TextureIndex()];
|
||
// material.texCoordSets.occlusion = mat.additionalValues["occlusionTexture"].TextureTexCoord();
|
||
// }
|
||
// if (mat.additionalValues.find("alphaMode") != mat.additionalValues.end()) {
|
||
// tinygltf::Parameter param = mat.additionalValues["alphaMode"];
|
||
// if (param.string_value == "BLEND") {
|
||
// material.alphaMode = Material::ALPHAMODE_BLEND;
|
||
// }
|
||
// if (param.string_value == "MASK") {
|
||
// material.alphaCutoff = 0.5f;
|
||
// material.alphaMode = Material::ALPHAMODE_MASK;
|
||
// }
|
||
// }
|
||
// if (mat.additionalValues.find("alphaCutoff") != mat.additionalValues.end()) {
|
||
// material.alphaCutoff = static_cast<float>(mat.additionalValues["alphaCutoff"].Factor());
|
||
// }
|
||
// if (mat.additionalValues.find("emissiveFactor") != mat.additionalValues.end()) {
|
||
// material.emissiveFactor = glm::vec4(glm::make_vec3(mat.additionalValues["emissiveFactor"].ColorFactor().data()), 1.0);
|
||
// }
|
||
|
||
// // Extensions
|
||
// if (mat.extensions.find("KHR_materials_pbrSpecularGlossiness") != mat.extensions.end()) {
|
||
// auto ext = mat.extensions.find("KHR_materials_pbrSpecularGlossiness");
|
||
// if (ext->second.Has("specularGlossinessTexture")) {
|
||
// auto index = ext->second.Get("specularGlossinessTexture").Get("index");
|
||
// material.extension.specularGlossinessTexture = &textures[index.Get<int>()];
|
||
// auto texCoordSet = ext->second.Get("specularGlossinessTexture").Get("texCoord");
|
||
// material.texCoordSets.specularGlossiness = texCoordSet.Get<int>();
|
||
// material.pbrWorkflows.specularGlossiness = true;
|
||
// material.pbrWorkflows.metallicRoughness = false;
|
||
// }
|
||
// if (ext->second.Has("diffuseTexture")) {
|
||
// auto index = ext->second.Get("diffuseTexture").Get("index");
|
||
// material.extension.diffuseTexture = &textures[index.Get<int>()];
|
||
// }
|
||
// if (ext->second.Has("diffuseFactor")) {
|
||
// auto factor = ext->second.Get("diffuseFactor");
|
||
// for (uint32_t i = 0; i < factor.ArrayLen(); i++) {
|
||
// auto val = factor.Get(i);
|
||
// material.extension.diffuseFactor[i] = val.IsNumber() ? (float)val.Get<double>() : (float)val.Get<int>();
|
||
// }
|
||
// }
|
||
// if (ext->second.Has("specularFactor")) {
|
||
// auto factor = ext->second.Get("specularFactor");
|
||
// for (uint32_t i = 0; i < factor.ArrayLen(); i++) {
|
||
// auto val = factor.Get(i);
|
||
// material.extension.specularFactor[i] = val.IsNumber() ? (float)val.Get<double>() : (float)val.Get<int>();
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// if (mat.extensions.find("KHR_materials_unlit") != mat.extensions.end()) {
|
||
// material.unlit = true;
|
||
// }
|
||
|
||
// if (mat.extensions.find("KHR_materials_emissive_strength") != mat.extensions.end()) {
|
||
// auto ext = mat.extensions.find("KHR_materials_emissive_strength");
|
||
// if (ext->second.Has("emissiveStrength")) {
|
||
// auto value = ext->second.Get("emissiveStrength");
|
||
// material.emissiveStrength = (float)value.Get<double>();
|
||
// }
|
||
// }
|
||
|
||
// material.index = static_cast<uint32_t>(materials.size());
|
||
// materials.push_back(material);
|
||
//}
|
||
//// Push a default material at the end of the list for meshes with no material assigned
|
||
//materials.push_back(Material());
|
||
}
|
||
|
||
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)
|
||
{
|
||
// 只读一个场景
|
||
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);
|
||
}
|
||
loader_info._vertexBuffer = new VertexBone[vertex_count];
|
||
loader_info._indexBuffer = new uint32_t[index_count];
|
||
|
||
// 读取节点
|
||
for (size_t i = 0; i < scene.nodes.size(); i++) {
|
||
_gltf_load_node(model, scene.nodes[i], nullptr, loader_info, data);
|
||
}
|
||
// 读取动画
|
||
if (model.animations.size() > 0) {
|
||
_gltf_load_animation(model, data);
|
||
}
|
||
// 读取蒙皮
|
||
_gltf_load_skin(model, data);
|
||
|
||
for (ModelNode* model_node : data->_allNode)
|
||
{
|
||
// Assign skins
|
||
if (model_node->skinIndex > -1)
|
||
{
|
||
model_node->_skin = data->_allSkin[model_node->skinIndex];
|
||
}
|
||
// Initial pose
|
||
if (model_node->_mesh)
|
||
{
|
||
model_node->Update();
|
||
}
|
||
}
|
||
}
|
||
|
||
void ModelLoader::_gltf_load_node(const tinygltf::Model& model, int node,
|
||
ModelNode* model_node_parent, LoaderInfo& loader_info, ModelData* data)
|
||
{
|
||
const tinygltf::Node& nn = model.nodes[node];
|
||
|
||
ModelNode* model_node = new ModelNode;
|
||
model_node->_parent = model_node_parent;
|
||
|
||
for (size_t i = 0; i < nn.children.size(); ++i)
|
||
{
|
||
_gltf_load_node(model, nn.children[i], model_node_parent, loader_info, data);
|
||
}
|
||
|
||
// 读取mesh
|
||
if (nn.mesh > -1)
|
||
{
|
||
const tinygltf::Mesh& mesh = model.meshes[nn.mesh];
|
||
ModelMesh* model_mesh = new ModelMesh;
|
||
|
||
for (size_t j = 0; j < mesh.primitives.size(); j++)
|
||
{
|
||
const tinygltf::Primitive& primitive = mesh.primitives[j];
|
||
uint32_t vertex_start = (uint32_t)loader_info._vertexPos;
|
||
uint32_t index_start = (uint32_t)loader_info._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)
|
||
{
|
||
VertexBone& v = loader_info._vertexBuffer[loader_info._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]);
|
||
|
||
if (!has_skin)
|
||
{
|
||
v._joint0 = {};
|
||
}
|
||
else
|
||
{
|
||
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;
|
||
}
|
||
}
|
||
|
||
if (!has_skin)
|
||
v._weight0 = {};
|
||
else
|
||
v._weight0 = *(Position4*)(&buffer_weight[i * stride_weight]);
|
||
|
||
if (Every::Zero(v._weight0))
|
||
{
|
||
v._weight0[0] = 1.0_f;
|
||
}
|
||
++loader_info._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++)
|
||
{
|
||
loader_info._indexBuffer[loader_info._indexPos] = buf[index] + vertex_start;
|
||
++loader_info._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++)
|
||
{
|
||
loader_info._indexBuffer[loader_info._indexPos] = buf[index] + vertex_start;
|
||
++loader_info._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++)
|
||
{
|
||
loader_info._indexBuffer[loader_info._indexPos] = buf[index] + vertex_start;
|
||
++loader_info._indexPos;
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
assert(0);
|
||
return;
|
||
}
|
||
}
|
||
ModelPrimitive* model_primitive = new ModelPrimitive;
|
||
model_primitive->firstIndex = index_start;
|
||
model_primitive->indexCount = index_count;
|
||
model_primitive->vertexCount = vertex_count;
|
||
model_primitive->material = primitive.material;// > -1 ?
|
||
//materials[primitive.material] : materials.back();
|
||
model_primitive->hasIndices = index_count > 0;
|
||
model_primitive->bb.min = pos_min;
|
||
model_primitive->bb.max = pos_max;
|
||
model_primitive->bb.valid = true;
|
||
model_mesh->primitives.push_back(model_primitive);
|
||
}
|
||
// Mesh BB from BBs of primitives
|
||
for (ModelPrimitive* p : model_mesh->primitives)
|
||
{
|
||
if (p->bb.valid && !model_mesh->bb.valid)
|
||
{
|
||
model_mesh->bb = p->bb;
|
||
model_mesh->bb.valid = true;
|
||
}
|
||
model_mesh->bb.min = Each::Min(model_mesh->bb.min, p->bb.min);
|
||
model_mesh->bb.max = Each::Max(model_mesh->bb.max, p->bb.max);
|
||
}
|
||
model_node->_mesh = model_mesh;
|
||
}
|
||
if (model_node_parent) {
|
||
model_node_parent->_allChild.push_back(model_node);
|
||
}
|
||
else {
|
||
data->_allNodeChild.push_back(model_node);
|
||
}
|
||
data->_allNode.push_back(model_node);
|
||
}
|
||
|
||
void ModelLoader::_gltf_load_skin(const tinygltf::Model& gltfModel, ModelData* data)
|
||
{
|
||
for (const tinygltf::Skin& source : gltfModel.skins) {
|
||
ModelSkin* newSkin = new ModelSkin;
|
||
newSkin->name = source.name;
|
||
|
||
// Find skeleton root node
|
||
if (source.skeleton > -1) {
|
||
newSkin->skeletonRoot = nodeFromIndex(data, source.skeleton);
|
||
}
|
||
|
||
// Find joint nodes
|
||
for (int jointIndex : source.joints) {
|
||
ModelNode* node = nodeFromIndex(data, jointIndex);
|
||
if (node) {
|
||
newSkin->_allJoint.push_back(nodeFromIndex(data, jointIndex));
|
||
}
|
||
}
|
||
|
||
// 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];
|
||
newSkin->_allOffsetMatrix.resize(accessor.count);
|
||
memcpy(newSkin->_allOffsetMatrix.data(), &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(Matrix4));
|
||
}
|
||
data->_allSkin.push_back(newSkin);
|
||
}
|
||
}
|
||
|
||
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.node = nodeFromIndex(data, source.target_node);
|
||
if (!channel.node) {
|
||
continue;
|
||
}
|
||
|
||
animation._allChannel.push_back(channel);
|
||
}
|
||
|
||
data->_allAnimation.push_back(animation);
|
||
}
|
||
}
|
||
|
||
dl::ModelNode* ModelLoader::findNode(ModelData* data, ModelNode* parent, uint32_t index)
|
||
{
|
||
ModelNode* nodeFound = nullptr;
|
||
if (parent->_index == index) {
|
||
return parent;
|
||
}
|
||
for (auto& child : parent->_allChild) {
|
||
nodeFound = findNode(data, child, index);
|
||
if (nodeFound) {
|
||
break;
|
||
}
|
||
}
|
||
return nodeFound;
|
||
}
|
||
|
||
dl::ModelNode* ModelLoader::nodeFromIndex(ModelData* data, uint32_t index)
|
||
{
|
||
ModelNode* nodeFound = nullptr;
|
||
for (auto& node : data->_allNode) {
|
||
nodeFound = findNode(data, node, index);
|
||
if (nodeFound) {
|
||
break;
|
||
}
|
||
}
|
||
return nodeFound;
|
||
}
|
||
|
||
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::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 |