#include "stdafx.h" #include "wiScene.h" #include "ModelImporter.h" #include "wiRandom.h" #include "Utility/stb_image.h" #include #include #include #define TINYGLTF_IMPLEMENTATION #define TINYGLTF_NO_FS #define TINYGLTF_NO_STB_IMAGE #define TINYGLTF_NO_STB_IMAGE_WRITE #include "tiny_gltf.h" using namespace wi::graphics; using namespace wi::scene; using namespace wi::ecs; // Transform the data from glTF space to engine-space: static const bool transform_to_LH = true; namespace tinygltf { bool FileExists(const std::string& abs_filename, void*) { return wi::helper::FileExists(abs_filename); } std::string ExpandFilePath(const std::string& filepath, void*) { #ifdef _WIN32 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0); char* str = new char[len]; ExpandEnvironmentStringsA(filepath.c_str(), str, len); std::string s(str); delete[] str; return s; #else #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \ defined(__ANDROID__) || defined(__EMSCRIPTEN__) // no expansion std::string s = filepath; #else std::string s; wordexp_t p; if (filepath.empty()) { return ""; } // char** w; int ret = wordexp(filepath.c_str(), &p, 0); if (ret) { // err s = filepath; return s; } // Use first element only. if (p.we_wordv) { s = std::string(p.we_wordv[0]); wordfree(&p); } else { s = filepath; } #endif return s; #endif } bool ReadWholeFile(std::vector* out, std::string* err, const std::string& filepath, void*) { return wi::helper::FileRead(filepath, *out); } bool WriteWholeFile(std::string* err, const std::string& filepath, const std::vector& contents, void*) { return wi::helper::FileWrite(filepath, contents.data(), contents.size()); } bool LoadImageData(Image *image, const int image_idx, std::string *err, std::string *warn, int req_width, int req_height, const unsigned char *bytes, int size, void *userdata) { (void)warn; if (image->uri.empty()) { // Force some image resource name: std::string ss; do { ss.clear(); ss += "gltfimport_" + std::to_string(wi::random::GetRandom(std::numeric_limits::max())) + ".png"; } while (wi::resourcemanager::Contains(ss)); // this is to avoid overwriting an existing imported image image->uri = ss; } auto resource = wi::resourcemanager::Load( image->uri, wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA, (const uint8_t*)bytes, (size_t)size ); if (!resource.IsValid()) { return false; } image->width = resource.GetTexture().desc.width; image->height = resource.GetTexture().desc.height; image->component = 4; wi::resourcemanager::ResourceSerializer* seri = (wi::resourcemanager::ResourceSerializer*)userdata; seri->resources.push_back(resource); return true; } bool WriteImageData(const std::string *basepath, const std::string *filename, Image *image, bool embedImages, void *) { assert(0); // TODO return false; } } struct LoaderState { std::string name; tinygltf::Model gltfModel; Scene* scene; wi::unordered_map entityMap; // node -> entity Entity rootEntity = INVALID_ENTITY; }; void Import_Extension_VRM(LoaderState& state); void Import_Extension_VRMC(LoaderState& state); void Import_Mixamo_Bone(LoaderState& state, Entity armatureEntity, Entity boneEntity, const tinygltf::Node& node); // Recursively loads nodes and resolves hierarchy: void LoadNode(int nodeIndex, Entity parent, LoaderState& state) { if (nodeIndex < 0) { return; } auto& node = state.gltfModel.nodes[nodeIndex]; Scene& scene = *state.scene; Entity entity = INVALID_ENTITY; if(node.mesh >= 0) { assert(node.mesh < (int)scene.meshes.GetCount()); if (node.skin >= 0) { // This node is an armature: MeshComponent& mesh = scene.meshes[node.mesh]; assert(!mesh.vertex_boneindices.empty()); entity = scene.armatures.GetEntity(node.skin); mesh.armatureID = entity; // The object component will use an identity transform but will be parented to the armature: Entity objectEntity = scene.Entity_CreateObject(node.name); ObjectComponent& object = *scene.objects.GetComponent(objectEntity); object.meshID = scene.meshes.GetEntity(node.mesh); scene.Component_Attach(objectEntity, entity, true); } else { // This node is a mesh instance: entity = scene.Entity_CreateObject(node.name); ObjectComponent& object = *scene.objects.GetComponent(entity); object.meshID = scene.meshes.GetEntity(node.mesh); } } else if (node.camera >= 0) { if (node.name.empty()) { static int camID = 0; node.name = "cam" + std::to_string(camID++); } entity = scene.Entity_CreateCamera(node.name, 16, 9); } auto ext_lights_punctual = node.extensions.find("KHR_lights_punctual"); if (ext_lights_punctual != node.extensions.end()) { // https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_lights_punctual entity = scene.Entity_CreateLight(""); // light component will be filled later } if (entity == INVALID_ENTITY) { entity = CreateEntity(); scene.transforms.Create(entity); scene.names.Create(entity) = node.name; } state.entityMap[nodeIndex] = entity; TransformComponent& transform = *scene.transforms.GetComponent(entity); if (!node.scale.empty()) { transform.scale_local = XMFLOAT3((float)node.scale[0], (float)node.scale[1], (float)node.scale[2]); } if (!node.rotation.empty()) { transform.rotation_local = XMFLOAT4((float)node.rotation[0], (float)node.rotation[1], (float)node.rotation[2], (float)node.rotation[3]); } if (!node.translation.empty()) { transform.translation_local = XMFLOAT3((float)node.translation[0], (float)node.translation[1], (float)node.translation[2]); } if (!node.matrix.empty()) { transform.world._11 = (float)node.matrix[0]; transform.world._12 = (float)node.matrix[1]; transform.world._13 = (float)node.matrix[2]; transform.world._14 = (float)node.matrix[3]; transform.world._21 = (float)node.matrix[4]; transform.world._22 = (float)node.matrix[5]; transform.world._23 = (float)node.matrix[6]; transform.world._24 = (float)node.matrix[7]; transform.world._31 = (float)node.matrix[8]; transform.world._32 = (float)node.matrix[9]; transform.world._33 = (float)node.matrix[10]; transform.world._34 = (float)node.matrix[11]; transform.world._41 = (float)node.matrix[12]; transform.world._42 = (float)node.matrix[13]; transform.world._43 = (float)node.matrix[14]; transform.world._44 = (float)node.matrix[15]; transform.ApplyTransform(); // this creates S, R, T vectors from world matrix } transform.UpdateTransform(); if (parent != INVALID_ENTITY) { scene.Component_Attach(entity, parent, true); } if (!node.children.empty()) { for (int child : node.children) { LoadNode(child, entity, state); } } } void ImportModel_GLTF(const std::string& fileName, Scene& scene) { std::string directory = wi::helper::GetDirectoryFromPath(fileName); std::string name = wi::helper::GetFileNameFromPath(fileName); std::string extension = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(name)); tinygltf::TinyGLTF loader; std::string err; std::string warn; tinygltf::FsCallbacks callbacks; callbacks.ReadWholeFile = tinygltf::ReadWholeFile; callbacks.WriteWholeFile = tinygltf::WriteWholeFile; callbacks.FileExists = tinygltf::FileExists; callbacks.ExpandFilePath = tinygltf::ExpandFilePath; loader.SetFsCallbacks(callbacks); wi::resourcemanager::ResourceSerializer seri; // keep this alive to not delete loaded images while importing gltf loader.SetImageLoader(tinygltf::LoadImageData, &seri); loader.SetImageWriter(tinygltf::WriteImageData, nullptr); LoaderState state; state.scene = &scene; wi::vector filedata; bool ret = wi::helper::FileRead(fileName, filedata); if (ret) { std::string basedir = tinygltf::GetBaseDir(fileName); if (!extension.compare("GLTF")) { ret = loader.LoadASCIIFromString(&state.gltfModel, &err, &warn, reinterpret_cast(&filedata.at(0)), static_cast(filedata.size()), basedir); } else { ret = loader.LoadBinaryFromMemory(&state.gltfModel, &err, &warn, filedata.data(), static_cast(filedata.size()), basedir); } } else { err = "Failed to read file: " + fileName; } if (!ret) { wi::helper::messageBox(err, "GLTF error!"); } state.rootEntity = CreateEntity(); scene.transforms.Create(state.rootEntity); scene.names.Create(state.rootEntity) = name; state.name = name; // Create materials: for (auto& x : state.gltfModel.materials) { Entity materialEntity = scene.Entity_CreateMaterial(x.name); scene.Component_Attach(materialEntity, state.rootEntity); MaterialComponent& material = *scene.materials.GetComponent(materialEntity); material.baseColor = XMFLOAT4(1, 1, 1, 1); material.roughness = 1.0f; material.metalness = 1.0f; material.reflectance = 0.04f; material.SetDoubleSided(x.doubleSided); // metallic-roughness workflow: auto baseColorTexture = x.values.find("baseColorTexture"); auto metallicRoughnessTexture = x.values.find("metallicRoughnessTexture"); auto baseColorFactor = x.values.find("baseColorFactor"); auto roughnessFactor = x.values.find("roughnessFactor"); auto metallicFactor = x.values.find("metallicFactor"); // common workflow: auto normalTexture = x.additionalValues.find("normalTexture"); auto emissiveTexture = x.additionalValues.find("emissiveTexture"); auto occlusionTexture = x.additionalValues.find("occlusionTexture"); auto emissiveFactor = x.additionalValues.find("emissiveFactor"); auto alphaCutoff = x.additionalValues.find("alphaCutoff"); auto alphaMode = x.additionalValues.find("alphaMode"); if (baseColorTexture != x.values.end()) { auto& tex = state.gltfModel.textures[baseColorTexture->second.TextureIndex()]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::BASECOLORMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::BASECOLORMAP].name = img.uri; material.textures[MaterialComponent::BASECOLORMAP].uvset = baseColorTexture->second.TextureTexCoord(); } if (normalTexture != x.additionalValues.end()) { auto& tex = state.gltfModel.textures[normalTexture->second.TextureIndex()]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::NORMALMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::NORMALMAP].name = img.uri; material.textures[MaterialComponent::NORMALMAP].uvset = normalTexture->second.TextureTexCoord(); } if (metallicRoughnessTexture != x.values.end()) { auto& tex = state.gltfModel.textures[metallicRoughnessTexture->second.TextureIndex()]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::SURFACEMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SURFACEMAP].name = img.uri; material.textures[MaterialComponent::SURFACEMAP].uvset = metallicRoughnessTexture->second.TextureTexCoord(); } if (emissiveTexture != x.additionalValues.end()) { auto& tex = state.gltfModel.textures[emissiveTexture->second.TextureIndex()]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::EMISSIVEMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::EMISSIVEMAP].name = img.uri; material.textures[MaterialComponent::EMISSIVEMAP].uvset = emissiveTexture->second.TextureTexCoord(); } if (occlusionTexture != x.additionalValues.end()) { auto& tex = state.gltfModel.textures[occlusionTexture->second.TextureIndex()]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::OCCLUSIONMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::OCCLUSIONMAP].name = img.uri; material.textures[MaterialComponent::OCCLUSIONMAP].uvset = occlusionTexture->second.TextureTexCoord(); material.SetOcclusionEnabled_Secondary(true); } if (baseColorFactor != x.values.end()) { material.baseColor.x = float(baseColorFactor->second.ColorFactor()[0]); material.baseColor.y = float(baseColorFactor->second.ColorFactor()[1]); material.baseColor.z = float(baseColorFactor->second.ColorFactor()[2]); material.baseColor.w = float(baseColorFactor->second.ColorFactor()[3]); } if (roughnessFactor != x.values.end()) { material.roughness = float(roughnessFactor->second.Factor()); } if (metallicFactor != x.values.end()) { material.metalness = float(metallicFactor->second.Factor()); } if (emissiveFactor != x.additionalValues.end()) { material.emissiveColor.x = float(emissiveFactor->second.ColorFactor()[0]); material.emissiveColor.y = float(emissiveFactor->second.ColorFactor()[1]); material.emissiveColor.z = float(emissiveFactor->second.ColorFactor()[2]); material.emissiveColor.w = float(emissiveFactor->second.ColorFactor()[3]); } if (alphaMode != x.additionalValues.end()) { if (alphaMode->second.string_value.compare("BLEND") == 0) { material.userBlendMode = wi::enums::BLENDMODE_ALPHA; } if (alphaMode->second.string_value.compare("MASK") == 0) { material.alphaRef = 0.5f; } } if (alphaCutoff != x.additionalValues.end()) { material.alphaRef = 1 - float(alphaCutoff->second.Factor()); } auto ext_unlit = x.extensions.find("KHR_materials_unlit"); if (ext_unlit != x.extensions.end()) { // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit material.shaderType = MaterialComponent::SHADERTYPE_UNLIT; } auto ext_transmission = x.extensions.find("KHR_materials_transmission"); if (ext_transmission != x.extensions.end()) { // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission if (ext_transmission->second.Has("transmissionFactor")) { auto& factor = ext_transmission->second.Get("transmissionFactor"); material.transmission = float(factor.IsNumber() ? factor.Get() : factor.Get()); } if (ext_transmission->second.Has("transmissionTexture")) { int index = ext_transmission->second.Get("transmissionTexture").Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::TRANSMISSIONMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::TRANSMISSIONMAP].name = img.uri; material.textures[MaterialComponent::TRANSMISSIONMAP].uvset = (uint32_t)ext_transmission->second.Get("transmissionTexture").Get("texCoord").Get(); } } // specular-glossiness workflow: auto specularGlossinessWorkflow = x.extensions.find("KHR_materials_pbrSpecularGlossiness"); if (specularGlossinessWorkflow != x.extensions.end()) { // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness material.SetUseSpecularGlossinessWorkflow(true); if (specularGlossinessWorkflow->second.Has("diffuseTexture")) { int index = specularGlossinessWorkflow->second.Get("diffuseTexture").Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::BASECOLORMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::BASECOLORMAP].name = img.uri; material.textures[MaterialComponent::BASECOLORMAP].uvset = (uint32_t)specularGlossinessWorkflow->second.Get("diffuseTexture").Get("texCoord").Get(); } if (specularGlossinessWorkflow->second.Has("specularGlossinessTexture")) { int index = specularGlossinessWorkflow->second.Get("specularGlossinessTexture").Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::SURFACEMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SURFACEMAP].name = img.uri; material.textures[MaterialComponent::SURFACEMAP].uvset = (uint32_t)specularGlossinessWorkflow->second.Get("specularGlossinessTexture").Get("texCoord").Get(); } if (specularGlossinessWorkflow->second.Has("diffuseFactor")) { auto& factor = specularGlossinessWorkflow->second.Get("diffuseFactor"); material.baseColor.x = factor.ArrayLen() > 0 ? float(factor.Get(0).IsNumber() ? factor.Get(0).Get() : factor.Get(0).Get()) : 1.0f; material.baseColor.y = factor.ArrayLen() > 1 ? float(factor.Get(1).IsNumber() ? factor.Get(1).Get() : factor.Get(1).Get()) : 1.0f; material.baseColor.z = factor.ArrayLen() > 2 ? float(factor.Get(2).IsNumber() ? factor.Get(2).Get() : factor.Get(2).Get()) : 1.0f; material.baseColor.w = factor.ArrayLen() > 3 ? float(factor.Get(3).IsNumber() ? factor.Get(3).Get() : factor.Get(3).Get()) : 1.0f; } if (specularGlossinessWorkflow->second.Has("specularFactor")) { auto& factor = specularGlossinessWorkflow->second.Get("specularFactor"); material.specularColor.x = factor.ArrayLen() > 0 ? float(factor.Get(0).IsNumber() ? factor.Get(0).Get() : factor.Get(0).Get()) : 1.0f; material.specularColor.y = factor.ArrayLen() > 0 ? float(factor.Get(1).IsNumber() ? factor.Get(1).Get() : factor.Get(1).Get()) : 1.0f; material.specularColor.z = factor.ArrayLen() > 0 ? float(factor.Get(2).IsNumber() ? factor.Get(2).Get() : factor.Get(2).Get()) : 1.0f; material.specularColor.w = factor.ArrayLen() > 0 ? float(factor.Get(3).IsNumber() ? factor.Get(3).Get() : factor.Get(3).Get()) : 1.0f; } if (specularGlossinessWorkflow->second.Has("glossinessFactor")) { auto& factor = specularGlossinessWorkflow->second.Get("glossinessFactor"); material.roughness = 1 - float(factor.IsNumber() ? factor.Get() : factor.Get()); } } auto ext_sheen = x.extensions.find("KHR_materials_sheen"); if (ext_sheen != x.extensions.end()) { // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_sheen material.shaderType = MaterialComponent::SHADERTYPE_PBR_CLOTH; if (ext_sheen->second.Has("sheenColorFactor")) { auto& factor = ext_sheen->second.Get("sheenColorFactor"); material.sheenColor.x = factor.ArrayLen() > 0 ? float(factor.Get(0).IsNumber() ? factor.Get(0).Get() : factor.Get(0).Get()) : 1.0f; material.sheenColor.y = factor.ArrayLen() > 0 ? float(factor.Get(1).IsNumber() ? factor.Get(1).Get() : factor.Get(1).Get()) : 1.0f; material.sheenColor.z = factor.ArrayLen() > 0 ? float(factor.Get(2).IsNumber() ? factor.Get(2).Get() : factor.Get(2).Get()) : 1.0f; material.sheenColor.w = factor.ArrayLen() > 0 ? float(factor.Get(3).IsNumber() ? factor.Get(3).Get() : factor.Get(3).Get()) : 1.0f; } if (ext_sheen->second.Has("sheenColorTexture")) { auto& param = ext_sheen->second.Get("sheenColorTexture"); int index = param.Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::SHEENCOLORMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SHEENCOLORMAP].name = img.uri; material.textures[MaterialComponent::SHEENCOLORMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } if (ext_sheen->second.Has("sheenRoughnessFactor")) { auto& factor = ext_sheen->second.Get("sheenRoughnessFactor"); material.sheenRoughness = float(factor.IsNumber() ? factor.Get() : factor.Get()); } if (ext_sheen->second.Has("sheenRoughnessTexture")) { auto& param = ext_sheen->second.Get("sheenRoughnessTexture"); int index = param.Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::SHEENROUGHNESSMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SHEENROUGHNESSMAP].name = img.uri; material.textures[MaterialComponent::SHEENROUGHNESSMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } } auto ext_clearcoat = x.extensions.find("KHR_materials_clearcoat"); if (ext_clearcoat != x.extensions.end()) { // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat if (material.shaderType == MaterialComponent::SHADERTYPE_PBR_CLOTH) { material.shaderType = MaterialComponent::SHADERTYPE_PBR_CLOTH_CLEARCOAT; } else { material.shaderType = MaterialComponent::SHADERTYPE_PBR_CLEARCOAT; } if (ext_clearcoat->second.Has("clearcoatFactor")) { auto& factor = ext_clearcoat->second.Get("clearcoatFactor"); material.clearcoat = float(factor.IsNumber() ? factor.Get() : factor.Get()); } if (ext_clearcoat->second.Has("clearcoatTexture")) { auto& param = ext_clearcoat->second.Get("clearcoatTexture"); int index = param.Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::CLEARCOATMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::CLEARCOATMAP].name = img.uri; material.textures[MaterialComponent::CLEARCOATMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } if (ext_clearcoat->second.Has("clearcoatRoughnessFactor")) { auto& factor = ext_clearcoat->second.Get("clearcoatRoughnessFactor"); material.clearcoatRoughness = float(factor.IsNumber() ? factor.Get() : factor.Get()); } if (ext_clearcoat->second.Has("clearcoatRoughnessTexture")) { auto& param = ext_clearcoat->second.Get("clearcoatRoughnessTexture"); int index = param.Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::CLEARCOATROUGHNESSMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::CLEARCOATROUGHNESSMAP].name = img.uri; material.textures[MaterialComponent::CLEARCOATROUGHNESSMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } if (ext_clearcoat->second.Has("clearcoatNormalTexture")) { auto& param = ext_clearcoat->second.Get("clearcoatNormalTexture"); int index = param.Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::CLEARCOATNORMALMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::CLEARCOATNORMALMAP].name = img.uri; material.textures[MaterialComponent::CLEARCOATNORMALMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } } auto ext_ior = x.extensions.find("KHR_materials_ior"); if (ext_ior != x.extensions.end()) { // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior if (ext_ior->second.Has("ior")) { auto& factor = ext_ior->second.Get("ior"); float ior = float(factor.IsNumber() ? factor.Get() : factor.Get()); material.reflectance = std::pow((ior - 1.0f) / (ior + 1.0f), 2.0f); } } auto ext_specular = x.extensions.find("KHR_materials_specular"); if (ext_specular != x.extensions.end()) { // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular material.specularColor = XMFLOAT4(1, 1, 1, 1); if (ext_specular->second.Has("specularFactor")) { auto& factor = ext_specular->second.Get("specularFactor"); material.specularColor.w = float(factor.IsNumber() ? factor.Get() : factor.Get()); } if (ext_specular->second.Has("specularTexture")) { if (!material.textures[MaterialComponent::SURFACEMAP].resource.IsValid()) { auto& param = ext_specular->second.Get("specularTexture"); int index = param.Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::SURFACEMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SURFACEMAP].name = img.uri; material.textures[MaterialComponent::SURFACEMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } else if (!material.textures[MaterialComponent::SPECULARMAP].resource.IsValid()) { auto& param = ext_specular->second.Get("specularTexture"); int index = param.Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::SPECULARMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SPECULARMAP].name = img.uri; material.textures[MaterialComponent::SPECULARMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } else { wi::backlog::post("[KHR_materials_specular warning] specularTexture must be either in surfaceMap.a or specularColorTexture.a! specularTexture discarded!", wi::backlog::LogLevel::Warning); } } if (ext_specular->second.Has("specularColorTexture")) { auto& param = ext_specular->second.Get("specularColorTexture"); int index = param.Get("index").Get(); auto& tex = state.gltfModel.textures[index]; int img_source = tex.source; if (tex.extensions.count("KHR_texture_basisu")) { img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; material.textures[MaterialComponent::SPECULARMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SPECULARMAP].name = img.uri; material.textures[MaterialComponent::SPECULARMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } if (ext_specular->second.Has("specularColorFactor")) { auto& factor = ext_specular->second.Get("specularColorFactor"); material.specularColor.x = factor.ArrayLen() > 0 ? float(factor.Get(0).IsNumber() ? factor.Get(0).Get() : factor.Get(0).Get()) : 1.0f; material.specularColor.y = factor.ArrayLen() > 0 ? float(factor.Get(1).IsNumber() ? factor.Get(1).Get() : factor.Get(1).Get()) : 1.0f; material.specularColor.z = factor.ArrayLen() > 0 ? float(factor.Get(2).IsNumber() ? factor.Get(2).Get() : factor.Get(2).Get()) : 1.0f; } } } // Create meshes: for (auto& x : state.gltfModel.meshes) { Entity meshEntity = scene.Entity_CreateMesh(x.name); scene.Component_Attach(meshEntity, state.rootEntity); MeshComponent& mesh = *scene.meshes.GetComponent(meshEntity); for (auto& prim : x.primitives) { assert(prim.indices >= 0); // Fill indices: const tinygltf::Accessor& accessor = state.gltfModel.accessors[prim.indices]; const tinygltf::BufferView& bufferView = state.gltfModel.bufferViews[accessor.bufferView]; const tinygltf::Buffer& buffer = state.gltfModel.buffers[bufferView.buffer]; int stride = accessor.ByteStride(bufferView); size_t indexCount = accessor.count; size_t indexOffset = mesh.indices.size(); mesh.indices.resize(indexOffset + indexCount); mesh.subsets.push_back(MeshComponent::MeshSubset()); mesh.subsets.back().indexOffset = (uint32_t)indexOffset; mesh.subsets.back().indexCount = (uint32_t)indexCount; mesh.subsets.back().materialID = scene.materials.GetEntity(std::max(0, prim.material)); MaterialComponent* material = scene.materials.GetComponent(mesh.subsets.back().materialID); uint32_t vertexOffset = (uint32_t)mesh.vertex_positions.size(); const uint8_t* data = buffer.data.data() + accessor.byteOffset + bufferView.byteOffset; int index_remap[3]; if (transform_to_LH) { index_remap[0] = 0; index_remap[1] = 1; index_remap[2] = 2; } else { index_remap[0] = 0; index_remap[1] = 2; index_remap[2] = 1; } if (stride == 1) { for (size_t i = 0; i < indexCount; i += 3) { mesh.indices[indexOffset + i + 0] = vertexOffset + data[i + index_remap[0]]; mesh.indices[indexOffset + i + 1] = vertexOffset + data[i + index_remap[1]]; mesh.indices[indexOffset + i + 2] = vertexOffset + data[i + index_remap[2]]; } } else if (stride == 2) { for (size_t i = 0; i < indexCount; i += 3) { mesh.indices[indexOffset + i + 0] = vertexOffset + ((uint16_t*)data)[i + index_remap[0]]; mesh.indices[indexOffset + i + 1] = vertexOffset + ((uint16_t*)data)[i + index_remap[1]]; mesh.indices[indexOffset + i + 2] = vertexOffset + ((uint16_t*)data)[i + index_remap[2]]; } } else if (stride == 4) { for (size_t i = 0; i < indexCount; i += 3) { mesh.indices[indexOffset + i + 0] = vertexOffset + ((uint32_t*)data)[i + index_remap[0]]; mesh.indices[indexOffset + i + 1] = vertexOffset + ((uint32_t*)data)[i + index_remap[1]]; mesh.indices[indexOffset + i + 2] = vertexOffset + ((uint32_t*)data)[i + index_remap[2]]; } } else { assert(0 && "unsupported index stride!"); } for (auto& attr : prim.attributes) { const std::string& attr_name = attr.first; int attr_data = attr.second; const tinygltf::Accessor& accessor = state.gltfModel.accessors[attr_data]; const tinygltf::BufferView& bufferView = state.gltfModel.bufferViews[accessor.bufferView]; const tinygltf::Buffer& buffer = state.gltfModel.buffers[bufferView.buffer]; int stride = accessor.ByteStride(bufferView); size_t vertexCount = accessor.count; const uint8_t* data = buffer.data.data() + accessor.byteOffset + bufferView.byteOffset; if (!attr_name.compare("POSITION")) { mesh.vertex_positions.resize(vertexOffset + vertexCount); for (size_t i = 0; i < vertexCount; ++i) { mesh.vertex_positions[vertexOffset + i] = ((XMFLOAT3*)data)[i]; } if (accessor.sparse.isSparse) { auto& sparse = accessor.sparse; const tinygltf::BufferView& sparse_indices_view = state.gltfModel.bufferViews[sparse.indices.bufferView]; const tinygltf::BufferView& sparse_values_view = state.gltfModel.bufferViews[sparse.values.bufferView]; const tinygltf::Buffer& sparse_indices_buffer = state.gltfModel.buffers[sparse_indices_view.buffer]; const tinygltf::Buffer& sparse_values_buffer = state.gltfModel.buffers[sparse_values_view.buffer]; const uint8_t* sparse_indices_data = sparse_indices_buffer.data.data() + sparse.indices.byteOffset + sparse_indices_view.byteOffset; const uint8_t* sparse_values_data = sparse_values_buffer.data.data() + sparse.values.byteOffset + sparse_values_view.byteOffset; switch (sparse.indices.componentType) { default: case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: for (int s = 0; s < sparse.count; ++s) { mesh.vertex_positions[sparse_indices_data[s]] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: for (int s = 0; s < sparse.count; ++s) { mesh.vertex_positions[((const uint16_t*)sparse_indices_data)[s]] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: for (int s = 0; s < sparse.count; ++s) { mesh.vertex_positions[((const uint32_t*)sparse_indices_data)[s]] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; } } } else if (!attr_name.compare("NORMAL")) { mesh.vertex_normals.resize(vertexOffset + vertexCount); for (size_t i = 0; i < vertexCount; ++i) { mesh.vertex_normals[vertexOffset + i] = ((XMFLOAT3*)data)[i]; } if (accessor.sparse.isSparse) { auto& sparse = accessor.sparse; const tinygltf::BufferView& sparse_indices_view = state.gltfModel.bufferViews[sparse.indices.bufferView]; const tinygltf::BufferView& sparse_values_view = state.gltfModel.bufferViews[sparse.values.bufferView]; const tinygltf::Buffer& sparse_indices_buffer = state.gltfModel.buffers[sparse_indices_view.buffer]; const tinygltf::Buffer& sparse_values_buffer = state.gltfModel.buffers[sparse_values_view.buffer]; const uint8_t* sparse_indices_data = sparse_indices_buffer.data.data() + sparse.indices.byteOffset + sparse_indices_view.byteOffset; const uint8_t* sparse_values_data = sparse_values_buffer.data.data() + sparse.values.byteOffset + sparse_values_view.byteOffset; switch (sparse.indices.componentType) { default: case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: for (int s = 0; s < sparse.count; ++s) { mesh.vertex_normals[sparse_indices_data[s]] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: for (int s = 0; s < sparse.count; ++s) { mesh.vertex_normals[((const uint16_t*)sparse_indices_data)[s]] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: for (int s = 0; s < sparse.count; ++s) { mesh.vertex_normals[((const uint32_t*)sparse_indices_data)[s]] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; } } } else if (!attr_name.compare("TANGENT")) { mesh.vertex_tangents.resize(vertexOffset + vertexCount); for (size_t i = 0; i < vertexCount; ++i) { mesh.vertex_tangents[vertexOffset + i] = ((XMFLOAT4*)data)[i]; } } else if (!attr_name.compare("TEXCOORD_0")) { mesh.vertex_uvset_0.resize(vertexOffset + vertexCount); if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { for (size_t i = 0; i < vertexCount; ++i) { const XMFLOAT2& tex = ((XMFLOAT2*)data)[i]; mesh.vertex_uvset_0[vertexOffset + i].x = tex.x; mesh.vertex_uvset_0[vertexOffset + i].y = tex.y; } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { for (size_t i = 0; i < vertexCount; ++i) { const uint8_t& s = *(uint8_t*)((size_t)data + i * stride + 0); const uint8_t& t = *(uint8_t*)((size_t)data + i * stride + 1); mesh.vertex_uvset_0[vertexOffset + i].x = s / 255.0f; mesh.vertex_uvset_0[vertexOffset + i].y = t / 255.0f; } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { for (size_t i = 0; i < vertexCount; ++i) { const uint16_t& s = *(uint16_t*)((size_t)data + i * stride + 0 * sizeof(uint16_t)); const uint16_t& t = *(uint16_t*)((size_t)data + i * stride + 1 * sizeof(uint16_t)); mesh.vertex_uvset_0[vertexOffset + i].x = s / 65535.0f; mesh.vertex_uvset_0[vertexOffset + i].y = t / 65535.0f; } } } else if (!attr_name.compare("TEXCOORD_1")) { mesh.vertex_uvset_1.resize(vertexOffset + vertexCount); if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { for (size_t i = 0; i < vertexCount; ++i) { const XMFLOAT2& tex = *(XMFLOAT2*)((size_t)data + i * stride); mesh.vertex_uvset_1[vertexOffset + i].x = tex.x; mesh.vertex_uvset_1[vertexOffset + i].y = tex.y; } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { for (size_t i = 0; i < vertexCount; ++i) { const uint8_t& s = *(uint8_t*)((size_t)data + i * stride + 0); const uint8_t& t = *(uint8_t*)((size_t)data + i * stride + 1); mesh.vertex_uvset_1[vertexOffset + i].x = s / 255.0f; mesh.vertex_uvset_1[vertexOffset + i].y = t / 255.0f; } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { for (size_t i = 0; i < vertexCount; ++i) { const uint16_t& s = *(uint16_t*)((size_t)data + i * stride + 0 * sizeof(uint16_t)); const uint16_t& t = *(uint16_t*)((size_t)data + i * stride + 1 * sizeof(uint16_t)); mesh.vertex_uvset_1[vertexOffset + i].x = s / 65535.0f; mesh.vertex_uvset_1[vertexOffset + i].y = t / 65535.0f; } } } else if (!attr_name.compare("JOINTS_0")) { mesh.vertex_boneindices.resize(vertexOffset + vertexCount); if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { struct JointTmp { uint8_t ind[4]; }; for (size_t i = 0; i < vertexCount; ++i) { const JointTmp& joint = ((JointTmp*)data)[i]; mesh.vertex_boneindices[vertexOffset + i].x = joint.ind[0]; mesh.vertex_boneindices[vertexOffset + i].y = joint.ind[1]; mesh.vertex_boneindices[vertexOffset + i].z = joint.ind[2]; mesh.vertex_boneindices[vertexOffset + i].w = joint.ind[3]; } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { struct JointTmp { uint16_t ind[4]; }; for (size_t i = 0; i < vertexCount; ++i) { const JointTmp& joint = ((JointTmp*)data)[i]; mesh.vertex_boneindices[vertexOffset + i].x = joint.ind[0]; mesh.vertex_boneindices[vertexOffset + i].y = joint.ind[1]; mesh.vertex_boneindices[vertexOffset + i].z = joint.ind[2]; mesh.vertex_boneindices[vertexOffset + i].w = joint.ind[3]; } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { struct JointTmp { uint32_t ind[4]; }; for (size_t i = 0; i < vertexCount; ++i) { const JointTmp& joint = ((JointTmp*)data)[i]; mesh.vertex_boneindices[vertexOffset + i].x = joint.ind[0]; mesh.vertex_boneindices[vertexOffset + i].y = joint.ind[1]; mesh.vertex_boneindices[vertexOffset + i].z = joint.ind[2]; mesh.vertex_boneindices[vertexOffset + i].w = joint.ind[3]; } } else { assert(0); } } else if (!attr_name.compare("WEIGHTS_0")) { mesh.vertex_boneweights.resize(vertexOffset + vertexCount); if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { for (size_t i = 0; i < vertexCount; ++i) { mesh.vertex_boneweights[vertexOffset + i] = *(XMFLOAT4*)((size_t)data + i * stride); } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { for (size_t i = 0; i < vertexCount; ++i) { const uint8_t& x = *(uint8_t*)((size_t)data + i * stride + 0); const uint8_t& y = *(uint8_t*)((size_t)data + i * stride + 1); const uint8_t& z = *(uint8_t*)((size_t)data + i * stride + 2); const uint8_t& w = *(uint8_t*)((size_t)data + i * stride + 3); mesh.vertex_boneweights[vertexOffset + i].x = x / 255.0f; mesh.vertex_boneweights[vertexOffset + i].x = y / 255.0f; mesh.vertex_boneweights[vertexOffset + i].x = z / 255.0f; mesh.vertex_boneweights[vertexOffset + i].x = w / 255.0f; } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { for (size_t i = 0; i < vertexCount; ++i) { const uint16_t& x = *(uint8_t*)((size_t)data + i * stride + 0 * sizeof(uint16_t)); const uint16_t& y = *(uint8_t*)((size_t)data + i * stride + 1 * sizeof(uint16_t)); const uint16_t& z = *(uint8_t*)((size_t)data + i * stride + 2 * sizeof(uint16_t)); const uint16_t& w = *(uint8_t*)((size_t)data + i * stride + 3 * sizeof(uint16_t)); mesh.vertex_boneweights[vertexOffset + i].x = x / 65535.0f; mesh.vertex_boneweights[vertexOffset + i].x = y / 65535.0f; mesh.vertex_boneweights[vertexOffset + i].x = z / 65535.0f; mesh.vertex_boneweights[vertexOffset + i].x = w / 65535.0f; } } } else if (!attr_name.compare("COLOR_0")) { if(material != nullptr) { material->SetUseVertexColors(true); } mesh.vertex_colors.resize(vertexOffset + vertexCount); if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { if (accessor.type == TINYGLTF_TYPE_VEC3) { for (size_t i = 0; i < vertexCount; ++i) { const XMFLOAT3& color = *(XMFLOAT3*)((size_t)data + i * stride); uint32_t rgba = wi::math::CompressColor(color); mesh.vertex_colors[vertexOffset + i] = rgba; } } else if (accessor.type == TINYGLTF_TYPE_VEC4) { for (size_t i = 0; i < vertexCount; ++i) { const XMFLOAT4& color = *(XMFLOAT4*)((size_t)data + i * stride); uint32_t rgba = wi::math::CompressColor(color); mesh.vertex_colors[vertexOffset + i] = rgba; } } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { if (accessor.type == TINYGLTF_TYPE_VEC3) { for (size_t i = 0; i < vertexCount; ++i) { const uint8_t& r = *(uint8_t*)((size_t)data + i * stride + 0); const uint8_t& g = *(uint8_t*)((size_t)data + i * stride + 1); const uint8_t& b = *(uint8_t*)((size_t)data + i * stride + 2); const uint8_t a = 0xFF; wi::Color color = wi::Color(r, g, b, a); mesh.vertex_colors[vertexOffset + i] = color; } } else if (accessor.type == TINYGLTF_TYPE_VEC4) { for (size_t i = 0; i < vertexCount; ++i) { const uint8_t& r = *(uint8_t*)((size_t)data + i * stride + 0); const uint8_t& g = *(uint8_t*)((size_t)data + i * stride + 1); const uint8_t& b = *(uint8_t*)((size_t)data + i * stride + 2); const uint8_t& a = *(uint8_t*)((size_t)data + i * stride + 3); wi::Color color = wi::Color(r, g, b, a); mesh.vertex_colors[vertexOffset + i] = color; } } } else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { if (accessor.type == TINYGLTF_TYPE_VEC3) { for (size_t i = 0; i < vertexCount; ++i) { const uint16_t& r = *(uint16_t*)((size_t)data + i * stride + 0 * sizeof(uint16_t)); const uint16_t& g = *(uint16_t*)((size_t)data + i * stride + 1 * sizeof(uint16_t)); const uint16_t& b = *(uint16_t*)((size_t)data + i * stride + 2 * sizeof(uint16_t)); uint32_t rgba = wi::math::CompressColor(XMFLOAT3(r / 65535.0f, g / 65535.0f, b / 65535.0f)); mesh.vertex_colors[vertexOffset + i] = rgba; } } else if (accessor.type == TINYGLTF_TYPE_VEC4) { for (size_t i = 0; i < vertexCount; ++i) { const uint16_t& r = *(uint16_t*)((size_t)data + i * stride + 0 * sizeof(uint16_t)); const uint16_t& g = *(uint16_t*)((size_t)data + i * stride + 1 * sizeof(uint16_t)); const uint16_t& b = *(uint16_t*)((size_t)data + i * stride + 2 * sizeof(uint16_t)); const uint16_t& a = *(uint16_t*)((size_t)data + i * stride + 3 * sizeof(uint16_t)); uint32_t rgba = wi::math::CompressColor(XMFLOAT4(r / 65535.0f, g / 65535.0f, b / 65535.0f, a / 65535.0f)); mesh.vertex_colors[vertexOffset + i] = rgba; } } } } } mesh.morph_targets.resize(prim.targets.size()); for (size_t i = 0; i < prim.targets.size(); i++) { MeshComponent::MorphTarget& morph_target = mesh.morph_targets[i]; for (auto& attr : prim.targets[i]) { const std::string& attr_name = attr.first; int attr_data = attr.second; const tinygltf::Accessor& accessor = state.gltfModel.accessors[attr_data]; if (!attr_name.compare("POSITION")) { if (accessor.sparse.isSparse) { auto& sparse = accessor.sparse; const tinygltf::BufferView& sparse_indices_view = state.gltfModel.bufferViews[sparse.indices.bufferView]; const tinygltf::BufferView& sparse_values_view = state.gltfModel.bufferViews[sparse.values.bufferView]; const tinygltf::Buffer& sparse_indices_buffer = state.gltfModel.buffers[sparse_indices_view.buffer]; const tinygltf::Buffer& sparse_values_buffer = state.gltfModel.buffers[sparse_values_view.buffer]; const uint8_t* sparse_indices_data = sparse_indices_buffer.data.data() + sparse.indices.byteOffset + sparse_indices_view.byteOffset; const uint8_t* sparse_values_data = sparse_values_buffer.data.data() + sparse.values.byteOffset + sparse_values_view.byteOffset; const size_t sparseOffset = morph_target.sparse_indices.size(); morph_target.vertex_positions.resize(sparseOffset + sparse.count); morph_target.sparse_indices.resize(sparseOffset + sparse.count); switch (sparse.indices.componentType) { default: case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: for (int s = 0; s < sparse.count; ++s) { morph_target.sparse_indices[sparseOffset + s] = vertexOffset + sparse_indices_data[s]; morph_target.vertex_positions[sparseOffset + s] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: for (int s = 0; s < sparse.count; ++s) { morph_target.sparse_indices[sparseOffset + s] = vertexOffset + ((const uint16_t*)sparse_indices_data)[s]; morph_target.vertex_positions[sparseOffset + s] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: for (int s = 0; s < sparse.count; ++s) { morph_target.sparse_indices[sparseOffset + s] = vertexOffset + ((const uint32_t*)sparse_indices_data)[s]; morph_target.vertex_positions[sparseOffset + s] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; } } else { const tinygltf::BufferView& bufferView = state.gltfModel.bufferViews[accessor.bufferView]; const tinygltf::Buffer& buffer = state.gltfModel.buffers[bufferView.buffer]; int stride = accessor.ByteStride(bufferView); size_t vertexCount = accessor.count; const unsigned char* data = buffer.data.data() + accessor.byteOffset + bufferView.byteOffset; morph_target.vertex_positions.resize(vertexOffset + vertexCount); for (size_t j = 0; j < vertexCount; ++j) { morph_target.vertex_positions[vertexOffset + j] = ((XMFLOAT3*)data)[j]; } } } else if (!attr_name.compare("NORMAL")) { if (accessor.sparse.isSparse) { auto& sparse = accessor.sparse; const tinygltf::BufferView& sparse_indices_view = state.gltfModel.bufferViews[sparse.indices.bufferView]; const tinygltf::BufferView& sparse_values_view = state.gltfModel.bufferViews[sparse.values.bufferView]; const tinygltf::Buffer& sparse_indices_buffer = state.gltfModel.buffers[sparse_indices_view.buffer]; const tinygltf::Buffer& sparse_values_buffer = state.gltfModel.buffers[sparse_values_view.buffer]; const uint8_t* sparse_indices_data = sparse_indices_buffer.data.data() + sparse.indices.byteOffset + sparse_indices_view.byteOffset; const uint8_t* sparse_values_data = sparse_values_buffer.data.data() + sparse.values.byteOffset + sparse_values_view.byteOffset; const size_t sparseOffset = morph_target.sparse_indices.size(); morph_target.vertex_normals.resize(sparseOffset + sparse.count); morph_target.sparse_indices.resize(sparseOffset + sparse.count); switch (sparse.indices.componentType) { default: case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: for (int s = 0; s < sparse.count; ++s) { morph_target.sparse_indices[sparseOffset + s] = vertexOffset + sparse_indices_data[s]; morph_target.vertex_normals[sparseOffset + s] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: for (int s = 0; s < sparse.count; ++s) { morph_target.sparse_indices[sparseOffset + s] = vertexOffset + ((const uint16_t*)sparse_indices_data)[s]; morph_target.vertex_normals[sparseOffset + s] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: for (int s = 0; s < sparse.count; ++s) { morph_target.sparse_indices[sparseOffset + s] = vertexOffset + ((const uint32_t*)sparse_indices_data)[s]; morph_target.vertex_normals[sparseOffset + s] = ((const XMFLOAT3*)sparse_values_data)[s]; } break; } } else { const tinygltf::BufferView& bufferView = state.gltfModel.bufferViews[accessor.bufferView]; const tinygltf::Buffer& buffer = state.gltfModel.buffers[bufferView.buffer]; int stride = accessor.ByteStride(bufferView); size_t vertexCount = accessor.count; const unsigned char* data = buffer.data.data() + accessor.byteOffset + bufferView.byteOffset; morph_target.vertex_normals.resize(vertexOffset + vertexCount); for (size_t j = 0; j < vertexCount; ++j) { morph_target.vertex_normals[vertexOffset + j] = ((XMFLOAT3*)data)[j]; } } } } } } for (size_t i = 0; i < x.weights.size(); i++) { mesh.morph_targets[i].weight = static_cast(x.weights[i]); } mesh.CreateRenderData(); } // Create armatures: for (auto& skin : state.gltfModel.skins) { Entity armatureEntity = CreateEntity(); scene.names.Create(armatureEntity) = skin.name; scene.layers.Create(armatureEntity); scene.transforms.Create(armatureEntity); scene.Component_Attach(armatureEntity, state.rootEntity); ArmatureComponent& armature = scene.armatures.Create(armatureEntity); if (skin.inverseBindMatrices >= 0) { const tinygltf::Accessor &accessor = state.gltfModel.accessors[skin.inverseBindMatrices]; const tinygltf::BufferView &bufferView = state.gltfModel.bufferViews[accessor.bufferView]; const tinygltf::Buffer &buffer = state.gltfModel.buffers[bufferView.buffer]; armature.inverseBindMatrices.resize(accessor.count); memcpy(armature.inverseBindMatrices.data(), &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(XMFLOAT4X4)); } else { assert(0); } } // Create transform hierarchy, assign objects, meshes, armatures, cameras: const tinygltf::Scene &gltfScene = state.gltfModel.scenes[std::max(0, state.gltfModel.defaultScene)]; for (size_t i = 0; i < gltfScene.nodes.size(); i++) { LoadNode(gltfScene.nodes[i], state.rootEntity, state); } // Create armature-bone mappings: int armatureIndex = 0; for (auto& skin : state.gltfModel.skins) { Entity armatureEntity = scene.armatures.GetEntity(armatureIndex); ArmatureComponent& armature = scene.armatures[armatureIndex++]; const size_t jointCount = skin.joints.size(); armature.boneCollection.resize(jointCount); // Create bone collection: for (size_t i = 0; i < jointCount; ++i) { int jointIndex = skin.joints[i]; Entity boneEntity = state.entityMap[jointIndex]; armature.boneCollection[i] = boneEntity; Import_Mixamo_Bone(state, armatureEntity, boneEntity, state.gltfModel.nodes[jointIndex]); } } // Create animations: for (auto& anim : state.gltfModel.animations) { Entity entity = CreateEntity(); scene.names.Create(entity) = anim.name; scene.Component_Attach(entity, state.rootEntity); AnimationComponent& animationcomponent = scene.animations.Create(entity); animationcomponent.samplers.resize(anim.samplers.size()); animationcomponent.channels.resize(anim.channels.size()); for (size_t i = 0; i < anim.samplers.size(); ++i) { auto& sam = anim.samplers[i]; if (!sam.interpolation.compare("LINEAR")) { animationcomponent.samplers[i].mode = AnimationComponent::AnimationSampler::Mode::LINEAR; } else if (!sam.interpolation.compare("STEP")) { animationcomponent.samplers[i].mode = AnimationComponent::AnimationSampler::Mode::STEP; } else if (!sam.interpolation.compare("CUBICSPLINE")) { animationcomponent.samplers[i].mode = AnimationComponent::AnimationSampler::Mode::CUBICSPLINE; } animationcomponent.samplers[i].data = CreateEntity(); scene.Component_Attach(animationcomponent.samplers[i].data, state.rootEntity); AnimationDataComponent& animationdata = scene.animation_datas.Create(animationcomponent.samplers[i].data); // AnimationSampler input = keyframe times { const tinygltf::Accessor& accessor = state.gltfModel.accessors[sam.input]; const tinygltf::BufferView& bufferView = state.gltfModel.bufferViews[accessor.bufferView]; const tinygltf::Buffer& buffer = state.gltfModel.buffers[bufferView.buffer]; assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT); int stride = accessor.ByteStride(bufferView); size_t count = accessor.count; animationdata.keyframe_times.resize(count); const unsigned char* data = buffer.data.data() + accessor.byteOffset + bufferView.byteOffset; assert(stride == 4); for (size_t j = 0; j < count; ++j) { float time = ((float*)data)[j]; animationdata.keyframe_times[j] = time; animationcomponent.start = std::min(animationcomponent.start, time); animationcomponent.end = std::max(animationcomponent.end, time); } } // AnimationSampler output = keyframe data { const tinygltf::Accessor& accessor = state.gltfModel.accessors[sam.output]; const tinygltf::BufferView& bufferView = state.gltfModel.bufferViews[accessor.bufferView]; const tinygltf::Buffer& buffer = state.gltfModel.buffers[bufferView.buffer]; int stride = accessor.ByteStride(bufferView); size_t count = accessor.count; const unsigned char* data = buffer.data.data() + accessor.byteOffset + bufferView.byteOffset; switch (accessor.type) { case TINYGLTF_TYPE_SCALAR: { assert(stride == sizeof(float)); animationdata.keyframe_data.resize(count); for (size_t j = 0; j < count; ++j) { animationdata.keyframe_data[j] = ((float*)data)[j]; } } break; case TINYGLTF_TYPE_VEC3: { assert(stride == sizeof(XMFLOAT3)); animationdata.keyframe_data.resize(count * 3); for (size_t j = 0; j < count; ++j) { ((XMFLOAT3*)animationdata.keyframe_data.data())[j] = ((XMFLOAT3*)data)[j]; } } break; case TINYGLTF_TYPE_VEC4: { assert(stride == sizeof(XMFLOAT4)); animationdata.keyframe_data.resize(count * 4); for (size_t j = 0; j < count; ++j) { ((XMFLOAT4*)animationdata.keyframe_data.data())[j] = ((XMFLOAT4*)data)[j]; } } break; default: assert(0); break; } } } for (size_t i = 0; i < anim.channels.size(); ++i) { auto& channel = anim.channels[i]; animationcomponent.channels[i].target = state.entityMap[channel.target_node]; assert(channel.sampler >= 0); animationcomponent.channels[i].samplerIndex = (uint32_t)channel.sampler; if (!channel.target_path.compare("scale")) { animationcomponent.channels[i].path = AnimationComponent::AnimationChannel::Path::SCALE; } else if (!channel.target_path.compare("rotation")) { animationcomponent.channels[i].path = AnimationComponent::AnimationChannel::Path::ROTATION; } else if (!channel.target_path.compare("translation")) { animationcomponent.channels[i].path = AnimationComponent::AnimationChannel::Path::TRANSLATION; } else if (!channel.target_path.compare("weights")) { animationcomponent.channels[i].path = AnimationComponent::AnimationChannel::Path::WEIGHTS; } else { animationcomponent.channels[i].path = AnimationComponent::AnimationChannel::Path::UNKNOWN; } } } // Create lights: int lightIndex = 0; for (auto& x : state.gltfModel.lights) { Entity entity = scene.lights.GetEntity(lightIndex); LightComponent& light = scene.lights[lightIndex++]; NameComponent& name = *scene.names.GetComponent(entity); name = x.name; if (!x.type.compare("spot")) { light.type = LightComponent::LightType::SPOT; } if (!x.type.compare("point")) { light.type = LightComponent::LightType::POINT; } if (!x.type.compare("directional")) { light.type = LightComponent::LightType::DIRECTIONAL; } if (!x.color.empty()) { light.color = XMFLOAT3(float(x.color[0]), float(x.color[1]), float(x.color[2])); } light.intensity = float(x.intensity); light.range = x.range > 0 ? float(x.range) : std::numeric_limits::max(); light.outerConeAngle = float(x.spot.outerConeAngle); light.innerConeAngle = float(x.spot.innerConeAngle); light.SetCastShadow(true); // In gltf, default light direction is forward, in engine, it's downwards, so apply a rotation: TransformComponent& transform = *scene.transforms.GetComponent(entity); transform.RotateRollPitchYaw(XMFLOAT3(XM_PIDIV2, 0, 0)); } int cameraIndex = 0; for (auto& x : state.gltfModel.cameras) { Entity entity = scene.cameras.GetEntity(cameraIndex); CameraComponent& camera = scene.cameras[cameraIndex++]; TransformComponent& transform = *scene.transforms.GetComponent(entity); transform.RotateRollPitchYaw(XMFLOAT3(XM_PI, 0, XM_PI)); } if (transform_to_LH) { TransformComponent& transform = *scene.transforms.GetComponent(state.rootEntity); transform.scale_local.z = -transform.scale_local.z; transform.SetDirty(); } Import_Extension_VRM(state); Import_Extension_VRMC(state); // Update the scene, to have up to date values immediately after loading: // For example, snap to camera functionality relies on this scene.Update(0); } void Import_Extension_VRM(LoaderState& state) { auto ext_vrm = state.gltfModel.extensions.find("VRM"); if (ext_vrm != state.gltfModel.extensions.end()) { // Rotate VRM humanoid character to face -Z: TransformComponent& transform = *state.scene->transforms.GetComponent(state.rootEntity); transform.RotateRollPitchYaw(XMFLOAT3(0, XM_PI, 0)); if (ext_vrm->second.Has("blendShapeMaster")) { // https://github.com/vrm-c/vrm-specification/tree/master/specification/0.0#vrm-extension-morph-setting-jsonextensionsvrmblendshapemaster Entity entity = state.rootEntity; if (state.scene->expressions.Contains(entity)) { entity = CreateEntity(); state.scene->Component_Attach(entity, state.rootEntity); state.scene->names.Create(entity) = "blendShapeMaster"; } ExpressionComponent& component = state.scene->expressions.Create(entity); const auto& blendShapeMaster = ext_vrm->second.Get("blendShapeMaster"); if (blendShapeMaster.Has("blendShapeGroups")) { const auto& blendShapeGroups = blendShapeMaster.Get("blendShapeGroups"); for (size_t blendShapeGroup_index = 0; blendShapeGroup_index < blendShapeGroups.ArrayLen(); ++blendShapeGroup_index) { const auto& blendShapeGroup = blendShapeGroups.Get(int(blendShapeGroup_index)); ExpressionComponent::Expression& expression = component.expressions.emplace_back(); if (blendShapeGroup.Has("name")) { const auto& value = blendShapeGroup.Get("name"); expression.name = value.Get(); } if (blendShapeGroup.Has("presetName")) { const auto& value = blendShapeGroup.Get("presetName"); std::string presetName = wi::helper::toUpper(value.Get()); if (!presetName.compare("JOY")) { expression.preset = ExpressionComponent::Preset::Happy; } else if (!presetName.compare("ANGRY")) { expression.preset = ExpressionComponent::Preset::Angry; } else if (!presetName.compare("SORROW")) { expression.preset = ExpressionComponent::Preset::Sad; } else if (!presetName.compare("FUN")) { expression.preset = ExpressionComponent::Preset::Relaxed; } else if (!presetName.compare("A")) { expression.preset = ExpressionComponent::Preset::Aa; } else if (!presetName.compare("I")) { expression.preset = ExpressionComponent::Preset::Ih; } else if (!presetName.compare("U")) { expression.preset = ExpressionComponent::Preset::Ou; } else if (!presetName.compare("E")) { expression.preset = ExpressionComponent::Preset::Ee; } else if (!presetName.compare("O")) { expression.preset = ExpressionComponent::Preset::Oh; } else if (!presetName.compare("BLINK")) { expression.preset = ExpressionComponent::Preset::Blink; } else if (!presetName.compare("BLINK_L")) { expression.preset = ExpressionComponent::Preset::BlinkLeft; } else if (!presetName.compare("BLINK_R")) { expression.preset = ExpressionComponent::Preset::BlinkRight; } else if (!presetName.compare("LOOKUP")) { expression.preset = ExpressionComponent::Preset::LookUp; } else if (!presetName.compare("LOOKDOWN")) { expression.preset = ExpressionComponent::Preset::LookDown; } else if (!presetName.compare("LOOKLEFT")) { expression.preset = ExpressionComponent::Preset::LookLeft; } else if (!presetName.compare("LOOKRIGHT")) { expression.preset = ExpressionComponent::Preset::LookRight; } else if (!presetName.compare("NEUTRAL")) { expression.preset = ExpressionComponent::Preset::Neutral; } const size_t preset_index = (size_t)expression.preset; if (preset_index < arraysize(component.presets)) { component.presets[preset_index] = (int)component.expressions.size() - 1; } } if (blendShapeGroup.Has("isBinary")) { const auto& value = blendShapeGroup.Get("isBinary"); expression.SetBinary(value.Get()); } if (blendShapeGroup.Has("binds")) { const auto& binds = blendShapeGroup.Get("binds"); for (size_t bind_index = 0; bind_index < binds.ArrayLen(); ++bind_index) { const auto& bind = binds.Get(int(bind_index)); ExpressionComponent::Expression::MorphTargetBinding& morph_target_binding = expression.morph_target_bindings.emplace_back(); if (bind.Has("mesh")) { const auto& value = bind.Get("mesh"); morph_target_binding.meshID = state.scene->meshes.GetEntity(value.GetNumberAsInt()); } if (bind.Has("index")) { const auto& value = bind.Get("index"); morph_target_binding.index = value.GetNumberAsInt(); } if (bind.Has("weight")) { const auto& value = bind.Get("weight"); morph_target_binding.weight = float(value.GetNumberAsInt()) / 100.0f; } } } } } } if (ext_vrm->second.Has("secondaryAnimation")) { // https://github.com/vrm-c/vrm-specification/tree/master/specification/0.0#vrm-extension-spring-bone-settings-jsonextensionsvrmsecondaryanimation const auto& secondaryAnimation = ext_vrm->second.Get("secondaryAnimation"); if (secondaryAnimation.Has("boneGroups")) { const auto& boneGroups = secondaryAnimation.Get("boneGroups"); for (size_t boneGroup_index = 0; boneGroup_index < boneGroups.ArrayLen(); ++boneGroup_index) { const auto& boneGroup = boneGroups.Get(int(boneGroup_index)); SpringComponent component; if (boneGroup.Has("dragForce")) { auto& value = boneGroup.Get("dragForce"); component.dragForce = float(value.GetNumberAsDouble()); } if (boneGroup.Has("gravityDir")) { auto& value = boneGroup.Get("gravityDir"); if (value.Has("x")) { component.gravityDir.x = float(value.Get("x").GetNumberAsDouble()); } if (value.Has("y")) { component.gravityDir.y = float(value.Get("y").GetNumberAsDouble()); } if (value.Has("z")) { component.gravityDir.z = float(value.Get("z").GetNumberAsDouble()); } } //if (boneGroup.Has("center")) //{ // auto& value = boneGroup.Get("center"); // center = float(value.GetNumberAsDouble()); //} if (boneGroup.Has("gravityPower")) { auto& value = boneGroup.Get("gravityPower"); component.gravityPower = float(value.GetNumberAsDouble()); } if (boneGroup.Has("hitRadius")) { auto& value = boneGroup.Get("hitRadius"); component.hitRadius = float(value.GetNumberAsDouble()); } if (boneGroup.Has("stiffiness")) // yes, not stiffness, but stiffiness { auto& value = boneGroup.Get("stiffiness"); component.stiffnessForce = float(value.GetNumberAsDouble()); } if (boneGroup.Has("colliderGroups")) { const auto& colliderGroups = boneGroup.Get("colliderGroups"); for (size_t collider_group_index = 0; collider_group_index < colliderGroups.ArrayLen(); ++collider_group_index) { int colliderGroupIndex = colliderGroups.Get(int(collider_group_index)).GetNumberAsInt(); const auto& colliderGroup = secondaryAnimation.Get("colliderGroups").Get(colliderGroupIndex); Entity transformID = INVALID_ENTITY; if (colliderGroup.Has("node")) { auto& value = colliderGroup.Get("node"); int node = value.GetNumberAsInt(); transformID = state.entityMap[node]; } if (colliderGroup.Has("colliders")) { const auto& colliders = colliderGroup.Get("colliders"); for (size_t collider_index = 0; collider_index < colliders.ArrayLen(); ++collider_index) { Entity colliderID = CreateEntity(); //component.colliders.push_back(colliderID); // for now, we will just use all colliders in the scene for every spring ColliderComponent& collider_component = state.scene->colliders.Create(colliderID); state.scene->transforms.Create(colliderID); state.scene->layers.Create(colliderID); state.scene->Component_Attach(colliderID, transformID, true); const auto& collider = colliders.Get(int(collider_index)); if (collider.Has("offset")) { auto& value = collider.Get("offset"); if (value.Has("x")) { collider_component.offset.x = float(value.Get("x").GetNumberAsDouble()); } if (value.Has("y")) { collider_component.offset.y = float(value.Get("y").GetNumberAsDouble()); } if (value.Has("z")) { collider_component.offset.z = float(value.Get("z").GetNumberAsDouble()); } } if (collider.Has("radius")) { auto& value = collider.Get("radius"); collider_component.radius = float(value.GetNumberAsDouble()); } } } } } if (boneGroup.Has("bones")) { auto& bones = boneGroup.Get("bones"); for (size_t bone_index = 0; bone_index < bones.ArrayLen(); ++bone_index) { const auto& bone = bones.Get(int(bone_index)); int node = bone.GetNumberAsInt(); Entity entity = state.entityMap[node]; state.scene->springs.Create(entity) = component; wi::vector stack = state.gltfModel.nodes[node].children; while (!stack.empty()) { int child_node = stack.back(); stack.pop_back(); Entity child_entity = state.entityMap[child_node]; state.scene->springs.Create(child_entity) = component; stack.insert(stack.end(), state.gltfModel.nodes[child_node].children.begin(), state.gltfModel.nodes[child_node].children.end()); } } } } } } if (ext_vrm->second.Has("humanoid")) { // https://github.com/vrm-c/vrm-specification/tree/master/specification/0.0#vrm-extension-models-bone-mapping-jsonextensionsvrmhumanoid Entity entity = state.rootEntity; if (state.scene->humanoids.Contains(entity)) { entity = CreateEntity(); state.scene->Component_Attach(entity, state.rootEntity); state.scene->names.Create(entity) = "humanoid"; } HumanoidComponent& component = state.scene->humanoids.Create(entity); component.default_look_direction = XMFLOAT3(0, 0, -1); const auto& humanoid = ext_vrm->second.Get("humanoid"); if (humanoid.Has("humanBones")) { const auto& humanBones = humanoid.Get("humanBones"); for (size_t bone_index = 0; bone_index < humanBones.ArrayLen(); ++bone_index) { const auto& humanBone = humanBones.Get(int(bone_index)); Entity boneID = INVALID_ENTITY; // https://github.com/vrm-c/vrm-specification/tree/master/specification/0.0#defined-bones if (humanBone.Has("node")) { const auto& value = humanBone.Get("node"); int node = value.GetNumberAsInt(); boneID = state.entityMap[node]; } if (humanBone.Has("bone")) { const auto& value = humanBone.Get("bone"); const std::string& type = wi::helper::toUpper(value.Get()); if (!type.compare("NECK")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Neck)] = boneID; } else if (!type.compare("HEAD")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Head)] = boneID; } else if (!type.compare("LEFTEYE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftEye)] = boneID; } else if (!type.compare("RIGHTEYE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightEye)] = boneID; } else if (!type.compare("JAW")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Jaw)] = boneID; } else if (!type.compare("HIPS")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Hips)] = boneID; } else if (!type.compare("SPINE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Spine)] = boneID; } else if (!type.compare("CHEST")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Chest)] = boneID; } else if (!type.compare("UPPERCHEST")) { component.bones[size_t(HumanoidComponent::HumanoidBone::UpperChest)] = boneID; } else if (!type.compare("LEFTSHOULDER")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftShoulder)] = boneID; } else if (!type.compare("RIGHTSHOULDER")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightShoulder)] = boneID; } else if (!type.compare("LEFTUPPERARM")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperArm)] = boneID; } else if (!type.compare("RIGHTUPPERARM")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightUpperArm)] = boneID; } else if (!type.compare("LEFTLOWERARM")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerArm)] = boneID; } else if (!type.compare("RIGHTLOWERARM")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLowerArm)] = boneID; } else if (!type.compare("LEFTHAND")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftHand)] = boneID; } else if (!type.compare("RIGHTHAND")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightHand)] = boneID; } else if (!type.compare("LEFTUPPERLEG")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperLeg)] = boneID; } else if (!type.compare("RIGHTUPPERLEG")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightUpperLeg)] = boneID; } else if (!type.compare("LEFTLOWERLEG")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerLeg)] = boneID; } else if (!type.compare("RIGHTLOWERLEG")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLowerLeg)] = boneID; } else if (!type.compare("LEFTFOOT")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftFoot)] = boneID; } else if (!type.compare("RIGHTFOOT")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightFoot)] = boneID; } else if (!type.compare("LEFTTOES")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftToes)] = boneID; } else if (!type.compare("RIGHTTOES")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightToes)] = boneID; } else if (!type.compare("LEFTTHUMBPROXIMAL")) { // VRM 0.0 thumb proximal = VRM 1.0 thumb metacarpal component.bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbMetacarpal)] = boneID; } else if (!type.compare("RIGHTTHUMBPROXIMAL")) { // VRM 0.0 thumb proximal = VRM 1.0 thumb metacarpal component.bones[size_t(HumanoidComponent::HumanoidBone::RightThumbMetacarpal)] = boneID; } else if (!type.compare("LEFTTHUMBINTERMEDIATE")) { // VRM 0.0 thumb intermediate = VRM 1.0 thumb proximal component.bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbProximal)] = boneID; } else if (!type.compare("RIGHTTHUMBINTERMEDIATE")) { // VRM 0.0 thumb intermediate = VRM 1.0 thumb proximal component.bones[size_t(HumanoidComponent::HumanoidBone::RightThumbProximal)] = boneID; } else if (!type.compare("LEFTTHUMBDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbDistal)] = boneID; } else if (!type.compare("RIGHTTHUMBDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightThumbDistal)] = boneID; } else if (!type.compare("LEFTINDEXPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexProximal)] = boneID; } else if (!type.compare("RIGHTINDEXPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightIndexProximal)] = boneID; } else if (!type.compare("LEFTINDEXINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexIntermediate)] = boneID; } else if (!type.compare("RIGHTINDEXINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightIndexIntermediate)] = boneID; } else if (!type.compare("LEFTINDEXDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexDistal)] = boneID; } else if (!type.compare("RIGHTINDEXDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightIndexDistal)] = boneID; } else if (!type.compare("LEFTMIDDLEPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleProximal)] = boneID; } else if (!type.compare("RIGHTMIDDLEPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleProximal)] = boneID; } else if (!type.compare("LEFTMIDDLEINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleIntermediate)] = boneID; } else if (!type.compare("RIGHTMIDDLEINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleIntermediate)] = boneID; } else if (!type.compare("LEFTMIDDLEDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleDistal)] = boneID; } else if (!type.compare("RIGHTMIDDLEDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleDistal)] = boneID; } else if (!type.compare("LEFTRINGPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftRingProximal)] = boneID; } else if (!type.compare("RIGHTRINGPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightRingProximal)] = boneID; } else if (!type.compare("LEFTRINGINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftRingIntermediate)] = boneID; } else if (!type.compare("RIGHTRINGINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightRingIntermediate)] = boneID; } else if (!type.compare("LEFTRINGDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftRingDistal)] = boneID; } else if (!type.compare("RIGHTRINGDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightRingDistal)] = boneID; } else if (!type.compare("LEFTLITTLEPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleProximal)] = boneID; } else if (!type.compare("RIGHTLITTLEPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLittleProximal)] = boneID; } else if (!type.compare("LEFTLITTLEINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleIntermediate)] = boneID; } else if (!type.compare("RIGHTLITTLEINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLittleIntermediate)] = boneID; } else if (!type.compare("LEFTLITTLEDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleDistal)] = boneID; } else if (!type.compare("RIGHTLITTLEDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLittleDistal)] = boneID; } } } } } if (ext_vrm->second.Has("materialProperties")) { const auto& materialProperties = ext_vrm->second.Get("materialProperties"); for (size_t material_index = 0; material_index < materialProperties.ArrayLen(); ++material_index) { const auto& material = materialProperties.Get(int(material_index)); MaterialComponent* component = nullptr; if (material.Has("name")) { const auto& value = material.Get("name"); const std::string& name = value.Get(); Entity entity = state.scene->Entity_FindByName(name); component = state.scene->materials.GetComponent(entity); } if (component != nullptr) { if (material.Has("shader")) { const auto& value = material.Get("shader"); const std::string& name = wi::helper::toUpper(value.Get()); if (!name.compare("VRM/MTOON")) { component->shaderType = MaterialComponent::SHADERTYPE_CARTOON; } } } } } } } void Import_Extension_VRMC(LoaderState& state) { auto ext_vrm = state.gltfModel.extensions.find("VRMC_vrm"); if (ext_vrm != state.gltfModel.extensions.end()) { if (ext_vrm->second.Has("expressions")) { // https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_vrm-1.0-beta/expressions.md#vrmc_vrmexpressions Entity entity = state.rootEntity; if (state.scene->expressions.Contains(entity)) { entity = CreateEntity(); state.scene->Component_Attach(entity, state.rootEntity); state.scene->names.Create(entity) = "expressions"; } ExpressionComponent& component = state.scene->expressions.Create(entity); const auto& expressions = ext_vrm->second.Get("expressions"); static const char* expression_types[] = { "preset", "custom", }; for (auto& expression_type : expression_types) { if (expressions.Has(expression_type)) { const auto& names = expressions.Get(expression_type); for (auto& name : names.Keys()) { const auto& vrm_expression = names.Get(name); ExpressionComponent::Expression& expression = component.expressions.emplace_back(); if (!strcmp(expression_type, "preset")) { std::string presetName = wi::helper::toUpper(name); if (!presetName.compare("HAPPY")) { expression.preset = ExpressionComponent::Preset::Happy; } else if (!presetName.compare("ANGRY")) { expression.preset = ExpressionComponent::Preset::Angry; } else if (!presetName.compare("SAD")) { expression.preset = ExpressionComponent::Preset::Sad; } else if (!presetName.compare("RELAXED")) { expression.preset = ExpressionComponent::Preset::Relaxed; } else if (!presetName.compare("SURPRISED")) { expression.preset = ExpressionComponent::Preset::Surprised; } else if (!presetName.compare("AA")) { expression.preset = ExpressionComponent::Preset::Aa; } else if (!presetName.compare("IH")) { expression.preset = ExpressionComponent::Preset::Ih; } else if (!presetName.compare("OU")) { expression.preset = ExpressionComponent::Preset::Ou; } else if (!presetName.compare("EE")) { expression.preset = ExpressionComponent::Preset::Ee; } else if (!presetName.compare("OH")) { expression.preset = ExpressionComponent::Preset::Oh; } else if (!presetName.compare("BLINK")) { expression.preset = ExpressionComponent::Preset::Blink; } else if (!presetName.compare("BLINKLEFT")) { expression.preset = ExpressionComponent::Preset::BlinkLeft; } else if (!presetName.compare("BLINKRIGHT")) { expression.preset = ExpressionComponent::Preset::BlinkRight; } else if (!presetName.compare("LOOKUP")) { expression.preset = ExpressionComponent::Preset::LookUp; } else if (!presetName.compare("LOOKDOWN")) { expression.preset = ExpressionComponent::Preset::LookDown; } else if (!presetName.compare("LOOKLEFT")) { expression.preset = ExpressionComponent::Preset::LookLeft; } else if (!presetName.compare("LOOKRIGHT")) { expression.preset = ExpressionComponent::Preset::LookRight; } else if (!presetName.compare("NEUTRAL")) { expression.preset = ExpressionComponent::Preset::Neutral; } const size_t preset_index = (size_t)expression.preset; if (preset_index < arraysize(component.presets)) { component.presets[preset_index] = (int)component.expressions.size() - 1; } } expression.name = name; if (vrm_expression.Has("isBinary")) { const auto& value = vrm_expression.Get("isBinary"); expression.SetBinary(value.Get()); } if (vrm_expression.Has("overrideMouth")) { const auto& value = vrm_expression.Get("overrideMouth"); const std::string& override_enum = value.Get(); if (!override_enum.compare("block")) { expression.override_mouth = ExpressionComponent::Override::Block; } if (!override_enum.compare("blend")) { expression.override_mouth = ExpressionComponent::Override::Blend; } } if (vrm_expression.Has("morphTargetBinds")) { const auto& morpTargetBinds = vrm_expression.Get("morphTargetBinds"); for (size_t morphTargetBind_index = 0; morphTargetBind_index < morpTargetBinds.ArrayLen(); ++morphTargetBind_index) { const auto& morphTargetBind = morpTargetBinds.Get(int(morphTargetBind_index)); ExpressionComponent::Expression::MorphTargetBinding& morph_target_binding = expression.morph_target_bindings.emplace_back(); if (morphTargetBind.Has("node")) { const auto& value = morphTargetBind.Get("node"); morph_target_binding.meshID = state.scene->meshes.GetEntity(state.gltfModel.nodes[value.GetNumberAsInt()].mesh); } if (morphTargetBind.Has("index")) { const auto& value = morphTargetBind.Get("index"); morph_target_binding.index = value.GetNumberAsInt(); } if (morphTargetBind.Has("weight")) { const auto& value = morphTargetBind.Get("weight"); morph_target_binding.weight = float(value.GetNumberAsDouble()); } } } //if (vrm_expression.Has("materialColorBinds")) //{ // const auto& materialColorBinds = vrm_expression.Get("materialColorBinds"); // // TODO: find example model and implement //} //if (vrm_expression.Has("textureTransformBinds ")) //{ // const auto& textureTransformBinds = vrm_expression.Get("textureTransformBinds"); // // TODO: find example model and implement //} } } } } if (ext_vrm->second.Has("humanoid")) { // https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_vrm-1.0-beta/humanoid.md Entity entity = state.rootEntity; if (state.scene->humanoids.Contains(entity)) { entity = CreateEntity(); state.scene->Component_Attach(entity, state.rootEntity); state.scene->names.Create(entity) = "humanoid"; } HumanoidComponent& component = state.scene->humanoids.Create(entity); const auto& humanoid = ext_vrm->second.Get("humanoid"); if (humanoid.Has("humanBones")) { const auto& humanBones = humanoid.Get("humanBones"); const auto& keys = humanBones.Keys(); for (const auto& key : keys) { Entity boneID = INVALID_ENTITY; const auto& value = humanBones.Get(key); if (value.Has("node")) { const auto& node_value = value.Get("node"); int node = node_value.GetNumberAsInt(); boneID = state.entityMap[node]; } const std::string& type = wi::helper::toUpper(key); if (!type.compare("NECK")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Neck)] = boneID; } else if (!type.compare("HEAD")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Head)] = boneID; } else if (!type.compare("LEFTEYE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftEye)] = boneID; } else if (!type.compare("RIGHTEYE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightEye)] = boneID; } else if (!type.compare("JAW")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Jaw)] = boneID; } else if (!type.compare("HIPS")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Hips)] = boneID; } else if (!type.compare("SPINE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Spine)] = boneID; } else if (!type.compare("CHEST")) { component.bones[size_t(HumanoidComponent::HumanoidBone::Chest)] = boneID; } else if (!type.compare("UPPERCHEST")) { component.bones[size_t(HumanoidComponent::HumanoidBone::UpperChest)] = boneID; } else if (!type.compare("LEFTSHOULDER")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftShoulder)] = boneID; } else if (!type.compare("RIGHTSHOULDER")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightShoulder)] = boneID; } else if (!type.compare("LEFTUPPERARM")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperArm)] = boneID; } else if (!type.compare("RIGHTUPPERARM")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightUpperArm)] = boneID; } else if (!type.compare("LEFTLOWERARM")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerArm)] = boneID; } else if (!type.compare("RIGHTLOWERARM")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLowerArm)] = boneID; } else if (!type.compare("LEFTHAND")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftHand)] = boneID; } else if (!type.compare("RIGHTHAND")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightHand)] = boneID; } else if (!type.compare("LEFTUPPERLEG")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperLeg)] = boneID; } else if (!type.compare("RIGHTUPPERLEG")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightUpperLeg)] = boneID; } else if (!type.compare("LEFTLOWERLEG")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerLeg)] = boneID; } else if (!type.compare("RIGHTLOWERLEG")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLowerLeg)] = boneID; } else if (!type.compare("LEFTFOOT")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftFoot)] = boneID; } else if (!type.compare("RIGHTFOOT")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightFoot)] = boneID; } else if (!type.compare("LEFTTOES")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftToes)] = boneID; } else if (!type.compare("RIGHTTOES")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightToes)] = boneID; } else if (!type.compare("LEFTTHUMBPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbProximal)] = boneID; } else if (!type.compare("RIGHTTHUMBPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightThumbProximal)] = boneID; } else if (!type.compare("LEFTTHUMBINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbMetacarpal)] = boneID; } else if (!type.compare("RIGHTTHUMBINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightThumbMetacarpal)] = boneID; } else if (!type.compare("LEFTTHUMBDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbDistal)] = boneID; } else if (!type.compare("RIGHTTHUMBDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightThumbDistal)] = boneID; } else if (!type.compare("LEFTINDEXPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexProximal)] = boneID; } else if (!type.compare("RIGHTINDEXPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightIndexProximal)] = boneID; } else if (!type.compare("LEFTINDEXINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexIntermediate)] = boneID; } else if (!type.compare("RIGHTINDEXINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightIndexIntermediate)] = boneID; } else if (!type.compare("LEFTINDEXDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexDistal)] = boneID; } else if (!type.compare("RIGHTINDEXDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightIndexDistal)] = boneID; } else if (!type.compare("LEFTMIDDLEPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleProximal)] = boneID; } else if (!type.compare("RIGHTMIDDLEPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleProximal)] = boneID; } else if (!type.compare("LEFTMIDDLEINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleIntermediate)] = boneID; } else if (!type.compare("RIGHTMIDDLEINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleIntermediate)] = boneID; } else if (!type.compare("LEFTMIDDLEDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleDistal)] = boneID; } else if (!type.compare("RIGHTMIDDLEDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleDistal)] = boneID; } else if (!type.compare("LEFTRINGPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftRingProximal)] = boneID; } else if (!type.compare("RIGHTRINGPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightRingProximal)] = boneID; } else if (!type.compare("LEFTRINGINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftRingIntermediate)] = boneID; } else if (!type.compare("RIGHTRINGINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightRingIntermediate)] = boneID; } else if (!type.compare("LEFTRINGDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftRingDistal)] = boneID; } else if (!type.compare("RIGHTRINGDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightRingDistal)] = boneID; } else if (!type.compare("LEFTLITTLEPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleProximal)] = boneID; } else if (!type.compare("RIGHTLITTLEPROXIMAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLittleProximal)] = boneID; } else if (!type.compare("LEFTLITTLEINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleIntermediate)] = boneID; } else if (!type.compare("RIGHTLITTLEINTERMEDIATE")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLittleIntermediate)] = boneID; } else if (!type.compare("LEFTLITTLEDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleDistal)] = boneID; } else if (!type.compare("RIGHTLITTLEDISTAL")) { component.bones[size_t(HumanoidComponent::HumanoidBone::RightLittleDistal)] = boneID; } } } } } auto ext_vrmc_springbone = state.gltfModel.extensions.find("VRMC_springBone"); if (ext_vrmc_springbone != state.gltfModel.extensions.end()) { // https://github.com/vrm-c/vrm-specification/tree/master/specification/VRMC_springBone-1.0-beta // Colliders: if (ext_vrmc_springbone->second.Has("colliders")) { const auto& colliders = ext_vrmc_springbone->second.Get("colliders"); for (size_t collider_index = 0; collider_index < colliders.ArrayLen(); ++collider_index) { const auto& collider = colliders.Get(int(collider_index)); ColliderComponent component; if (collider.Has("shape")) { const auto& shape = collider.Get("shape"); if (shape.Has("capsule")) { auto& capsule = shape.Get("capsule"); component.shape = ColliderComponent::Shape::Capsule; if (capsule.Has("offset")) { auto& value = capsule.Get("offset"); component.offset.x = float(value.Get(0).GetNumberAsDouble()); component.offset.y = float(value.Get(1).GetNumberAsDouble()); component.offset.z = float(value.Get(2).GetNumberAsDouble()); } if (capsule.Has("radius")) { auto& value = capsule.Get("radius"); component.radius = float(value.GetNumberAsDouble()); } if (capsule.Has("tail")) { auto& value = capsule.Get("tail"); component.tail.x = float(value.Get(0).GetNumberAsDouble()); component.tail.y = float(value.Get(1).GetNumberAsDouble()); component.tail.z = float(value.Get(2).GetNumberAsDouble()); } } if (shape.Has("sphere")) { auto& sphere = shape.Get("sphere"); component.shape = ColliderComponent::Shape::Sphere; if (sphere.Has("offset")) { auto& value = sphere.Get("offset"); component.offset.x = float(value.Get(0).GetNumberAsDouble()); component.offset.y = float(value.Get(1).GetNumberAsDouble()); component.offset.z = float(value.Get(2).GetNumberAsDouble()); } if (sphere.Has("radius")) { auto& value = sphere.Get("radius"); component.radius = float(value.GetNumberAsDouble()); } } } if (collider.Has("node")) { const auto& node = collider.Get("node"); int node_index = node.GetNumberAsInt(); Entity entity = state.entityMap[node_index]; Entity colliderID = CreateEntity(); state.scene->colliders.Create(colliderID) = component; state.scene->transforms.Create(colliderID); state.scene->layers.Create(colliderID); state.scene->Component_Attach(colliderID, entity, true); } } } // Springs: if (ext_vrmc_springbone->second.Has("springs")) { const auto& springs = ext_vrmc_springbone->second.Get("springs"); for (size_t spring_index = 0; spring_index < springs.ArrayLen(); ++spring_index) { const auto& spring = springs.Get(int(spring_index)); //if (spring.Has("center")) //{ // const auto& center = spring.Get("center"); //} //wi::vector colliderIDs; //if (spring.Has("colliderGroups")) //{ // // collider group references: // const auto& colliderGroups = spring.Get("colliderGroups"); // for (size_t collider_group_index = 0; collider_group_index < colliderGroups.ArrayLen(); ++collider_group_index) // { // const auto& colliderGroup = ext_vrmc_springbone->second.Get("colliderGroups").Get(int(collider_group_index)); // if (colliderGroup.Has("colliders")) // { // const auto& colliders = colliderGroup.Get("colliders"); // for (size_t collider_index = 0; collider_index < colliders.ArrayLen(); ++collider_index) // { // int collider = colliders.Get(int(collider_index)).GetNumberAsInt(); // colliderIDs.push_back(state.scene->colliders.GetEntity(collider)); // } // } // } //} if (spring.Has("joints")) { const auto& joints = spring.Get("joints"); for (size_t joint_index = 0; joint_index < joints.ArrayLen(); ++joint_index) { const auto& joint = joints.Get(int(joint_index)); SpringComponent component; //component.colliders = colliderIDs; // for now, we will just use all colliders in the scene for every spring if (joint.Has("dragForce")) { auto& value = joint.Get("dragForce"); component.dragForce = float(value.GetNumberAsDouble()); } if (joint.Has("gravityDir")) { auto& value = joint.Get("gravityDir"); component.gravityDir.x = float(value.Get(0).GetNumberAsDouble()); component.gravityDir.y = float(value.Get(1).GetNumberAsDouble()); component.gravityDir.z = float(value.Get(2).GetNumberAsDouble()); } if (joint.Has("gravityPower")) { auto& value = joint.Get("gravityPower"); component.gravityPower = float(value.GetNumberAsDouble()); } if (joint.Has("hitRadius")) { auto& value = joint.Get("hitRadius"); component.hitRadius = float(value.GetNumberAsDouble()); } if (joint.Has("stiffness")) { auto& value = joint.Get("stiffness"); component.stiffnessForce = float(value.GetNumberAsDouble()); } if (joint.Has("node")) { auto& value = joint.Get("node"); int node = value.GetNumberAsInt(); Entity entity = state.entityMap[node]; state.scene->springs.Create(entity) = component; } } } if (spring.Has("name")) { const auto& name = spring.Get("name"); } } if (ext_vrmc_springbone->second.Has("colliders")) { const auto& colliders = ext_vrmc_springbone->second.Get("colliders"); } } } } void Import_Mixamo_Bone(LoaderState& state, Entity armatureEntity, Entity boneEntity, const tinygltf::Node& node) { auto get_humanoid = [&]() -> HumanoidComponent& { HumanoidComponent* component = state.scene->humanoids.GetComponent(armatureEntity); if (component == nullptr) { component = &state.scene->humanoids.Create(armatureEntity); } return *component; }; if (!node.name.compare("mixamorig:Hips")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Hips)] = boneEntity; } else if (!node.name.compare("mixamorig:Spine")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Spine)] = boneEntity; } else if (!node.name.compare("mixamorig:Spine1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Chest)] = boneEntity; } else if (!node.name.compare("mixamorig:Spine2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::UpperChest)] = boneEntity; } else if (!node.name.compare("mixamorig:Neck")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Neck)] = boneEntity; } else if (!node.name.compare("mixamorig:Head")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Head)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftShoulder")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftShoulder)] = boneEntity; } else if (!node.name.compare("mixamorig:RightShoulder")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightShoulder)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftArm")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperArm)] = boneEntity; } else if (!node.name.compare("mixamorig:RightArm")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightUpperArm)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftForeArm")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerArm)] = boneEntity; } else if (!node.name.compare("mixamorig:RightForeArm")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLowerArm)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHand")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftHand)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHand")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightHand)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandThumb1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbMetacarpal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandThumb1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbMetacarpal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandThumb2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandThumb2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandThumb3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandThumb3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandIndex1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandIndex1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandIndex2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexIntermediate)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandIndex2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexIntermediate)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandIndex3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandIndex3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandMiddle1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandMiddle1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandMiddle2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleIntermediate)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandMiddle2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleIntermediate)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandMiddle3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandMiddle3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandRing1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandRing1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandRing2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingIntermediate)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandRing2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingIntermediate)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandRing3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandRing3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandPinky1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandPinky1")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleProximal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandPinky2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleIntermediate)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandPinky2")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleIntermediate)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftHandPinky3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:RightHandPinky3")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleDistal)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftUpLeg")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperLeg)] = boneEntity; } else if (!node.name.compare("mixamorig:RightUpLeg")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightUpperLeg)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftLeg")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerLeg)] = boneEntity; } else if (!node.name.compare("mixamorig:RightLeg")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLowerLeg)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftFoot")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftFoot)] = boneEntity; } else if (!node.name.compare("mixamorig:RightFoot")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightFoot)] = boneEntity; } else if (!node.name.compare("mixamorig:LeftToeBase")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftToes)] = boneEntity; } else if (!node.name.compare("mixamorig:RightToeBase")) { get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightToes)] = boneEntity; } }