Files
WickedEngine/Editor/ModelImporter_GLTF.cpp
T

5272 lines
189 KiB
C++

#include "stdafx.h"
#include "wiScene.h"
#include "ModelImporter.h"
#include "wiRandom.h"
#include "Utility/stb_image.h"
#include "Utility/dds.h"
#include <wiHelper.h>
#include <wiUnorderedSet.h>
#define TINYGLTF_IMPLEMENTATION
#define TINYGLTF_NO_STB_IMAGE
#define TINYGLTF_NO_STB_IMAGE_WRITE
#include "tiny_gltf.h"
#ifndef _WIN32
# include <wordexp.h>
#endif
using namespace wi::graphics;
using namespace wi::scene;
using namespace wi::ecs;
using json = nlohmann::json;
namespace wi::tinygltf
{
using 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<unsigned char>* 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<unsigned char>& contents, void*) {
return wi::helper::FileWrite(filepath, contents.data(), contents.size());
}
bool GetFileSizeInBytes(size_t* filesize_out, std::string* err,
const std::string& filepath, void* userdata)
{
*filesize_out = wi::helper::FileSize(filepath);
return true;
}
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:
image->uri = "gltfimport_" + std::to_string(wi::helper::HashByteData(bytes, size)) + ".png";
}
auto resource = wi::resourcemanager::Load(
image->uri,
wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA | wi::resourcemanager::Flags::IMPORT_DELAY,
(const uint8_t*)bytes,
(size_t)size
);
if (!resource.IsValid())
{
return false;
}
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,
const Image* image, bool embedImages, const FsCallbacks*, const URICallbacks*,
std::string* out_uri, void*)
{
assert(0); // TODO
return false;
}
}
struct LoaderState
{
std::string name;
tinygltf::Model gltfModel;
Scene* scene;
wi::unordered_map<int, Entity> entityMap; // node -> entity
std::unordered_multimap<int, Entity> punctualLightMap; // KHR_lights_punctual -> entities
Entity rootEntity = INVALID_ENTITY;
//Export states
wi::unordered_map<std::string, int> textureMap; // path -> textureid
wi::unordered_map<Entity, int> nodeMap;// entity -> node
wi::unordered_map<size_t, TransformComponent> transforms_original; // original transform states
};
void Import_Extension_VRM(LoaderState& state);
void Import_Extension_VRMC(LoaderState& state);
void VRM_ToonMaterialCustomize(const std::string& name, MaterialComponent& material);
void Import_VRMC_Bone(LoaderState& state, Entity boneEntity, const std::string& boneName);
void Import_Mixamo_Bone(LoaderState& state, Entity boneEntity, const tinygltf::Node& node);
void Import_Makehuman_Bone(LoaderState& state, Entity boneEntity, const tinygltf::Node& node);
static void ImportMetadata(LoaderState& state, Entity entity, const tinygltf::Value& extras)
{
if (extras.IsObject())
{
MetadataComponent& metadata = state.scene->metadatas.Create(entity);
for (auto& key : extras.Keys())
{
auto& value = extras.Get(key);
switch (value.Type())
{
case tinygltf::Type::BOOL_TYPE:
metadata.bool_values.set(key, value.Get<bool>());
break;
case tinygltf::Type::INT_TYPE:
metadata.int_values.set(key, value.Get<int>());
break;
case tinygltf::Type::REAL_TYPE:
metadata.float_values.set(key, float(value.Get<double>()));
break;
case tinygltf::Type::STRING_TYPE:
metadata.string_values.set(key, value.Get<std::string>());
break;
default:
json j;
tinygltf::ValueToJson(value, &j);
metadata.string_values.set(key, j.dump());
}
}
}
}
// Recursively loads nodes and resolves hierarchy:
void LoadNode(int nodeIndex, Entity parent, LoaderState& state)
{
if (nodeIndex < 0 || state.entityMap.count(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:
entity = scene.armatures.GetEntity(node.skin);
MeshComponent* mesh = &scene.meshes[node.mesh];
Entity meshEntity = scene.meshes.GetEntity(node.mesh);
assert(!mesh->vertex_boneindices.empty());
if (mesh->armatureID != INVALID_ENTITY)
{
// Reuse mesh with different skin is not possible currently, so we create a new one:
meshEntity = entity;
MeshComponent& newMesh = scene.meshes.Create(meshEntity);
newMesh = scene.meshes[node.mesh];
newMesh.CreateRenderData();
mesh = &newMesh;
}
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 = meshEntity;
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
int index = ext_lights_punctual->second.Get("light").Get<int>();
state.punctualLightMap.insert({ index, entity });
}
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())
{
// Note: limiting min scale because scale <= 0.0001 will break matrix decompose and mess up the model (float precision issue?)
for (int idx = 0; idx < 3; ++idx)
{
if (std::abs(node.scale[idx]) <= 0.0001)
{
const double sign = node.scale[idx] < 0 ? -1 : 1;
node.scale[idx] = 0.0001001 * sign;
}
}
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
}
ImportMetadata(state, entity, node.extras);
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 FlipZAxis(LoaderState& state)
{
Scene& wiscene = *state.scene;
// Flip mesh data first
for(size_t i = 0; i < wiscene.meshes.GetCount(); ++i)
{
auto& mesh = wiscene.meshes[i];
for(auto& v_pos : mesh.vertex_positions)
{
v_pos.z *= -1.f;
}
for(auto& v_norm : mesh.vertex_normals)
{
v_norm.z *= -1.f;
}
for(auto& v_tan : mesh.vertex_tangents)
{
v_tan.z *= -1.f;
}
for(auto& v_morph : mesh.morph_targets)
{
for(auto& v_morph_norm : v_morph.vertex_normals)
{
v_morph_norm.z *= -1.f;
}
for(auto& v_morph_pos : v_morph.vertex_positions)
{
v_morph_pos.z *= -1.f;
}
}
mesh.FlipCulling(); // calls CreateRenderData
}
// Flip scene's transformComponents
bool state_restore = (state.transforms_original.size() > 0);
if(!state_restore)
{
wi::unordered_map<wi::ecs::Entity, wi::ecs::Entity> hierarchy_list;
wi::unordered_map<size_t, wi::scene::TransformComponent> correction_queue;
for(size_t i = 0; i < wiscene.transforms.GetCount(); ++i)
{
auto transformEntity = wiscene.transforms.GetEntity(i);
if(transformEntity == state.rootEntity)
continue;
correction_queue[i] = wiscene.transforms[i];
auto hierarchy = wiscene.hierarchy.GetComponent(transformEntity);
if(transformEntity != state.rootEntity && hierarchy != nullptr)
{
hierarchy_list[transformEntity] = hierarchy->parentID;
wiscene.Component_Detach(transformEntity);
}
}
state.transforms_original.insert(correction_queue.begin(), correction_queue.end());
for(auto& correction_pair : correction_queue)
{
auto& transform = wiscene.transforms[correction_pair.first];
auto& transform_original = correction_pair.second;
XMVECTOR V_S,V_R,V_T;
XMMatrixDecompose(&V_S, &V_R, &V_T, XMLoadFloat4x4(&transform_original.world));
XMFLOAT3 pos, scale;
XMFLOAT4 rot;
XMStoreFloat3(&scale, V_S);
XMStoreFloat3(&pos, V_T);
XMStoreFloat4(&rot, V_R);
pos.z *= -1.f;
rot.x *= -1.f;
rot.y *= -1.f;
auto build_m =
XMMatrixScalingFromVector(XMLoadFloat3(&scale)) *
XMMatrixRotationQuaternion(XMLoadFloat4(&rot)) *
XMMatrixTranslationFromVector(XMLoadFloat3(&pos));
XMFLOAT4X4 build_m4;
XMStoreFloat4x4(&build_m4, build_m);
transform.world = build_m4;
transform.ApplyTransform();
}
for(auto& hierarchy_pair : hierarchy_list)
{
wiscene.Component_Attach(hierarchy_pair.first, hierarchy_pair.second);
}
}
else
{
for(size_t i = 0; i < wiscene.transforms.GetCount(); ++i)
{
auto transform_original_find = state.transforms_original.find(i);
if(transform_original_find != state.transforms_original.end())
{
auto& transform = wiscene.transforms[i];
transform = transform_original_find->second;
}
}
state.transforms_original.clear();
}
// Flip armature's bind pose
for(size_t i = 0; i < wiscene.armatures.GetCount(); ++i)
{
auto& armature = wiscene.armatures[i];
for(int i = 0; i < armature.inverseBindMatrices.size(); ++i)
{
auto& bind = armature.inverseBindMatrices[i];
XMVECTOR V_S,V_R,V_T;
XMMatrixDecompose(&V_S, &V_R, &V_T, XMLoadFloat4x4(&bind));
XMFLOAT3 pos, scale;
XMFLOAT4 rot;
XMStoreFloat3(&scale, V_S);
XMStoreFloat3(&pos, V_T);
XMStoreFloat4(&rot, V_R);
pos.z *= -1.f;
rot.x *= -1.f;
rot.y *= -1.f;
auto build_m =
XMMatrixScalingFromVector(XMLoadFloat3(&scale)) *
XMMatrixRotationQuaternion(XMLoadFloat4(&rot)) *
XMMatrixTranslationFromVector(XMLoadFloat3(&pos));
XMFLOAT4X4 build_m4;
XMStoreFloat4x4(&build_m4, build_m);
bind = build_m4;
}
}
// Flip animation data for translation and rotation
for(size_t i = 0; i < wiscene.animations.GetCount(); ++i)
{
auto& animation = wiscene.animations[i];
for(auto& channel : animation.channels)
{
auto data = wiscene.animation_datas.GetComponent(animation.samplers[channel.samplerIndex].data);
if(channel.path == wi::scene::AnimationComponent::AnimationChannel::Path::TRANSLATION)
{
for(size_t k = 0; k < data->keyframe_data.size()/3; ++k)
{
data->keyframe_data[k*3+2] *= -1.f;
}
}
if(channel.path == wi::scene::AnimationComponent::AnimationChannel::Path::ROTATION)
{
for(size_t k = 0; k < data->keyframe_data.size()/4; ++k)
{
data->keyframe_data[k*4] *= -1.f;
data->keyframe_data[k*4+1] *= -1.f;
}
}
}
}
}
static const wi::unordered_set<std::string> SUPPORTED_EXTENSIONS = {
"EXT_lights_image_based",
"KHR_lights_punctual",
"KHR_materials_anisotropy",
"KHR_materials_clearcoat",
"KHR_materials_emissive_strength",
"KHR_materials_ior",
"KHR_materials_pbrSpecularGlossiness",
"KHR_materials_sheen",
"KHR_materials_specular",
"KHR_materials_transmission",
"KHR_materials_unlit",
"VRM",
"VRMC_materials_mtoon",
"VRMC_springBone",
"VRMC_springBone_extended_collider",
"VRMC_vrm",
"VRMC_vrm_animation",
};
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 = wi::tinygltf::ReadWholeFile;
callbacks.WriteWholeFile = wi::tinygltf::WriteWholeFile;
callbacks.FileExists = wi::tinygltf::FileExists;
callbacks.GetFileSizeInBytes = wi::tinygltf::GetFileSizeInBytes;
callbacks.ExpandFilePath = wi::tinygltf::ExpandFilePath;
bool ret = loader.SetFsCallbacks(callbacks);
assert(ret);
wi::resourcemanager::ResourceSerializer seri; // keep this alive to not delete loaded images while importing gltf
loader.SetImageLoader(wi::tinygltf::LoadImageData, &seri);
loader.SetImageWriter(wi::tinygltf::WriteImageData, nullptr);
LoaderState state;
state.scene = &scene;
wi::vector<uint8_t> filedata;
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<const char*>(filedata.data()),
static_cast<unsigned int>(filedata.size()),
basedir
);
}
else
{
ret = loader.LoadBinaryFromMemory(
&state.gltfModel,
&err,
&warn,
filedata.data(),
static_cast<unsigned int>(filedata.size()),
basedir
);
}
}
else
{
err = "Failed to read file: " + fileName;
}
if (!ret)
{
wi::helper::messageBox(err, "glTF error!");
return;
}
for (auto& ext : state.gltfModel.extensionsRequired)
{
if (SUPPORTED_EXTENSIONS.find(ext) == SUPPORTED_EXTENSIONS.end())
{
wi::backlog::post("The glTF file " + fileName + " requires the unsupported extension " + ext + ". Trying to import it anyway, some objects might be missing!", wi::backlog::LogLevel::Warning);
}
}
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;
auto& img = state.gltfModel.images[img_source];
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;
auto& img = state.gltfModel.images[img_source];
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;
auto& img = state.gltfModel.images[img_source];
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;
auto& img = state.gltfModel.images[img_source];
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;
auto& img = state.gltfModel.images[img_source];
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_mtoon = x.extensions.find("VRMC_materials_mtoon");
if (ext_mtoon != x.extensions.end())
{
// https://github.com/vrm-c/vrm-specification/tree/master/specification/VRMC_materials_mtoon-1.0
VRM_ToonMaterialCustomize(x.name, material);
}
auto ext_emissiveStrength = x.extensions.find("KHR_materials_emissive_strength");
if (ext_emissiveStrength != x.extensions.end())
{
// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md
if (ext_emissiveStrength->second.Has("emissiveStrength"))
{
auto& factor = ext_emissiveStrength->second.Get("emissiveStrength");
material.SetEmissiveStrength(float(factor.IsNumber() ? factor.Get<double>() : factor.Get<int>()));
}
}
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<double>() : factor.Get<int>());
}
if (ext_transmission->second.Has("transmissionTexture"))
{
int index = ext_transmission->second.Get("transmissionTexture").Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::TRANSMISSIONMAP].name = img.uri;
material.textures[MaterialComponent::TRANSMISSIONMAP].uvset = (uint32_t)ext_transmission->second.Get("transmissionTexture").Get("texCoord").Get<int>();
}
}
// 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<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::BASECOLORMAP].name = img.uri;
material.textures[MaterialComponent::BASECOLORMAP].uvset = (uint32_t)specularGlossinessWorkflow->second.Get("diffuseTexture").Get("texCoord").Get<int>();
}
if (specularGlossinessWorkflow->second.Has("specularGlossinessTexture"))
{
int index = specularGlossinessWorkflow->second.Get("specularGlossinessTexture").Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::SURFACEMAP].name = img.uri;
material.textures[MaterialComponent::SURFACEMAP].uvset = (uint32_t)specularGlossinessWorkflow->second.Get("specularGlossinessTexture").Get("texCoord").Get<int>();
}
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<double>() : factor.Get(0).Get<int>()) : 1.0f;
material.baseColor.y = factor.ArrayLen() > 1 ? float(factor.Get(1).IsNumber() ? factor.Get(1).Get<double>() : factor.Get(1).Get<int>()) : 1.0f;
material.baseColor.z = factor.ArrayLen() > 2 ? float(factor.Get(2).IsNumber() ? factor.Get(2).Get<double>() : factor.Get(2).Get<int>()) : 1.0f;
material.baseColor.w = factor.ArrayLen() > 3 ? float(factor.Get(3).IsNumber() ? factor.Get(3).Get<double>() : factor.Get(3).Get<int>()) : 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<double>() : factor.Get(0).Get<int>()) : 1.0f;
material.specularColor.y = factor.ArrayLen() > 0 ? float(factor.Get(1).IsNumber() ? factor.Get(1).Get<double>() : factor.Get(1).Get<int>()) : 1.0f;
material.specularColor.z = factor.ArrayLen() > 0 ? float(factor.Get(2).IsNumber() ? factor.Get(2).Get<double>() : factor.Get(2).Get<int>()) : 1.0f;
material.specularColor.w = factor.ArrayLen() > 0 ? float(factor.Get(3).IsNumber() ? factor.Get(3).Get<double>() : factor.Get(3).Get<int>()) : 1.0f;
}
if (specularGlossinessWorkflow->second.Has("glossinessFactor"))
{
auto& factor = specularGlossinessWorkflow->second.Get("glossinessFactor");
material.roughness = 1 - float(factor.IsNumber() ? factor.Get<double>() : factor.Get<int>());
}
}
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<double>() : factor.Get(0).Get<int>()) : 1.0f;
material.sheenColor.y = factor.ArrayLen() > 0 ? float(factor.Get(1).IsNumber() ? factor.Get(1).Get<double>() : factor.Get(1).Get<int>()) : 1.0f;
material.sheenColor.z = factor.ArrayLen() > 0 ? float(factor.Get(2).IsNumber() ? factor.Get(2).Get<double>() : factor.Get(2).Get<int>()) : 1.0f;
material.sheenColor.w = factor.ArrayLen() > 0 ? float(factor.Get(3).IsNumber() ? factor.Get(3).Get<double>() : factor.Get(3).Get<int>()) : 1.0f;
}
if (ext_sheen->second.Has("sheenColorTexture"))
{
auto& param = ext_sheen->second.Get("sheenColorTexture");
int index = param.Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::SHEENCOLORMAP].name = img.uri;
material.textures[MaterialComponent::SHEENCOLORMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
if (ext_sheen->second.Has("sheenRoughnessFactor"))
{
auto& factor = ext_sheen->second.Get("sheenRoughnessFactor");
material.sheenRoughness = float(factor.IsNumber() ? factor.Get<double>() : factor.Get<int>());
}
if (ext_sheen->second.Has("sheenRoughnessTexture"))
{
auto& param = ext_sheen->second.Get("sheenRoughnessTexture");
int index = param.Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::SHEENROUGHNESSMAP].name = img.uri;
material.textures[MaterialComponent::SHEENROUGHNESSMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
}
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<double>() : factor.Get<int>());
}
if (ext_clearcoat->second.Has("clearcoatTexture"))
{
auto& param = ext_clearcoat->second.Get("clearcoatTexture");
int index = param.Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::CLEARCOATMAP].name = img.uri;
material.textures[MaterialComponent::CLEARCOATMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
if (ext_clearcoat->second.Has("clearcoatRoughnessFactor"))
{
auto& factor = ext_clearcoat->second.Get("clearcoatRoughnessFactor");
material.clearcoatRoughness = float(factor.IsNumber() ? factor.Get<double>() : factor.Get<int>());
}
if (ext_clearcoat->second.Has("clearcoatRoughnessTexture"))
{
auto& param = ext_clearcoat->second.Get("clearcoatRoughnessTexture");
int index = param.Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::CLEARCOATROUGHNESSMAP].name = img.uri;
material.textures[MaterialComponent::CLEARCOATROUGHNESSMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
if (ext_clearcoat->second.Has("clearcoatNormalTexture"))
{
auto& param = ext_clearcoat->second.Get("clearcoatNormalTexture");
int index = param.Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::CLEARCOATNORMALMAP].name = img.uri;
material.textures[MaterialComponent::CLEARCOATNORMALMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
}
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<double>() : factor.Get<int>());
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<double>() : factor.Get<int>());
}
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<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::SURFACEMAP].name = img.uri;
material.textures[MaterialComponent::SURFACEMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
else if (!material.textures[MaterialComponent::SPECULARMAP].resource.IsValid())
{
auto& param = ext_specular->second.Get("specularTexture");
int index = param.Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::SPECULARMAP].name = img.uri;
material.textures[MaterialComponent::SPECULARMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
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<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::SPECULARMAP].name = img.uri;
material.textures[MaterialComponent::SPECULARMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
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<double>() : factor.Get(0).Get<int>()) : 1.0f;
material.specularColor.y = factor.ArrayLen() > 0 ? float(factor.Get(1).IsNumber() ? factor.Get(1).Get<double>() : factor.Get(1).Get<int>()) : 1.0f;
material.specularColor.z = factor.ArrayLen() > 0 ? float(factor.Get(2).IsNumber() ? factor.Get(2).Get<double>() : factor.Get(2).Get<int>()) : 1.0f;
}
}
auto ext_aniso = x.extensions.find("KHR_materials_anisotropy");
if (ext_aniso != x.extensions.end())
{
// https://github.com/ux3d/glTF/tree/extensions/KHR_materials_anisotropy/extensions/2.0/Khronos/KHR_materials_anisotropy
material.shaderType = MaterialComponent::SHADERTYPE_PBR_ANISOTROPIC;
if (ext_aniso->second.Has("anisotropyStrength"))
{
auto& factor = ext_aniso->second.Get("anisotropyStrength");
material.anisotropy_strength = float(factor.IsNumber() ? factor.Get<double>() : factor.Get<int>());
}
if (ext_aniso->second.Has("anisotropyRotation"))
{
auto& factor = ext_aniso->second.Get("anisotropyRotation");
material.anisotropy_rotation = float(factor.IsNumber() ? factor.Get<double>() : factor.Get<int>());
}
if (ext_aniso->second.Has("anisotropyTexture"))
{
auto& param = ext_aniso->second.Get("anisotropyTexture");
int index = param.Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::ANISOTROPYMAP].name = img.uri;
material.textures[MaterialComponent::ANISOTROPYMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
// Differently from the proposed spec, in the proposed sample model, I see different namings: https://github.com/KhronosGroup/glTF-Sample-Models/tree/Anisotropy-Barn-Lamp/2.0/AnisotropyBarnLamp
if (ext_aniso->second.Has("anisotropy"))
{
auto& factor = ext_aniso->second.Get("anisotropy");
material.anisotropy_strength = float(factor.IsNumber() ? factor.Get<double>() : factor.Get<int>());
}
if (ext_aniso->second.Has("anisotropyDirection"))
{
auto& factor = ext_aniso->second.Get("anisotropyDirection");
material.anisotropy_rotation = float(factor.IsNumber() ? factor.Get<double>() : factor.Get<int>());
}
if (ext_aniso->second.Has("anisotropyDirectionTexture"))
{
auto& param = ext_aniso->second.Get("anisotropyDirectionTexture");
int index = param.Get("index").Get<int>();
auto& tex = state.gltfModel.textures[index];
int img_source = tex.source;
auto& img = state.gltfModel.images[img_source];
material.textures[MaterialComponent::ANISOTROPYMAP].name = img.uri;
material.textures[MaterialComponent::ANISOTROPYMAP].uvset = (uint32_t)param.Get("texCoord").Get<int>();
}
}
ImportMetadata(state, materialEntity, x.extras);
material.CreateRenderData();
}
// 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)
{
mesh.subsets.push_back(MeshComponent::MeshSubset());
if (scene.materials.GetCount() == 0)
{
// Create a material last minute if there was none
scene.materials.Create(CreateEntity());
}
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 size_t index_remap[] = {
0,2,1
};
if (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 = AlignTo(accessor.count, size_t(3)); // there was a model with invalid index count, this is a safety fix for it
size_t indexOffset = mesh.indices.size();
mesh.indices.resize(indexOffset + indexCount);
mesh.subsets.back().indexOffset = (uint32_t)indexOffset;
mesh.subsets.back().indexCount = (uint32_t)indexCount;
const uint8_t* data = buffer.data.data() + accessor.byteOffset + bufferView.byteOffset;
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;
if (mesh.subsets.back().indexCount == 0)
{
// Autogen indices:
// Note: this is not common, so it is simpler to create a dummy index buffer here than rewrite engine to support this case
size_t indexOffset = mesh.indices.size();
mesh.indices.resize(indexOffset + vertexCount);
for (size_t vi = 0; vi < vertexCount; vi += 3)
{
mesh.indices[indexOffset + vi + 0] = uint32_t(vertexOffset + vi + index_remap[0]);
mesh.indices[indexOffset + vi + 1] = uint32_t(vertexOffset + vi + index_remap[1]);
mesh.indices[indexOffset + vi + 2] = uint32_t(vertexOffset + vi + index_remap[2]);
}
mesh.subsets.back().indexOffset = (uint32_t)indexOffset;
mesh.subsets.back().indexCount = (uint32_t)vertexCount;
}
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] = *(const XMFLOAT3*)(data + i * stride);
}
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] = *(const XMFLOAT3*)(data + i * stride);
}
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] = *(const XMFLOAT4*)(data + i * stride);
}
}
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 = *(const XMFLOAT2*)((size_t)data + i * stride);
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 = *(const 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 = *(const JointTmp*)(data + i * stride);
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 = *(const JointTmp*)(data + i * stride);
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 = *(const JointTmp*)(data + i * stride);
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;
morph_target.vertex_positions.resize(sparse.count);
morph_target.sparse_indices_positions.resize(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_positions[s] = vertexOffset + sparse_indices_data[s];
morph_target.vertex_positions[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_positions[s] = vertexOffset + ((const uint16_t*)sparse_indices_data)[s];
morph_target.vertex_positions[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_positions[s] = vertexOffset + ((const uint32_t*)sparse_indices_data)[s];
morph_target.vertex_positions[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;
morph_target.vertex_normals.resize(sparse.count);
morph_target.sparse_indices_normals.resize(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_normals[s] = vertexOffset + sparse_indices_data[s];
morph_target.vertex_normals[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_normals[s] = vertexOffset + ((const uint16_t*)sparse_indices_data)[s];
morph_target.vertex_normals[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_normals[s] = vertexOffset + ((const uint32_t*)sparse_indices_data)[s];
morph_target.vertex_normals[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<float_t>(x.weights[i]);
}
if (mesh.vertex_normals.empty())
{
mesh.vertex_normals.resize(mesh.vertex_positions.size());
mesh.ComputeNormals(MeshComponent::COMPUTE_NORMALS_SMOOTH_FAST);
}
ImportMetadata(state, meshEntity, x.extras);
mesh.CreateRenderData(); // tangents are generated inside if needed, which must be done before FlipZAxis!
}
// 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);
}
ImportMetadata(state, armatureEntity, skin.extras);
}
// 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, boneEntity, state.gltfModel.nodes[jointIndex]);
Import_Makehuman_Bone(state, boneEntity, state.gltfModel.nodes[jointIndex]);
}
}
// Invalid humanoids will be removed, this is to not have humanoid if naming convention matched by mistake when trying to import humanoid bone:
for (size_t i = 0; i < scene.humanoids.GetCount();)
{
if (!scene.humanoids[i].IsValid())
{
scene.humanoids.Remove(scene.humanoids.GetEntity(i));
}
else
{
i++;
}
}
// 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, entity);
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;
}
}
ImportMetadata(state, entity, anim.extras);
}
// Create lights:
int lightIndex = 0;
for (auto& x : state.gltfModel.lights)
{
for (auto [it, end] = state.punctualLightMap.equal_range(lightIndex); it != end; it++)
{
Entity entity = it->second;
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<float>::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));
ImportMetadata(state, entity, x.extras);
}
lightIndex++;
}
int cameraIndex = 0;
for (auto& x : state.gltfModel.cameras)
{
if (!x.type.compare("orthographic"))
continue;
Entity entity = scene.cameras.GetEntity(cameraIndex);
CameraComponent& camera = scene.cameras[cameraIndex++];
if (x.perspective.aspectRatio > 0)
{
camera.width = float(x.perspective.aspectRatio);
camera.height = 1.f;
}
camera.fov = (float)x.perspective.yfov;
camera.zFarP = (float)x.perspective.zfar;
camera.zNearP = (float)x.perspective.znear;
ImportMetadata(state, entity, x.extras);
}
// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_lights_image_based/README.md
auto env = state.gltfModel.extensions.find("EXT_lights_image_based");
if (env != state.gltfModel.extensions.end())
{
int counter = 0;
auto lights = env->second.Get("lights");
for (int i = 0; i < (int)lights.ArrayLen(); ++i)
{
if (scene.weathers.GetCount() == 0)
{
Entity entity = CreateEntity();
scene.weathers.Create(entity);
scene.names.Create(entity) = "weather";
}
WeatherComponent& weather = scene.weathers[0];
auto light = lights.Get(i);
if (light.Has("intensity"))
{
auto value = light.Get("intensity");
weather.skyExposure = (float)value.GetNumberAsDouble();
}
if (light.Has("rotation"))
{
auto value = light.Get("rotation");
XMFLOAT4 quaternion = {};
quaternion.x = value.ArrayLen() > 0 ? float(value.Get(0).IsNumber() ? value.Get(0).Get<double>() : value.Get(0).Get<int>()) : 0.0f;
quaternion.y = value.ArrayLen() > 1 ? float(value.Get(1).IsNumber() ? value.Get(1).Get<double>() : value.Get(1).Get<int>()) : 0.0f;
quaternion.z = value.ArrayLen() > 2 ? float(value.Get(2).IsNumber() ? value.Get(2).Get<double>() : value.Get(2).Get<int>()) : 0.0f;
quaternion.w = value.ArrayLen() > 3 ? float(value.Get(3).IsNumber() ? value.Get(3).Get<double>() : value.Get(3).Get<int>()) : 1.0f;
XMVECTOR Q = XMLoadFloat4(&quaternion);
float angle;
XMVECTOR axis;
XMQuaternionToAxisAngle(&axis, &angle, Q);
weather.sky_rotation = XM_2PI - angle;
}
//if (light.Has("irradianceCoefficients"))
//{
// auto value = light.Get("irradianceCoefficients");
// float spherical_harmonics[9][3] = {};
// for (int c = 0; c < std::min(9, (int)value.ArrayLen()); ++c)
// {
// for (int f = 0; f < std::min(3, (int)value.Get(c).ArrayLen()); ++f)
// {
// spherical_harmonics[c][f] = (float)value.Get(c).Get(f).GetNumberAsDouble();
// }
// }
//}
if (light.Has("specularImages"))
{
auto mips = light.Get("specularImages");
int mip_count = (int)mips.ArrayLen();
TextureDesc desc;
desc.format = Format::R9G9B9E5_SHAREDEXP;
desc.bind_flags = BindFlag::SHADER_RESOURCE;
if (light.Has("specularImageSize"))
{
auto value = light.Get("specularImageSize");
desc.width = desc.height = (uint32_t)value.GetNumberAsInt();
}
desc.array_size = 6;
desc.mip_levels = (uint32_t)mip_count;
desc.misc_flags = ResourceMiscFlag::TEXTURECUBE;
wi::vector<wi::vector<XMFLOAT3SE>> hdr_datas(mip_count * 6);
for (int m = 0; m < mip_count; ++m)
{
auto mip = mips.Get(m);
int face_count = (int)mip.ArrayLen();
for (int f = 0; f < face_count; ++f)
{
auto index = mip.Get(f).GetNumberAsInt();
auto& image = state.gltfModel.images[index];
int idx = f * mip_count + m;
wi::Resource res = wi::resourcemanager::Load(image.uri, wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA);
auto& imagefiledata = res.GetFileData();
const stbi_uc* filedata = imagefiledata.data();
size_t filesize = imagefiledata.size();
int width, height, bpp;
wi::Color* rgba = (wi::Color*)stbi_load_from_memory(filedata, (int)filesize, &width, &height, &bpp, 4);
if (rgba == nullptr)
continue;
wi::vector<XMFLOAT3SE>& hdr_data = hdr_datas[idx];
hdr_data.resize(width * height);
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
int y_flip = height - 1 - y;
wi::Color color = rgba[x + y_flip * width];
XMFLOAT4 unpk = color.toFloat4();
// Remove SRGB curve:
unpk.x = std::pow(unpk.x, 2.2f);
unpk.y = std::pow(unpk.y, 2.2f);
unpk.z = std::pow(unpk.z, 2.2f);
if (bpp == 4) // if has alpha channel, then it is assumed to have RGBD encoding
{
// RGBD conversion: https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_lights_image_based/README.md#rgbd
unpk.x /= unpk.w;
unpk.y /= unpk.w;
unpk.z /= unpk.w;
}
hdr_data[x + y * width] = XMFLOAT3SE(unpk.x, unpk.y, unpk.z);
}
}
stbi_image_free(rgba);
}
}
size_t wholeDataSize = 0;
for (auto& x : hdr_datas)
{
wholeDataSize += x.size() * sizeof(XMFLOAT3SE);
}
wi::vector<uint8_t> dds;
dds.resize(sizeof(dds::Header) + wholeDataSize);
dds::write_header(
dds.data(),
dds::DXGI_FORMAT_R9G9B9E5_SHAREDEXP,
desc.width,
desc.height,
desc.mip_levels,
desc.array_size,
true
);
size_t offset = sizeof(dds::Header);
for (auto& x : hdr_datas)
{
std::memcpy(dds.data() + offset, x.data(), x.size() * sizeof(XMFLOAT3SE));
offset += x.size() * sizeof(XMFLOAT3SE);
}
weather.skyMapName = wi::helper::RemoveExtension(wi::helper::GetFileNameFromPath(fileName)) + "/EXT_lights_image_based_" + std::to_string(counter++) + ".dds";
weather.skyMap = wi::resourcemanager::Load(
weather.skyMapName,
wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA,
dds.data(),
dds.size()
);
weather.ambient = {}; // remove ambient if gltf has env lighting
}
}
}
Import_Extension_VRM(state);
Import_Extension_VRMC(state);
//Correct orientation after importing
scene.Update(0);
FlipZAxis(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);
// after scene update, clean up duplicate colliders that could have been loaded by some extension
scene.DeleteDuplicateColliders();
}
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<std::string>();
}
if (blendShapeGroup.Has("presetName"))
{
const auto& value = blendShapeGroup.Get("presetName");
std::string presetName = wi::helper::toUpper(value.Get<std::string>());
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;
}
else if (!presetName.compare("SURPRISED"))
{
expression.preset = ExpressionComponent::Preset::Surprised;
}
else if (!wi::helper::toUpper(expression.name).compare("SURPRISED")) // wtf vroid
{
expression.preset = ExpressionComponent::Preset::Surprised;
}
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<bool>());
}
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<int> 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);
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& boneName = value.Get<std::string>();
Import_VRMC_Bone(state, boneID, boneName);
}
}
}
}
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));
if (material.Has("name"))
{
const auto& value = material.Get("name");
const std::string& materialName = value.Get<std::string>();
Entity entity = state.scene->Entity_FindByName(materialName);
MaterialComponent* component = state.scene->materials.GetComponent(entity);
if (component != nullptr)
{
if (material.Has("shader"))
{
const auto& value = material.Get("shader");
const std::string& shaderName = wi::helper::toUpper(value.Get<std::string>());
if (!shaderName.compare("VRM/MTOON"))
{
VRM_ToonMaterialCustomize(materialName, *component);
}
}
}
}
}
}
}
}
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/tree/master/specification/VRMC_vrm-1.0#vrmc_vrmexpressions-facial-expressions-optional
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<bool>());
}
if (vrm_expression.Has("overrideMouth"))
{
const auto& value = vrm_expression.Get("overrideMouth");
const std::string& override_enum = value.Get<std::string>();
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/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<Entity> 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");
}
}
}
auto ext_vrmc_springbone_extended_collider = state.gltfModel.extensions.find("VRMC_springBone_extended_collider");
if (ext_vrmc_springbone_extended_collider != state.gltfModel.extensions.end())
{
wi::backlog::post("VRMC_springBone_extended_collider extension found in model, but it is not implemented yet in the importer.", wi::backlog::LogLevel::Warning);
}
auto ext_vrma = state.gltfModel.extensions.find("VRMC_vrm_animation");
if (ext_vrma != state.gltfModel.extensions.end())
{
if (ext_vrma->second.Has("specVersion"))
{
std::string spec = ext_vrma->second.Get("specVersion").Get<std::string>();
if (spec.compare("1.0") != 0)
{
wilog_warning("VRMC_vrm_animation.specVersion is not 1.0!");
}
}
if (ext_vrma->second.Has("humanoid"))
{
auto humanoid = ext_vrma->second.Get("humanoid");
if (humanoid.Has("humanBones"))
{
auto humanBones = humanoid.Get("humanBones");
for (auto& key : humanBones.Keys())
{
const auto& bone = humanBones.Get(key);
if (bone.Has("node"))
{
auto node = bone.Get("node");
int idx = node.GetNumberAsInt();
if (state.entityMap.count(idx))
{
Entity boneEntity = state.entityMap[idx];
Import_VRMC_Bone(state, boneEntity, key);
}
}
}
}
else
{
wilog_warning("VRMC_vrm_animation.humanoid doesn't contain humanBones!");
}
}
if (ext_vrma->second.Has("expressions"))
{
wilog_warning("VRMC_vrm_animation.expressions not implemented yet! Please provide a sample model which contains this data in Github issue: https://github.com/turanszkij/WickedEngine/issues/new/");
}
if (ext_vrma->second.Has("lookAt"))
{
wilog_warning("VRMC_vrm_animation.lookAt not implemented yet! Please provide a sample model which contains this data in Github issue: https://github.com/turanszkij/WickedEngine/issues/new/");
}
}
}
void VRM_ToonMaterialCustomize(const std::string& name, MaterialComponent& material)
{
material.shaderType = MaterialComponent::SHADERTYPE_CARTOON;
// These customizations are just made for Wicked Engine, but not standardized:
if (name.find("SKIN") != std::string::npos)
{
// For skin, apply some subsurface scattering to look softer:
material.SetSubsurfaceScatteringAmount(0.5f);
material.SetSubsurfaceScatteringColor(XMFLOAT3(255.0f / 255.0f, 148.0f / 255.0f, 142.0f / 255.0f));
}
if (name.find("EYE") != std::string::npos ||
name.find("FaceEyeline") != std::string::npos ||
name.find("FaceEyelash") != std::string::npos ||
name.find("FaceBrow") != std::string::npos
)
{
// Disable shadow casting for some elements on the face like eyes, eyebrows, etc:
material.SetCastShadow(false);
}
}
void Import_VRMC_Bone(LoaderState& state, Entity boneID, const std::string& boneName)
{
auto get_humanoid = [&]() -> HumanoidComponent& {
HumanoidComponent* component = state.scene->humanoids.GetComponent(state.rootEntity);
if (component == nullptr)
{
component = &state.scene->humanoids.Create(state.rootEntity);
}
return *component;
};
std::string type = wi::helper::toUpper(boneName);
if (!type.compare("NECK"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Neck)] = boneID;
}
else if (!type.compare("HEAD"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Head)] = boneID;
}
else if (!type.compare("LEFTEYE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftEye)] = boneID;
}
else if (!type.compare("RIGHTEYE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightEye)] = boneID;
}
else if (!type.compare("JAW"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Jaw)] = boneID;
}
else if (!type.compare("HIPS"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Hips)] = boneID;
}
else if (!type.compare("SPINE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Spine)] = boneID;
}
else if (!type.compare("CHEST"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Chest)] = boneID;
}
else if (!type.compare("UPPERCHEST"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::UpperChest)] = boneID;
}
else if (!type.compare("LEFTSHOULDER"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftShoulder)] = boneID;
}
else if (!type.compare("RIGHTSHOULDER"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightShoulder)] = boneID;
}
else if (!type.compare("LEFTUPPERARM"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperArm)] = boneID;
}
else if (!type.compare("RIGHTUPPERARM"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightUpperArm)] = boneID;
}
else if (!type.compare("LEFTLOWERARM"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerArm)] = boneID;
}
else if (!type.compare("RIGHTLOWERARM"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLowerArm)] = boneID;
}
else if (!type.compare("LEFTHAND"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftHand)] = boneID;
}
else if (!type.compare("RIGHTHAND"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightHand)] = boneID;
}
else if (!type.compare("LEFTUPPERLEG"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperLeg)] = boneID;
}
else if (!type.compare("RIGHTUPPERLEG"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightUpperLeg)] = boneID;
}
else if (!type.compare("LEFTLOWERLEG"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerLeg)] = boneID;
}
else if (!type.compare("RIGHTLOWERLEG"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLowerLeg)] = boneID;
}
else if (!type.compare("LEFTFOOT"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftFoot)] = boneID;
}
else if (!type.compare("RIGHTFOOT"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightFoot)] = boneID;
}
else if (!type.compare("LEFTTOES"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftToes)] = boneID;
}
else if (!type.compare("RIGHTTOES"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightToes)] = boneID;
}
else if (!type.compare("LEFTTHUMBPROXIMAL"))
{
// VRM 0.0 thumb proximal = VRM 1.0 thumb metacarpal
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbMetacarpal)] = boneID;
}
else if (!type.compare("RIGHTTHUMBPROXIMAL"))
{
// VRM 0.0 thumb proximal = VRM 1.0 thumb metacarpal
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbMetacarpal)] = boneID;
}
else if (!type.compare("LEFTTHUMBINTERMEDIATE"))
{
// VRM 0.0 thumb intermediate = VRM 1.0 thumb proximal
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbProximal)] = boneID;
}
else if (!type.compare("RIGHTTHUMBINTERMEDIATE"))
{
// VRM 0.0 thumb intermediate = VRM 1.0 thumb proximal
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbProximal)] = boneID;
}
else if (!type.compare("LEFTTHUMBDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbDistal)] = boneID;
}
else if (!type.compare("RIGHTTHUMBDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbDistal)] = boneID;
}
else if (!type.compare("LEFTINDEXPROXIMAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexProximal)] = boneID;
}
else if (!type.compare("RIGHTINDEXPROXIMAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexProximal)] = boneID;
}
else if (!type.compare("LEFTINDEXINTERMEDIATE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexIntermediate)] = boneID;
}
else if (!type.compare("RIGHTINDEXINTERMEDIATE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexIntermediate)] = boneID;
}
else if (!type.compare("LEFTINDEXDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexDistal)] = boneID;
}
else if (!type.compare("RIGHTINDEXDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexDistal)] = boneID;
}
else if (!type.compare("LEFTMIDDLEPROXIMAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleProximal)] = boneID;
}
else if (!type.compare("RIGHTMIDDLEPROXIMAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleProximal)] = boneID;
}
else if (!type.compare("LEFTMIDDLEINTERMEDIATE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleIntermediate)] = boneID;
}
else if (!type.compare("RIGHTMIDDLEINTERMEDIATE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleIntermediate)] = boneID;
}
else if (!type.compare("LEFTMIDDLEDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleDistal)] = boneID;
}
else if (!type.compare("RIGHTMIDDLEDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleDistal)] = boneID;
}
else if (!type.compare("LEFTRINGPROXIMAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingProximal)] = boneID;
}
else if (!type.compare("RIGHTRINGPROXIMAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingProximal)] = boneID;
}
else if (!type.compare("LEFTRINGINTERMEDIATE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingIntermediate)] = boneID;
}
else if (!type.compare("RIGHTRINGINTERMEDIATE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingIntermediate)] = boneID;
}
else if (!type.compare("LEFTRINGDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingDistal)] = boneID;
}
else if (!type.compare("RIGHTRINGDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingDistal)] = boneID;
}
else if (!type.compare("LEFTLITTLEPROXIMAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleProximal)] = boneID;
}
else if (!type.compare("RIGHTLITTLEPROXIMAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleProximal)] = boneID;
}
else if (!type.compare("LEFTLITTLEINTERMEDIATE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleIntermediate)] = boneID;
}
else if (!type.compare("RIGHTLITTLEINTERMEDIATE"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleIntermediate)] = boneID;
}
else if (!type.compare("LEFTLITTLEDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleDistal)] = boneID;
}
else if (!type.compare("RIGHTLITTLEDISTAL"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleDistal)] = boneID;
}
}
void Import_Mixamo_Bone(LoaderState& state, Entity boneEntity, const tinygltf::Node& node)
{
auto get_humanoid = [&]() -> HumanoidComponent& {
HumanoidComponent* component = state.scene->humanoids.GetComponent(state.rootEntity);
if (component == nullptr)
{
component = &state.scene->humanoids.Create(state.rootEntity);
}
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;
}
}
void Import_Makehuman_Bone(LoaderState& state, Entity boneEntity, const tinygltf::Node& node)
{
auto get_humanoid = [&]() -> HumanoidComponent& {
HumanoidComponent* component = state.scene->humanoids.GetComponent(state.rootEntity);
if (component == nullptr)
{
component = &state.scene->humanoids.Create(state.rootEntity);
}
return *component;
};
if (!node.name.compare("pelvis"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Hips)] = boneEntity;
}
else if (!node.name.compare("spine_01"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Spine)] = boneEntity;
}
else if (!node.name.compare("spine_02"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Chest)] = boneEntity;
}
else if (!node.name.compare("spine_03"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::UpperChest)] = boneEntity;
}
else if (!node.name.compare("neck_01"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Neck)] = boneEntity;
}
else if (!node.name.compare("head"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::Head)] = boneEntity;
}
else if (!node.name.compare("clavicle_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftShoulder)] = boneEntity;
}
else if (!node.name.compare("clavicle_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightShoulder)] = boneEntity;
}
else if (!node.name.compare("upperarm_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperArm)] = boneEntity;
}
else if (!node.name.compare("upperarm_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightUpperArm)] = boneEntity;
}
else if (!node.name.compare("lowerarm_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerArm)] = boneEntity;
}
else if (!node.name.compare("lowerarm_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLowerArm)] = boneEntity;
}
else if (!node.name.compare("hand_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftHand)] = boneEntity;
}
else if (!node.name.compare("hand_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightHand)] = boneEntity;
}
else if (!node.name.compare("thumb_01_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbMetacarpal)] = boneEntity;
}
else if (!node.name.compare("thumb_01_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbMetacarpal)] = boneEntity;
}
else if (!node.name.compare("thumb_02_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbProximal)] = boneEntity;
}
else if (!node.name.compare("thumb_02_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbProximal)] = boneEntity;
}
else if (!node.name.compare("thumb_03_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftThumbDistal)] = boneEntity;
}
else if (!node.name.compare("thumb_03_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightThumbDistal)] = boneEntity;
}
else if (!node.name.compare("index_01_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexProximal)] = boneEntity;
}
else if (!node.name.compare("index_01_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexProximal)] = boneEntity;
}
else if (!node.name.compare("index_02_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexIntermediate)] = boneEntity;
}
else if (!node.name.compare("index_02_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexIntermediate)] = boneEntity;
}
else if (!node.name.compare("index_03_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftIndexDistal)] = boneEntity;
}
else if (!node.name.compare("index_03_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightIndexDistal)] = boneEntity;
}
else if (!node.name.compare("middle_01_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleProximal)] = boneEntity;
}
else if (!node.name.compare("middle_01_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleProximal)] = boneEntity;
}
else if (!node.name.compare("middle_02_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleIntermediate)] = boneEntity;
}
else if (!node.name.compare("middle_02_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleIntermediate)] = boneEntity;
}
else if (!node.name.compare("middle_03_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftMiddleDistal)] = boneEntity;
}
else if (!node.name.compare("middle_03_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightMiddleDistal)] = boneEntity;
}
else if (!node.name.compare("ring_01_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingProximal)] = boneEntity;
}
else if (!node.name.compare("ring_01_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingProximal)] = boneEntity;
}
else if (!node.name.compare("ring_02_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingIntermediate)] = boneEntity;
}
else if (!node.name.compare("ring_02_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingIntermediate)] = boneEntity;
}
else if (!node.name.compare("ring_03_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftRingDistal)] = boneEntity;
}
else if (!node.name.compare("ring_03_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightRingDistal)] = boneEntity;
}
else if (!node.name.compare("pinky_01_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleProximal)] = boneEntity;
}
else if (!node.name.compare("pinky_01_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleProximal)] = boneEntity;
}
else if (!node.name.compare("pinky_02_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleIntermediate)] = boneEntity;
}
else if (!node.name.compare("pinky_02_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleIntermediate)] = boneEntity;
}
else if (!node.name.compare("pinky_03_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLittleDistal)] = boneEntity;
}
else if (!node.name.compare("pinky_03_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLittleDistal)] = boneEntity;
}
else if (!node.name.compare("thigh_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftUpperLeg)] = boneEntity;
}
else if (!node.name.compare("thigh_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightUpperLeg)] = boneEntity;
}
else if (!node.name.compare("calf_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftLowerLeg)] = boneEntity;
}
else if (!node.name.compare("calf_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightLowerLeg)] = boneEntity;
}
else if (!node.name.compare("foot_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftFoot)] = boneEntity;
}
else if (!node.name.compare("foot_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightFoot)] = boneEntity;
}
else if (!node.name.compare("ball_l"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::LeftToes)] = boneEntity;
}
else if (!node.name.compare("ball_r"))
{
get_humanoid().bones[size_t(HumanoidComponent::HumanoidBone::RightToes)] = boneEntity;
}
}
template<typename T>
inline void _ExportHelper_valuetobuf(const T& input, tinygltf::Buffer& buffer_builder, size_t& buf_i)
{
const size_t _right = buf_i + sizeof(input);
if (_right > buffer_builder.data.size())
{
buffer_builder.data.resize(_right);
}
*(T*)(buffer_builder.data.data() + buf_i) = input;
buf_i = _right;
}
inline tinygltf::Value _ExportHelper_tovalue(float input)
{
return tinygltf::Value(input);
}
inline tinygltf::Value _ExportHelper_tovalue(XMFLOAT3 input)
{
auto value_builder = tinygltf::Value(tinygltf::Value::Array({
tinygltf::Value(input.x), tinygltf::Value(input.y), tinygltf::Value(input.z)
}));
return value_builder;
}
inline tinygltf::Value _ExportHelper_tovalue(XMFLOAT4 input)
{
auto value_builder = tinygltf::Value(tinygltf::Value::Array({
tinygltf::Value(input.x), tinygltf::Value(input.y), tinygltf::Value(input.z), tinygltf::Value(input.w)
}));
return value_builder;
}
wi::vector<std::string> original_texture_extension_iterator =
{
"png",
"jpg",
"jpeg",
};
inline std::string _ExportHelper_GetOriginalTexture(std::string texture_file)
{
for(auto& ext : original_texture_extension_iterator)
{
std::string target_file = wi::helper::ReplaceExtension(texture_file, ext);
wi::backlog::post(target_file);
if (wi::helper::FileExists(target_file))
{
return target_file;
}
}
return texture_file;
}
inline tinygltf::TextureInfo _ExportHelper_StoreMaterialTexture(LoaderState& state, const std::string& gltf_dir, const MaterialComponent& material, MaterialComponent::TEXTURESLOT slot)
{
const MaterialComponent::TextureMap& textureSlot = material.textures[slot];
std::string texture_file = textureSlot.name;
tinygltf::TextureInfo textureinfo_builder;
int texture_index = -1;
auto find_texture_id = state.textureMap.find(texture_file);
if(find_texture_id == state.textureMap.end())
{
std::string mime_type;
std::string src_extension = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(texture_file));
if (src_extension == "PNG")
{
mime_type = "image/png";
}
else if (src_extension == "JPG" || src_extension == "JPEG")
{
mime_type = "image/jpeg";
}
else if (src_extension == "BMP")
{
mime_type = "image/bmp";
}
else if (src_extension == "GIF")
{
mime_type = "image/gif";
}
int image_bufferView_index = 0;
tinygltf::Buffer buffer_builder;
int buffer_index = (int)state.gltfModel.buffers.size();
wi::vector<uint8_t> texturedata;
size_t buffer_size = 0;
if (!textureSlot.resource.GetFileData().empty() && !mime_type.empty())
{
// If texture file data is available and gltf compatible, simply save it to the gltf as-is:
buffer_builder.data = textureSlot.resource.GetFileData();
buffer_size = buffer_builder.data.size();
}
else
{
// If the texture file data is not available or it is block compressed, download it (and optionally decompress) from GPU:
Texture tex = textureSlot.resource.GetTexture();
if (IsFormatBlockCompressed(tex.desc.format))
{
// Decompress block compressed texture on GPU:
const XMFLOAT4 texMulAdd = textureSlot.uvset == 0 ? material.texMulAdd : XMFLOAT4(1, 1, 0, 0);
TextureDesc desc;
desc.format = Format::R8G8B8A8_UNORM;
desc.bind_flags = BindFlag::UNORDERED_ACCESS;
desc.width = uint32_t(tex.desc.width * texMulAdd.x);
desc.height = uint32_t(tex.desc.height * texMulAdd.y);
desc.mip_levels = 1;
Texture tex_decompressed;
GraphicsDevice* device = GetDevice();
device->CreateTexture(&desc, nullptr, &tex_decompressed);
CommandList cmd = device->BeginCommandList();
device->CreateSubresource(&tex_decompressed, SubresourceType::UAV, 0, 1, 0, 1);
wi::renderer::CopyTexture2D(
tex_decompressed, 0, 0, 0,
tex, 0, int(texMulAdd.z * tex.desc.width), int(texMulAdd.w * tex.desc.height),
cmd,
wi::renderer::BORDEREXPAND_DISABLE,
IsFormatSRGB(tex.desc.format)
); // copy that supports format conversion / decompression
tex = tex_decompressed;
}
if (wi::helper::saveTextureToMemory(tex, texturedata))
{
wi::vector<uint8_t> filedata;
if (wi::helper::saveTextureToMemoryFile(texturedata, tex.desc, "PNG", filedata))
{
buffer_size = filedata.size();
buffer_builder.data = std::move(filedata);
mime_type = "image/png";
}
}
}
state.gltfModel.buffers.push_back(buffer_builder);
tinygltf::BufferView bufferView_builder;
image_bufferView_index = (int)state.gltfModel.bufferViews.size();
bufferView_builder.buffer = buffer_index;
bufferView_builder.byteLength = buffer_size;
state.gltfModel.bufferViews.push_back(bufferView_builder);
tinygltf::Image image_builder;
//image_builder.uri = texture_file;
image_builder.bufferView = image_bufferView_index;
image_builder.mimeType = mime_type;
int image_index = (int)state.gltfModel.images.size();
wi::helper::MakePathRelative(gltf_dir, texture_file);
state.gltfModel.images.push_back(image_builder);
tinygltf::Texture texture_builder;
texture_index = (int)state.gltfModel.textures.size();
texture_builder.source = image_index;
state.gltfModel.textures.push_back(texture_builder);
state.textureMap[texture_file] = texture_index;
}
else
{
texture_index = find_texture_id->second;
}
textureinfo_builder.index = texture_index;
textureinfo_builder.texCoord = (int)textureSlot.uvset;
return textureinfo_builder;
}
void ExportModel_GLTF(const std::string& filename, Scene& scene)
{
tinygltf::TinyGLTF writer;
tinygltf::FsCallbacks callbacks;
callbacks.ReadWholeFile = wi::tinygltf::ReadWholeFile;
callbacks.WriteWholeFile = wi::tinygltf::WriteWholeFile;
callbacks.FileExists = wi::tinygltf::FileExists;
callbacks.ExpandFilePath = wi::tinygltf::ExpandFilePath;
callbacks.GetFileSizeInBytes = wi::tinygltf::GetFileSizeInBytes;
bool res = writer.SetFsCallbacks(callbacks);
assert(res);
LoaderState state;
state.scene = &scene;
auto& wiscene = *state.scene;
// Prerequisite: flip world Z coordinate
FlipZAxis(state);
wiscene.Update(0.f);
// Add extension prerequisite
state.gltfModel.extensionsUsed = {
"KHR_materials_ior",
"KHR_materials_specular",
};
if (scene.lights.GetCount() > 0)
{
state.gltfModel.extensionsUsed.push_back("KHR_lights_punctual");
}
for (size_t i = 0; i < scene.materials.GetCount(); ++i)
{
const MaterialComponent& material = scene.materials[i];
if (material.transmission > 0 || material.textures[wi::scene::MaterialComponent::TRANSMISSIONMAP].resource.IsValid())
{
state.gltfModel.extensionsUsed.push_back("KHR_materials_transmission");
}
if (material.IsUsingSpecularGlossinessWorkflow())
{
state.gltfModel.extensionsUsed.push_back("KHR_materials_pbrSpecularGlossiness");
}
if (material.GetEmissiveStrength() != 1.0f)
{
state.gltfModel.extensionsUsed.push_back("KHR_materials_emissive_strength");
}
if (material.shaderType == MaterialComponent::SHADERTYPE::SHADERTYPE_PBR_CLOTH)
{
state.gltfModel.extensionsUsed.push_back("KHR_materials_sheen");
}
else if (material.shaderType == MaterialComponent::SHADERTYPE::SHADERTYPE_PBR_CLEARCOAT)
{
state.gltfModel.extensionsUsed.push_back("KHR_materials_clearcoat");
}
else if (material.shaderType == MaterialComponent::SHADERTYPE::SHADERTYPE_PBR_CLOTH_CLEARCOAT)
{
state.gltfModel.extensionsUsed.push_back("KHR_materials_sheen");
state.gltfModel.extensionsUsed.push_back("KHR_materials_clearcoat");
}
else if (material.shaderType == MaterialComponent::SHADERTYPE::SHADERTYPE_UNLIT)
{
state.gltfModel.extensionsUsed.push_back("KHR_materials_unlit");
}
else if (material.shaderType == MaterialComponent::SHADERTYPE::SHADERTYPE_PBR_ANISOTROPIC)
{
state.gltfModel.extensionsUsed.push_back("KHR_materials_anisotropy");
}
}
if (wiscene.materials.GetCount() == 0)
{
state.gltfModel.materials.emplace_back().name = "dummyMaterial";
}
// Terrain chunks need some work to remap virtual texture atlas to individual textures for GLTF:
for (size_t i = 0; i < scene.terrains.GetCount(); ++i)
{
using namespace wi::terrain;
Terrain& terrain = scene.terrains[i];
for (auto& it : terrain.chunks)
{
const Chunk& chunk = it.first;
ChunkData& chunk_data = it.second;
MaterialComponent* material = scene.materials.GetComponent(chunk_data.entity);
if (material == nullptr)
continue;
VirtualTexture& vt = *chunk_data.vt;
for (uint32_t map_type = 0; map_type < arraysize(terrain.atlas.maps); ++map_type)
{
if (material->textures[map_type].name.empty())
{
const NameComponent* chunk_name = scene.names.GetComponent(chunk_data.entity);
if (chunk_name != nullptr)
{
switch (map_type)
{
default:
case MaterialComponent::BASECOLORMAP:
material->textures[map_type].name = chunk_name->name + "_basecolormap.png";
break;
case MaterialComponent::NORMALMAP:
material->textures[map_type].name = chunk_name->name + "_normalmap.png";
break;
case MaterialComponent::SURFACEMAP:
material->textures[map_type].name = chunk_name->name + "_surfacemap.png";
break;
}
}
}
if (map_type == 0)
{
auto tile = vt.residency ? vt.tiles[vt.tiles.size() - 2] : vt.tiles.back(); // last nonpacked mip
const float2 resolution_rcp = float2(
1.0f / (float)terrain.atlas.maps[map_type].texture.desc.width,
1.0f / (float)terrain.atlas.maps[map_type].texture.desc.height
);
material->texMulAdd.x = (float)SVT_TILE_SIZE * resolution_rcp.x;
material->texMulAdd.y = (float)SVT_TILE_SIZE * resolution_rcp.y;
material->texMulAdd.z = ((float)tile.x * (float)SVT_TILE_SIZE_PADDED + SVT_TILE_BORDER) * resolution_rcp.x;
material->texMulAdd.w = ((float)tile.y * (float)SVT_TILE_SIZE_PADDED + SVT_TILE_BORDER) * resolution_rcp.y;
}
}
}
}
// Write Materials
for(size_t mt_id = 0; mt_id < wiscene.materials.GetCount(); ++mt_id)
{
auto& material = wiscene.materials[mt_id];
auto materialEntity = wiscene.materials.GetEntity(mt_id);
auto nameComponent = wiscene.names.GetComponent(materialEntity);
tinygltf::Material material_builder;
if(nameComponent != nullptr)
{
material_builder.name = nameComponent->name;
}
// Dielectric-Metallic Workflow (Base PBR)
// Textures
if(material.textures[wi::scene::MaterialComponent::BASECOLORMAP].resource.IsValid())
{
material_builder.pbrMetallicRoughness.baseColorTexture = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::BASECOLORMAP
);
}
if(material.textures[wi::scene::MaterialComponent::NORMALMAP].resource.IsValid())
{
auto normalTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::NORMALMAP
);
material_builder.normalTexture.index = normalTexInfo_pre.index;
material_builder.normalTexture.texCoord = normalTexInfo_pre.texCoord;
}
if(material.textures[wi::scene::MaterialComponent::OCCLUSIONMAP].resource.IsValid())
{
auto occlTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::OCCLUSIONMAP
);
material_builder.occlusionTexture.index = occlTexInfo_pre.index;
material_builder.occlusionTexture.texCoord = occlTexInfo_pre.texCoord;
}
if(material.textures[wi::scene::MaterialComponent::EMISSIVEMAP].resource.IsValid())
{
material_builder.emissiveTexture = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::EMISSIVEMAP
);
}
if(material.textures[wi::scene::MaterialComponent::SURFACEMAP].resource.IsValid())
{
material_builder.pbrMetallicRoughness.metallicRoughnessTexture = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::SURFACEMAP
);
}
// Values
material_builder.pbrMetallicRoughness.baseColorFactor = {
material.baseColor.x,
material.baseColor.y,
material.baseColor.z,
material.baseColor.w
};
material_builder.pbrMetallicRoughness.roughnessFactor = { material.roughness };
material_builder.pbrMetallicRoughness.metallicFactor = { material.metalness };
material_builder.emissiveFactor = {
material.emissiveColor.x,
material.emissiveColor.y,
material.emissiveColor.z,
};
if (material.alphaRef < 1.f)
{
material_builder.alphaMode = "MASK";
material_builder.alphaCutoff = 1.f - material.alphaRef;
}
switch(material.userBlendMode)
{
case wi::enums::BLENDMODE_ALPHA:
material_builder.alphaMode = "BLEND";
break;
default:
break;
}
material_builder.doubleSided = material.IsDoubleSided();
// Unlit extension (KHR_materials_unlit)
// Values
if (material.shaderType == wi::scene::MaterialComponent::SHADERTYPE_UNLIT)
{
material_builder.extensions["KHR_materials_unlit"] = tinygltf::Value();
}
if (material.GetEmissiveStrength() != 1.0f)
{
tinygltf::Value::Object KHR_materials_emissive_strength_builder = {
{"emissiveStrength", tinygltf::Value(double(material.GetEmissiveStrength()))}
};
material_builder.extensions["KHR_materials_emissive_strength"] = tinygltf::Value(KHR_materials_emissive_strength_builder);
}
// Transmission extension (KHR_materials_transmission)
// Values
if (material.transmission > 0 || material.textures[wi::scene::MaterialComponent::TRANSMISSIONMAP].resource.IsValid())
{
tinygltf::Value::Object KHR_materials_transmission_builder = {
{"transmissionFactor", tinygltf::Value(double(material.transmission))}
};
// Textures
if (material.textures[wi::scene::MaterialComponent::TRANSMISSIONMAP].resource.IsValid())
{
auto transmissionTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::TRANSMISSIONMAP
);
KHR_materials_transmission_builder["transmissionTexture"] = tinygltf::Value({
{"index",tinygltf::Value(transmissionTexInfo_pre.index)},
{"texCoord",tinygltf::Value(transmissionTexInfo_pre.texCoord)}
});
}
material_builder.extensions["KHR_materials_transmission"] = tinygltf::Value(KHR_materials_transmission_builder);
}
// Specular-glosiness extension (KHR_materials_pbrSpecularGlossiness)
if(material.IsUsingSpecularGlossinessWorkflow())
{
// Values
tinygltf::Value::Object KHR_materials_pbrSpecularGlossiness_builder = {
{"diffuseFactor", tinygltf::Value({
tinygltf::Value(double(material.baseColor.x)),
tinygltf::Value(double(material.baseColor.y)),
tinygltf::Value(double(material.baseColor.z)),
tinygltf::Value(double(material.baseColor.w))
})},
{"specularFactor", tinygltf::Value({
tinygltf::Value(double(material.specularColor.x)),
tinygltf::Value(double(material.specularColor.y)),
tinygltf::Value(double(material.specularColor.z))
})},
{"glossinessFactor", tinygltf::Value(double(material.roughness))}
};
// Textures
if(material.textures[MaterialComponent::BASECOLORMAP].resource.IsValid())
{
auto diffuseTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::BASECOLORMAP
);
KHR_materials_pbrSpecularGlossiness_builder["diffuseTexture"] = tinygltf::Value({
{"index",tinygltf::Value(diffuseTexInfo_pre.index)},
{"texCoord",tinygltf::Value(diffuseTexInfo_pre.texCoord)}
});
}
if(material.textures[MaterialComponent::SURFACEMAP].resource.IsValid())
{
auto specglossTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::SURFACEMAP
);
KHR_materials_pbrSpecularGlossiness_builder["specularGlossinessTexture"] = tinygltf::Value({
{"index",tinygltf::Value(specglossTexInfo_pre.index)},
{"texCoord",tinygltf::Value(specglossTexInfo_pre.texCoord)}
});
}
}
// Sheen extension (KHR_materials_sheen)
if(material.shaderType == wi::scene::MaterialComponent::SHADERTYPE_PBR_CLOTH || material.shaderType == wi::scene::MaterialComponent::SHADERTYPE_PBR_CLOTH_CLEARCOAT)
{
// Values
tinygltf::Value::Object KHR_materials_sheen_builder = {
{"sheenColorFactor", tinygltf::Value({
tinygltf::Value(double(material.sheenColor.x)),
tinygltf::Value(double(material.sheenColor.y)),
tinygltf::Value(double(material.sheenColor.z))
})},
{"sheenRoughnessFactor", tinygltf::Value(double(material.sheenRoughness))}
};
// Textures
if(material.textures[wi::scene::MaterialComponent::SHEENCOLORMAP].resource.IsValid())
{
auto sheencolorTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::SHEENCOLORMAP
);
KHR_materials_sheen_builder["sheenColorTexture"] = tinygltf::Value({
{"index",tinygltf::Value(sheencolorTexInfo_pre.index)},
{"texCoord",tinygltf::Value(sheencolorTexInfo_pre.texCoord)}
});
}
if(material.textures[wi::scene::MaterialComponent::SHEENROUGHNESSMAP].resource.IsValid())
{
auto sheenRoughTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::SHEENROUGHNESSMAP
);
KHR_materials_sheen_builder["sheenRoughnessTexture"] = tinygltf::Value({
{"index",tinygltf::Value(sheenRoughTexInfo_pre.index)},
{"texCoord",tinygltf::Value(sheenRoughTexInfo_pre.texCoord)}
});
}
material_builder.extensions["KHR_materials_sheen"] = tinygltf::Value(KHR_materials_sheen_builder);
}
// Clearcoat extension (KHR_materials_clearcoat)
// Values
if (material.shaderType == MaterialComponent::SHADERTYPE_PBR_CLEARCOAT || material.shaderType == MaterialComponent::SHADERTYPE_PBR_CLOTH_CLEARCOAT)
{
tinygltf::Value::Object KHR_materials_clearcoat_builder = {
{"clearcoatFactor", tinygltf::Value(double(material.clearcoat))},
{"clearcoatRoughnessFactor", tinygltf::Value(double(material.clearcoatRoughness))}
};
// Textures
if (material.textures[wi::scene::MaterialComponent::CLEARCOATMAP].resource.IsValid())
{
auto clearcoatTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::CLEARCOATMAP
);
KHR_materials_clearcoat_builder["clearcoatTexture"] = tinygltf::Value({
{"index",tinygltf::Value(clearcoatTexInfo_pre.index)},
{"texCoord",tinygltf::Value(clearcoatTexInfo_pre.texCoord)}
});
}
if (material.textures[wi::scene::MaterialComponent::CLEARCOATNORMALMAP].resource.IsValid())
{
auto clearcoatNormTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::CLEARCOATNORMALMAP
);
KHR_materials_clearcoat_builder["clearcoatNormalTexture"] = tinygltf::Value({
{"index",tinygltf::Value(clearcoatNormTexInfo_pre.index)},
{"texCoord",tinygltf::Value(clearcoatNormTexInfo_pre.texCoord)}
});
}
if (material.textures[wi::scene::MaterialComponent::CLEARCOATROUGHNESSMAP].resource.IsValid())
{
auto clearcoatRoughTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::CLEARCOATROUGHNESSMAP
);
KHR_materials_clearcoat_builder["clearcoatRoughnessTexture"] = tinygltf::Value({
{"index",tinygltf::Value(clearcoatRoughTexInfo_pre.index)},
{"texCoord",tinygltf::Value(clearcoatRoughTexInfo_pre.texCoord)}
});
}
material_builder.extensions["KHR_materials_clearcoat"] = tinygltf::Value(KHR_materials_clearcoat_builder);
}
// IOR Extension (KHR_materials_ior)
float ior_retrieve_phase1 = std::sqrt(material.reflectance);
float ior_retrieve_phase2 = -(1+ior_retrieve_phase1)/(ior_retrieve_phase1-1);
tinygltf::Value::Object KHR_materials_ior_builder = {
{"ior",tinygltf::Value(double(ior_retrieve_phase2))}
};
material_builder.extensions["KHR_materials_ior"] = tinygltf::Value(KHR_materials_ior_builder);
// Specular Extension (KHR_materials_specular)
tinygltf::Value::Object KHR_materials_specular_builder = {
{"specularFactor", tinygltf::Value(material.specularColor.w)},
{"specularColorFactor",tinygltf::Value({
tinygltf::Value(double(material.specularColor.x)),
tinygltf::Value(double(material.specularColor.y)),
tinygltf::Value(double(material.specularColor.z))
})}
};
if(material.textures[wi::scene::MaterialComponent::SPECULARMAP].resource.IsValid())
{
auto specularTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::SPECULARMAP
);
KHR_materials_specular_builder["specularTexture"] = tinygltf::Value({
{"index",tinygltf::Value(specularTexInfo_pre.index)},
{"texCoord",tinygltf::Value(specularTexInfo_pre.texCoord)}
});
KHR_materials_specular_builder["specularColorTexture"] = tinygltf::Value({
{"index",tinygltf::Value(specularTexInfo_pre.index)},
{"texCoord",tinygltf::Value(specularTexInfo_pre.texCoord)}
});
}
material_builder.extensions["KHR_materials_specular"] = tinygltf::Value(KHR_materials_specular_builder);
if (material.shaderType == MaterialComponent::SHADERTYPE_PBR_ANISOTROPIC)
{
// Anisotropy Extension (KHR_materials_anisotropy)
tinygltf::Value::Object KHR_materials_anisotropy_builder = {
{"anisotropyStrength", tinygltf::Value(material.anisotropy_strength)},
{"anisotropyRotation", tinygltf::Value(material.anisotropy_rotation)}
};
if (material.textures[wi::scene::MaterialComponent::ANISOTROPYMAP].resource.IsValid())
{
auto specularTexInfo_pre = _ExportHelper_StoreMaterialTexture(
state,
wi::helper::GetDirectoryFromPath(filename),
material,
wi::scene::MaterialComponent::ANISOTROPYMAP
);
KHR_materials_anisotropy_builder["anisotropyTexture"] = tinygltf::Value({
{"index",tinygltf::Value(specularTexInfo_pre.index)},
{"texCoord",tinygltf::Value(specularTexInfo_pre.texCoord)}
});
}
material_builder.extensions["KHR_materials_anisotropy"] = tinygltf::Value(KHR_materials_anisotropy_builder);
}
state.gltfModel.materials.push_back(material_builder);
}
// Revert terrain texture remappings for residency tiles:
for (size_t i = 0; i < scene.terrains.GetCount(); ++i)
{
using namespace wi::terrain;
Terrain& terrain = scene.terrains[i];
for (auto& it : terrain.chunks)
{
const Chunk& chunk = it.first;
ChunkData& chunk_data = it.second;
MaterialComponent* material = scene.materials.GetComponent(chunk_data.entity);
if (material == nullptr)
continue;
VirtualTexture& vt = *chunk_data.vt;
if (vt.residency == nullptr)
continue;
material->texMulAdd = XMFLOAT4(1, 1, 0, 0);
}
}
// Write Meshes
for(size_t m_id = 0; m_id < wiscene.meshes.GetCount(); ++m_id)
{
auto& mesh = wiscene.meshes[m_id];
auto meshEntity = wiscene.meshes.GetEntity(m_id);
auto nameComponent = wiscene.names.GetComponent(meshEntity);
tinygltf::Mesh mesh_builder;
mesh_builder.name = nameComponent->name;
tinygltf::Buffer buffer_builder;
int buffer_index = (int)state.gltfModel.buffers.size();
size_t buf_idc_size = 0;
size_t buf_d_vpos_size = 0;
size_t buf_d_vnorm_size = 0;
size_t buf_d_vtan_size = 0;
size_t buf_d_uv0_size = 0;
size_t buf_d_uv1_size = 0;
size_t buf_d_joint_size = 0;
size_t buf_d_weights_size = 0;
size_t buf_d_col_size = 0;
size_t buf_d_vpos_offset = 0;
size_t buf_d_vnorm_offset = 0;
size_t buf_d_vtan_offset = 0;
size_t buf_d_uv0_offset = 0;
size_t buf_d_uv1_offset = 0;
size_t buf_d_joint_offset = 0;
size_t buf_d_weights_offset = 0;
size_t buf_d_col_offset = 0;
// Write mesh data to buffer first and then figure things out...
size_t buf_i = 0;
// We reverse the indices' windings so that the face isn't flipped
for (size_t i = 0; i < mesh.indices.size(); i += 3)
{
_ExportHelper_valuetobuf(mesh.indices[i + 0], buffer_builder, buf_i);
_ExportHelper_valuetobuf(mesh.indices[i + 2], buffer_builder, buf_i);
_ExportHelper_valuetobuf(mesh.indices[i + 1], buffer_builder, buf_i);
}
buf_idc_size = buf_i;
// Write positions next
buf_d_vpos_offset = buf_i;
for(auto& m_position : mesh.vertex_positions)
{
_ExportHelper_valuetobuf(m_position, buffer_builder, buf_i);
}
buf_d_vpos_size = buf_i - buf_d_vpos_offset;
// Write normals next
buf_d_vnorm_offset = buf_i;
for(auto& m_normal : mesh.vertex_normals)
{
XMVECTOR nor = XMLoadFloat3(&m_normal);
nor = XMVector3Normalize(nor);
XMStoreFloat3(&m_normal, nor);
_ExportHelper_valuetobuf(m_normal, buffer_builder, buf_i);
}
buf_d_vnorm_size = buf_i - buf_d_vnorm_offset;
// Write tangents next
buf_d_vtan_offset = buf_i;
for(auto& m_tangent : mesh.vertex_tangents)
{
float w = m_tangent.w;
XMVECTOR tan = XMLoadFloat4(&m_tangent);
tan = XMVector3Normalize(tan);
XMStoreFloat4(&m_tangent, tan);
m_tangent.w = w;
_ExportHelper_valuetobuf(m_tangent, buffer_builder, buf_i);
}
buf_d_vtan_size = buf_i - buf_d_vtan_offset;
// Write uvset 0 next
buf_d_uv0_offset = buf_i;
for(auto& m_uv0 : mesh.vertex_uvset_0)
{
_ExportHelper_valuetobuf(m_uv0, buffer_builder, buf_i);
}
buf_d_uv0_size = buf_i - buf_d_uv0_offset;
// Write uvset 1 next
buf_d_uv1_offset = buf_i;
for(auto& m_uv1 : mesh.vertex_uvset_1)
{
_ExportHelper_valuetobuf(m_uv1, buffer_builder, buf_i);
}
buf_d_uv1_size = buf_i - buf_d_uv1_offset;
// Write animation data - armature bone id
buf_d_joint_offset = buf_i;
for(auto& m_joint : mesh.vertex_boneindices)
{
auto m_joint_v = XMLoadUInt4(&m_joint);
XMSHORT4 m_joint_s;
XMStoreShort4(&m_joint_s, m_joint_v);
_ExportHelper_valuetobuf(m_joint_s, buffer_builder, buf_i);
}
buf_d_joint_size = buf_i - buf_d_joint_offset;
// Write animation data - armature weights id
buf_d_weights_offset = buf_i;
for(auto& m_bone_weights : mesh.vertex_boneweights)
{
_ExportHelper_valuetobuf(m_bone_weights, buffer_builder, buf_i);
}
buf_d_weights_size = buf_i - buf_d_weights_offset;
// Write vertex colors
buf_d_col_offset = buf_i;
for(auto& m_col : mesh.vertex_colors)
{
_ExportHelper_valuetobuf(m_col, buffer_builder, buf_i);
}
buf_d_col_size = buf_i - buf_d_col_offset;
// Mesh data
tinygltf::BufferView vpos_bufferView_builder;
int vpos_bufferView_index = (int)state.gltfModel.bufferViews.size();
vpos_bufferView_builder.buffer = buffer_index;
vpos_bufferView_builder.byteOffset = buf_d_vpos_offset;
vpos_bufferView_builder.byteLength = buf_d_vpos_size;
vpos_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(vpos_bufferView_builder);
tinygltf::Accessor vpos_accessor_builder;
int vpos_accessor_index = (int)state.gltfModel.accessors.size();
vpos_accessor_builder.bufferView = vpos_bufferView_index;
vpos_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
vpos_accessor_builder.count = mesh.vertex_positions.size();
vpos_accessor_builder.type = TINYGLTF_TYPE_VEC3;
auto bound = wi::primitive::AABB(mesh.vertex_positions[0], mesh.vertex_positions[0]);
for(auto& vpos : mesh.vertex_positions)
{
bound = wi::primitive::AABB::Merge(bound, wi::primitive::AABB(vpos, vpos));
}
auto bound_max = bound.getMax();
auto bound_min = bound.getMin();
vpos_accessor_builder.maxValues = {bound_max.x, bound_max.y, bound_max.z};
vpos_accessor_builder.minValues = {bound_min.x, bound_min.y, bound_min.z};
state.gltfModel.accessors.push_back(vpos_accessor_builder);
tinygltf::BufferView vnorm_bufferView_builder;
int vnorm_bufferView_index = (int)state.gltfModel.bufferViews.size();
vnorm_bufferView_builder.buffer = buffer_index;
vnorm_bufferView_builder.byteOffset = buf_d_vnorm_offset;
vnorm_bufferView_builder.byteLength = buf_d_vnorm_size;
vnorm_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(vnorm_bufferView_builder);
tinygltf::Accessor vnorm_accessor_builder;
int vnorm_accessor_index = (int)state.gltfModel.accessors.size();
vnorm_accessor_builder.bufferView = vnorm_bufferView_index;
vnorm_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
vnorm_accessor_builder.count = mesh.vertex_normals.size();
vnorm_accessor_builder.type = TINYGLTF_TYPE_VEC3;
state.gltfModel.accessors.push_back(vnorm_accessor_builder);
tinygltf::BufferView vtan_bufferView_builder;
int vtan_bufferView_index = (int)state.gltfModel.bufferViews.size();
vtan_bufferView_builder.buffer = buffer_index;
vtan_bufferView_builder.byteOffset = buf_d_vtan_offset;
vtan_bufferView_builder.byteLength = buf_d_vtan_size;
vtan_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(vtan_bufferView_builder);
tinygltf::Accessor vtan_accessor_builder;
int vtan_accessor_index = (int)state.gltfModel.accessors.size();
vtan_accessor_builder.bufferView = vtan_bufferView_index;
vtan_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
vtan_accessor_builder.count = mesh.vertex_tangents.size();
vtan_accessor_builder.type = TINYGLTF_TYPE_VEC4;
state.gltfModel.accessors.push_back(vtan_accessor_builder);
int uv0_accessor_index = -1;
if(buf_d_uv0_size > 0)
{
tinygltf::BufferView uv0_bufferView_builder;
int uv0_bufferView_index = (int)state.gltfModel.bufferViews.size();
uv0_bufferView_builder.buffer = buffer_index;
uv0_bufferView_builder.byteOffset = buf_d_uv0_offset;
uv0_bufferView_builder.byteLength = buf_d_uv0_size;
uv0_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(uv0_bufferView_builder);
tinygltf::Accessor uv0_accessor_builder;
uv0_accessor_index = (int)state.gltfModel.accessors.size();
uv0_accessor_builder.bufferView = uv0_bufferView_index;
uv0_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
uv0_accessor_builder.count = mesh.vertex_uvset_0.size();
uv0_accessor_builder.type = TINYGLTF_TYPE_VEC2;
state.gltfModel.accessors.push_back(uv0_accessor_builder);
}
int uv1_accessor_index = -1;
if(buf_d_uv1_size > 0)
{
tinygltf::BufferView uv1_bufferView_builder;
int uv1_bufferView_index = (int)state.gltfModel.bufferViews.size();
uv1_bufferView_builder.buffer = buffer_index;
uv1_bufferView_builder.byteOffset = buf_d_uv1_offset;
uv1_bufferView_builder.byteLength = buf_d_uv1_size;
uv1_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(uv1_bufferView_builder);
tinygltf::Accessor uv1_accessor_builder;
uv1_accessor_index = (int)state.gltfModel.accessors.size();
uv1_accessor_builder.bufferView = uv1_bufferView_index;
uv1_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
uv1_accessor_builder.count = mesh.vertex_uvset_1.size();
uv1_accessor_builder.type = TINYGLTF_TYPE_VEC2;
state.gltfModel.accessors.push_back(uv1_accessor_builder);
}
int joint_accessor_index = -1;
if(buf_d_joint_size > 0)
{
tinygltf::BufferView joint_bufferView_builder;
int joint_bufferView_index = (int)state.gltfModel.bufferViews.size();
joint_bufferView_builder.buffer = buffer_index;
joint_bufferView_builder.byteOffset = buf_d_joint_offset;
joint_bufferView_builder.byteLength = buf_d_joint_size;
joint_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(joint_bufferView_builder);
tinygltf::Accessor joint_accessor_builder;
joint_accessor_index = (int)state.gltfModel.accessors.size();
joint_accessor_builder.bufferView = joint_bufferView_index;
joint_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
joint_accessor_builder.count = mesh.vertex_boneindices.size();
joint_accessor_builder.type = TINYGLTF_TYPE_VEC4;
state.gltfModel.accessors.push_back(joint_accessor_builder);
}
int weight_accessor_index = -1;
if(buf_d_weights_size > 0)
{
tinygltf::BufferView weight_bufferView_builder;
int weight_bufferView_index = (int)state.gltfModel.bufferViews.size();
weight_bufferView_builder.buffer = buffer_index;
weight_bufferView_builder.byteOffset = buf_d_weights_offset;
weight_bufferView_builder.byteLength = buf_d_weights_size;
weight_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(weight_bufferView_builder);
tinygltf::Accessor weight_accessor_builder;
weight_accessor_index = (int)state.gltfModel.accessors.size();
weight_accessor_builder.bufferView = weight_bufferView_index;
weight_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
weight_accessor_builder.count = mesh.vertex_boneweights.size();
weight_accessor_builder.type = TINYGLTF_TYPE_VEC4;
state.gltfModel.accessors.push_back(weight_accessor_builder);
}
int color_accessor_index = -1;
if(buf_d_col_size > 0)
{
tinygltf::BufferView color_bufferView_builder;
int color_bufferView_index = (int)state.gltfModel.bufferViews.size();
color_bufferView_builder.buffer = buffer_index;
color_bufferView_builder.byteOffset = buf_d_col_offset;
color_bufferView_builder.byteLength = buf_d_col_size;
color_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(color_bufferView_builder);
tinygltf::Accessor color_accessor_builder;
color_accessor_index = (int)state.gltfModel.accessors.size();
color_accessor_builder.bufferView = color_bufferView_index;
color_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
color_accessor_builder.count = mesh.vertex_colors.size();
color_accessor_builder.type = TINYGLTF_TYPE_VEC4;
state.gltfModel.accessors.push_back(color_accessor_builder);
}
// Morph targets
// Prep up a zero value defaults for sparse morph target
size_t buf_d_morph_def_offset, buf_d_morph_def_size;
buf_d_morph_def_offset = buf_i;
for(auto& m_position : mesh.vertex_positions)
{
_ExportHelper_valuetobuf(XMFLOAT3(), buffer_builder, buf_i);
}
buf_d_morph_def_size = buf_i - buf_d_morph_def_offset;
tinygltf::BufferView morph_def_bufferView_builder;
int morph_def_bufferView_index = (int)state.gltfModel.bufferViews.size();
morph_def_bufferView_builder.buffer = buffer_index;
morph_def_bufferView_builder.byteOffset = buf_d_morph_def_offset;
morph_def_bufferView_builder.byteLength = buf_d_morph_def_size;
morph_def_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(morph_def_bufferView_builder);
wi::vector<std::pair<size_t,bool>> morphs_pos_accessors;
wi::vector<std::pair<size_t,bool>> morphs_norm_accessors;
for(auto& m_morph : mesh.morph_targets)
{
size_t buf_d_morph_idc_pos_offset, buf_d_morph_idc_pos_size,
buf_d_morph_idc_nor_offset, buf_d_morph_idc_nor_size,
buf_d_morph_pos_size, buf_d_morph_pos_offset,
buf_d_morph_norm_size, buf_d_morph_norm_offset;
buf_d_morph_idc_pos_offset = buf_i;
for(auto& m_morph_idc : m_morph.sparse_indices_positions)
{
_ExportHelper_valuetobuf(m_morph_idc, buffer_builder, buf_i);
}
buf_d_morph_idc_pos_size = buf_i - buf_d_morph_idc_pos_offset;
buf_d_morph_idc_nor_offset = buf_i;
for (auto& m_morph_idc : m_morph.sparse_indices_normals)
{
_ExportHelper_valuetobuf(m_morph_idc, buffer_builder, buf_i);
}
buf_d_morph_idc_nor_size = buf_i - buf_d_morph_idc_nor_offset;
buf_d_morph_pos_offset = buf_i;
for(auto& m_morph_pos : m_morph.vertex_positions)
{
_ExportHelper_valuetobuf(m_morph_pos, buffer_builder, buf_i);
}
buf_d_morph_pos_size = buf_i - buf_d_morph_pos_offset;
buf_d_morph_norm_offset = buf_i;
for(auto& m_morph_norm : m_morph.vertex_normals)
{
_ExportHelper_valuetobuf(m_morph_norm, buffer_builder, buf_i);
}
buf_d_morph_norm_size = buf_i - buf_d_morph_norm_offset;
// Build accessors
size_t morph_pos_accessor_index = 0;
if (buf_d_morph_pos_size > 0)
{
// Sparse accessor indices
auto is_sparse = (m_morph.sparse_indices_positions.size() > 0);
int morph_sparse_bufferView_index = 0;
if (is_sparse)
{
tinygltf::BufferView morph_sparse_bufferView_builder;
morph_sparse_bufferView_index = (int)state.gltfModel.bufferViews.size();
morph_sparse_bufferView_builder.buffer = buffer_index;
morph_sparse_bufferView_builder.byteOffset = buf_d_morph_idc_pos_offset;
morph_sparse_bufferView_builder.byteLength = buf_d_morph_idc_pos_size;
morph_sparse_bufferView_builder.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(morph_sparse_bufferView_builder);
}
tinygltf::BufferView morph_pos_bufferView_builder;
int morph_pos_bufferView_index = (int)state.gltfModel.bufferViews.size();
morph_pos_bufferView_builder.buffer = buffer_index;
morph_pos_bufferView_builder.byteOffset = buf_d_morph_pos_offset;
morph_pos_bufferView_builder.byteLength = buf_d_morph_pos_size;
morph_pos_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(morph_pos_bufferView_builder);
tinygltf::Accessor morph_pos_accessor_builder;
morph_pos_accessor_index = state.gltfModel.accessors.size();
morph_pos_accessor_builder.bufferView = (is_sparse) ? morph_def_bufferView_index : morph_pos_bufferView_index;
morph_pos_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
morph_pos_accessor_builder.count = (is_sparse) ? mesh.vertex_positions.size() : m_morph.vertex_positions.size();
morph_pos_accessor_builder.type = TINYGLTF_TYPE_VEC3;
if (is_sparse)
{
auto& sparse = morph_pos_accessor_builder.sparse;
sparse.isSparse = true;
sparse.count = (int)m_morph.sparse_indices_positions.size();
sparse.indices.bufferView = morph_sparse_bufferView_index;
sparse.indices.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
sparse.values.bufferView = morph_pos_bufferView_index;
}
state.gltfModel.accessors.push_back(morph_pos_accessor_builder);
}
size_t morph_norm_accessor_index = 0;
if (buf_d_morph_norm_size > 0)
{
// Sparse accessor indices
auto is_sparse = (m_morph.sparse_indices_normals.size() > 0);
int morph_sparse_bufferView_index = 0;
if (is_sparse)
{
tinygltf::BufferView morph_sparse_bufferView_builder;
morph_sparse_bufferView_index = (int)state.gltfModel.bufferViews.size();
morph_sparse_bufferView_builder.buffer = buffer_index;
morph_sparse_bufferView_builder.byteOffset = buf_d_morph_idc_nor_offset;
morph_sparse_bufferView_builder.byteLength = buf_d_morph_idc_nor_size;
morph_sparse_bufferView_builder.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(morph_sparse_bufferView_builder);
}
tinygltf::BufferView morph_norm_bufferView_builder;
int morph_norm_bufferView_index = (int)state.gltfModel.bufferViews.size();
morph_norm_bufferView_builder.buffer = buffer_index;
morph_norm_bufferView_builder.byteOffset = buf_d_morph_norm_offset;
morph_norm_bufferView_builder.byteLength = buf_d_morph_norm_size;
morph_norm_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(morph_norm_bufferView_builder);
tinygltf::Accessor morph_norm_accessor_builder;
morph_norm_accessor_index = state.gltfModel.accessors.size();
// morph_norm_accessor_builder.bufferView = (is_sparse) ? vnorm_bufferView_index : morph_norm_bufferView_index;
morph_norm_accessor_builder.bufferView = (is_sparse) ? morph_def_bufferView_index : morph_norm_bufferView_index;
morph_norm_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
// morph_norm_accessor_builder.count = (is_sparse) ? mesh.vertex_normals.size() : m_morph.vertex_normals.size();
morph_norm_accessor_builder.count = (is_sparse) ? mesh.vertex_positions.size() : m_morph.vertex_normals.size();
morph_norm_accessor_builder.type = TINYGLTF_TYPE_VEC3;
if (is_sparse)
{
auto& sparse = morph_norm_accessor_builder.sparse;
sparse.isSparse = true;
sparse.count = (int)m_morph.sparse_indices_normals.size();
sparse.indices.bufferView = morph_sparse_bufferView_index;
sparse.indices.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
sparse.values.bufferView = morph_norm_bufferView_index;
}
state.gltfModel.accessors.push_back(morph_norm_accessor_builder);
}
morphs_pos_accessors.push_back({morph_pos_accessor_index, buf_d_morph_pos_size > 0});
morphs_norm_accessors.push_back({morph_norm_accessor_index, buf_d_morph_norm_size > 0});
}
// Store mesh indices by subset, which mapped to primitives
uint32_t first_subset = 0;
uint32_t last_subset = 0;
mesh.GetLODSubsetRange(0, first_subset, last_subset); // GLTF doesn't have LODs, so export only LOD0
for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
{
const MeshComponent::MeshSubset& subset = mesh.subsets[subsetIndex];
if (subset.indexCount == 0)
continue;
// One primitive has one bufferview and accessor?
tinygltf::BufferView indices_bufferView_builder;
int indices_bufferView_index = (int)state.gltfModel.bufferViews.size();
indices_bufferView_builder.buffer = buffer_index;
indices_bufferView_builder.byteOffset = subset.indexOffset*sizeof(uint32_t);
indices_bufferView_builder.byteLength = subset.indexCount*sizeof(uint32_t);
indices_bufferView_builder.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(indices_bufferView_builder);
tinygltf::Accessor indices_accessor_builder;
int indices_accessor_index = (int)state.gltfModel.accessors.size();
indices_accessor_builder.bufferView = indices_bufferView_index;
indices_accessor_builder.byteOffset = 0;
indices_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
indices_accessor_builder.count = subset.indexCount;
indices_accessor_builder.type = TINYGLTF_TYPE_SCALAR;
state.gltfModel.accessors.push_back(indices_accessor_builder);
tinygltf::Primitive primitive_builder;
primitive_builder.indices = indices_accessor_index;
primitive_builder.attributes["POSITION"] = vpos_accessor_index;
primitive_builder.attributes["NORMAL"] = vnorm_accessor_index;
primitive_builder.attributes["TANGENT"] = vtan_accessor_index;
if(buf_d_uv0_size > 0)
primitive_builder.attributes["TEXCOORD_0"] = uv0_accessor_index;
if(buf_d_uv1_size > 0)
primitive_builder.attributes["TEXCOORD_1"] = uv1_accessor_index;
if(buf_d_joint_size > 0)
primitive_builder.attributes["JOINTS_0"] = joint_accessor_index;
if(buf_d_weights_size > 0)
primitive_builder.attributes["WEIGHTS_0"] = weight_accessor_index;
if(buf_d_col_size > 0)
primitive_builder.attributes["COLOR_0"] = color_accessor_index;
primitive_builder.material = std::max(0, std::min((int)wiscene.materials.GetIndex(subset.materialID), (int)wiscene.materials.GetCount() - 1));
primitive_builder.mode = TINYGLTF_MODE_TRIANGLES;
for(size_t msub_morph_id = 0; msub_morph_id < morphs_pos_accessors.size(); ++msub_morph_id)
{
std::map<std::string, int> morph_info;
if(morphs_pos_accessors[msub_morph_id].second)
morph_info["POSITION"] = (int)morphs_pos_accessors[msub_morph_id].first;
if(morphs_norm_accessors[msub_morph_id].second)
morph_info["NORMAL"] = (int)morphs_norm_accessors[msub_morph_id].first;
primitive_builder.targets.push_back(morph_info);
}
mesh_builder.primitives.push_back(primitive_builder);
}
state.gltfModel.buffers.push_back(buffer_builder);
state.gltfModel.meshes.push_back(mesh_builder);
}
// Write Lights
for(size_t l_id = 0; l_id < wiscene.lights.GetCount(); ++l_id)
{
auto& light = wiscene.lights[l_id];
auto lightEntity = wiscene.lights.GetEntity(l_id);
auto nameComponent = wiscene.names.GetComponent(lightEntity);
tinygltf::Light light_builder;
if(nameComponent != nullptr)
light_builder.name = nameComponent->name;
light_builder.type =
(light.type == LightComponent::LightType::DIRECTIONAL) ? "directional" :
(light.type == LightComponent::LightType::SPOT) ? "spot" : "point";
light_builder.color = {double(light.color.x), double(light.color.y), double(light.color.z)};
light_builder.intensity = double(light.intensity);
light_builder.range = double(light.range);
light_builder.spot.outerConeAngle = double(light.outerConeAngle);
light_builder.spot.innerConeAngle = double(light.innerConeAngle);
state.gltfModel.lights.push_back(light_builder);
}
// Write Cameras
for(size_t cam_id = 0; cam_id < wiscene.cameras.GetCount(); ++cam_id)
{
auto& camera = wiscene.cameras[cam_id];
auto cameraEntity = wiscene.cameras.GetEntity(cam_id);
auto nameComponent = wiscene.names.GetComponent(cameraEntity);
tinygltf::Camera camera_builder;
if(nameComponent != nullptr)
camera_builder.name = nameComponent->name;
camera_builder.type = "perspective";
camera_builder.perspective.aspectRatio = camera.width/camera.height;
camera_builder.perspective.yfov = camera.fov;
camera_builder.perspective.zfar = camera.zFarP;
camera_builder.perspective.znear = camera.zNearP;
state.gltfModel.cameras.push_back(camera_builder);
}
tinygltf::Scene scene_builder;
// Compose Node
for(size_t t_id = 0; t_id < wiscene.transforms.GetCount(); ++t_id)
{
auto& transformComponent = wiscene.transforms[t_id];
auto transformEntity = wiscene.transforms.GetEntity(t_id);
auto nameComponent = wiscene.names.GetComponent(transformEntity);
auto light_forward_flip = wiscene.lights.Contains(transformEntity);
if(light_forward_flip)
transformComponent.RotateRollPitchYaw(XMFLOAT3(-XM_PIDIV2,0,0));
auto objectComponent = wiscene.objects.GetComponent(transformEntity);
tinygltf::Node node_builder;
int node_index = (int)t_id;
if(nameComponent != nullptr)
node_builder.name = nameComponent->name;
node_builder.scale.push_back(transformComponent.scale_local.x);
node_builder.scale.push_back(transformComponent.scale_local.y);
node_builder.scale.push_back(transformComponent.scale_local.z);
node_builder.rotation.push_back(transformComponent.rotation_local.x);
node_builder.rotation.push_back(transformComponent.rotation_local.y);
node_builder.rotation.push_back(transformComponent.rotation_local.z);
node_builder.rotation.push_back(transformComponent.rotation_local.w);
node_builder.translation.push_back(transformComponent.translation_local.x);
node_builder.translation.push_back(transformComponent.translation_local.y);
node_builder.translation.push_back(transformComponent.translation_local.z);
if(light_forward_flip)
transformComponent.RotateRollPitchYaw(XMFLOAT3(XM_PIDIV2,0,0));
if(objectComponent != nullptr)
{
if(objectComponent->meshID != wi::ecs::INVALID_ENTITY && wiscene.meshes.Contains(objectComponent->meshID))
{
node_builder.mesh = (int)wiscene.meshes.GetIndex(objectComponent->meshID);
if(wiscene.meshes[node_builder.mesh].armatureID != wi::ecs::INVALID_ENTITY)
{
node_builder.skin = (int)wiscene.armatures.GetIndex(wiscene.meshes[node_builder.mesh].armatureID);
}
}
}
if(wiscene.lights.Contains(transformEntity))
{
tinygltf::Value::Object node_light_extension_builder;
node_light_extension_builder["light"] = tinygltf::Value(int(wiscene.lights.GetIndex(transformEntity)));
node_builder.extensions["KHR_lights_punctual"] = tinygltf::Value(node_light_extension_builder);
}
if(wiscene.cameras.Contains(transformEntity))
{
node_builder.camera = (int)wiscene.cameras.GetIndex(transformEntity);
}
state.nodeMap[transformEntity] = node_index;
state.gltfModel.nodes.push_back(node_builder);
scene_builder.nodes.push_back(node_index);
}
// Write Armature
for(size_t arm_id = 0; arm_id < wiscene.armatures.GetCount(); ++arm_id)
{
auto& armatureComponent = wiscene.armatures[arm_id];
auto armatureEntity = wiscene.armatures.GetEntity(arm_id);
auto nameComponent = wiscene.names.GetComponent(armatureEntity);
tinygltf::Skin skin_builder;
if(nameComponent != nullptr)
skin_builder.name = nameComponent->name;
tinygltf::Buffer buffer_builder;
int buffer_index = (int)state.gltfModel.buffers.size();
size_t buf_i = 0;
// Write Inverse Bind Matrices to buffer
for(auto& arm_invBindMatrix : armatureComponent.inverseBindMatrices)
{
_ExportHelper_valuetobuf(arm_invBindMatrix, buffer_builder, buf_i);
}
state.gltfModel.buffers.push_back(buffer_builder);
//// Inverse Bind Matrices data access
//// Analysis prep
//wi::jobsystem::context analysis_ctx;
//std::mutex analysis_lock_sync;
//uint32_t analysis_readCount = 16384;
tinygltf::BufferView aibm_bufferView_builder;
int aibm_bufferView_index = (int)state.gltfModel.bufferViews.size();
aibm_bufferView_builder.buffer = buffer_index;
// aibm_bufferView_builder.byteOffset = 0;
aibm_bufferView_builder.byteLength = buf_i;
// aibm_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(aibm_bufferView_builder);
tinygltf::Accessor aibm_accessor_builder;
int aibm_accessor_index = (int)state.gltfModel.accessors.size();
aibm_accessor_builder.bufferView = aibm_bufferView_index;
aibm_accessor_builder.byteOffset = 0;
aibm_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
aibm_accessor_builder.count = armatureComponent.inverseBindMatrices.size();
aibm_accessor_builder.type = TINYGLTF_TYPE_MAT4;
// _ExportHelper_AccessorAnalysis(
// aibm_accessor_builder, armatureComponent.inverseBindMatrices,
// 0, armatureComponent.inverseBindMatrices.size(),
// analysis_readCount);
state.gltfModel.accessors.push_back(aibm_accessor_builder);
skin_builder.inverseBindMatrices = aibm_accessor_index;
for(auto& arm_bone_id : armatureComponent.boneCollection)
{
skin_builder.joints.push_back(state.nodeMap[arm_bone_id]);
}
state.gltfModel.skins.push_back(skin_builder);
}
// Write Animations
wi::unordered_map<Entity, std::vector<size_t>> animation_datasets;
if(wiscene.animations.GetCount() > 0)
{
// Find accessor types first!
wi::unordered_map<Entity, size_t> animdata_vectype;
for(size_t anim_id = 0; anim_id < wiscene.animations.GetCount(); ++anim_id)
{
auto& animation = wiscene.animations[anim_id];
for(auto& channel : animation.channels)
{
if(animdata_vectype.find(animation.samplers[channel.samplerIndex].data) == animdata_vectype.end())
animdata_vectype[animation.samplers[channel.samplerIndex].data] =
(channel.path == AnimationComponent::AnimationChannel::Path::SCALE) ? TINYGLTF_TYPE_VEC3 :
(channel.path == AnimationComponent::AnimationChannel::Path::ROTATION) ? TINYGLTF_TYPE_VEC4 :
(channel.path == AnimationComponent::AnimationChannel::Path::TRANSLATION) ? TINYGLTF_TYPE_VEC3 :
(channel.path == AnimationComponent::AnimationChannel::Path::WEIGHTS) ? TINYGLTF_TYPE_SCALAR : TINYGLTF_TYPE_SCALAR;
}
}
// Store animations into a single buffer
size_t buf_i = 0;
tinygltf::Buffer buffer_builder;
int buffer_index = (int)state.gltfModel.buffers.size();
for(size_t animdata_id = 0; animdata_id < wiscene.animation_datas.GetCount(); ++animdata_id)
{
auto& animdata = wiscene.animation_datas[animdata_id];
auto animdataEntity = wiscene.animation_datas.GetEntity(animdata_id);
size_t buf_d_ftime_offset, buf_d_ftime_size,
buf_d_fdata_offset, buf_d_fdata_size;
buf_d_ftime_offset = buf_i;
for(auto& animdata_ftime : animdata.keyframe_times)
{
_ExportHelper_valuetobuf(animdata_ftime, buffer_builder, buf_i);
}
buf_d_ftime_size = buf_i - buf_d_ftime_offset;
buf_d_fdata_offset = buf_i;
for(auto& animdata_fdata : animdata.keyframe_data)
{
_ExportHelper_valuetobuf(animdata_fdata, buffer_builder, buf_i);
}
buf_d_fdata_size = buf_i - buf_d_fdata_offset;
tinygltf::BufferView ftime_bufferView_builder;
int ftime_bufferView_index = (int)state.gltfModel.bufferViews.size();
ftime_bufferView_builder.buffer = buffer_index;
ftime_bufferView_builder.byteOffset = buf_d_ftime_offset;
ftime_bufferView_builder.byteLength = buf_d_ftime_size;
// ftime_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(ftime_bufferView_builder);
tinygltf::Accessor ftime_accessor_builder;
int ftime_accessor_index = (int)state.gltfModel.accessors.size();
ftime_accessor_builder.bufferView = ftime_bufferView_index;
ftime_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
ftime_accessor_builder.count = animdata.keyframe_times.size();
ftime_accessor_builder.type = TINYGLTF_TYPE_SCALAR;
state.gltfModel.accessors.push_back(ftime_accessor_builder);
tinygltf::BufferView fdata_bufferView_builder;
int fdata_bufferView_index = (int)state.gltfModel.bufferViews.size();
fdata_bufferView_builder.buffer = buffer_index;
fdata_bufferView_builder.byteOffset = buf_d_fdata_offset;
fdata_bufferView_builder.byteLength = buf_d_fdata_size;
// fdata_bufferView_builder.target = TINYGLTF_TARGET_ARRAY_BUFFER;
state.gltfModel.bufferViews.push_back(fdata_bufferView_builder);
int anim_vectype = TINYGLTF_TYPE_SCALAR;
size_t anim_sizedivider = 1;
auto find_animdata_vectype = animdata_vectype.find(animdataEntity);
if(find_animdata_vectype != animdata_vectype.end())
{
anim_vectype = (int)find_animdata_vectype->second;
anim_sizedivider = (find_animdata_vectype->second == TINYGLTF_TYPE_SCALAR) ? 1 : find_animdata_vectype->second;
}
tinygltf::Accessor fdata_accessor_builder;
int fdata_accessor_index = (int)state.gltfModel.accessors.size();
fdata_accessor_builder.bufferView = fdata_bufferView_index;
fdata_accessor_builder.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
fdata_accessor_builder.count = animdata.keyframe_data.size() / anim_sizedivider;
fdata_accessor_builder.type = anim_vectype;
state.gltfModel.accessors.push_back(fdata_accessor_builder);
animation_datasets[animdataEntity] = {
(size_t)ftime_bufferView_index,
(size_t)ftime_accessor_index,
(size_t)fdata_bufferView_index,
(size_t)fdata_accessor_index
};
}
state.gltfModel.buffers.push_back(buffer_builder);
}
for(size_t anim_id = 0; anim_id < wiscene.animations.GetCount(); ++anim_id)
{
auto& animation = wiscene.animations[anim_id];
tinygltf::Animation animation_builder;
Entity entity = wiscene.animations.GetEntity(anim_id);
const NameComponent* name = wiscene.names.GetComponent(entity);
if (name != nullptr)
{
animation_builder.name = name->name;
}
for(auto& sampler : animation.samplers)
{
tinygltf::AnimationSampler sampler_builder;
sampler_builder.input = (int)animation_datasets[sampler.data][1];
sampler_builder.output = (int)animation_datasets[sampler.data][3];
sampler_builder.interpolation =
(sampler.mode == AnimationComponent::AnimationSampler::Mode::CUBICSPLINE) ? "CUBICSPLINE" :
(sampler.mode == AnimationComponent::AnimationSampler::Mode::STEP) ? "STEP" : "LINEAR";
animation_builder.samplers.push_back(sampler_builder);
}
for(auto& channel : animation.channels)
{
tinygltf::AnimationChannel channel_builder;
channel_builder.target_node = state.nodeMap[channel.target];
channel_builder.sampler = (int)channel.samplerIndex;
channel_builder.target_path =
(channel.path == AnimationComponent::AnimationChannel::Path::SCALE) ? "scale" :
(channel.path == AnimationComponent::AnimationChannel::Path::ROTATION) ? "rotation" :
(channel.path == AnimationComponent::AnimationChannel::Path::TRANSLATION) ? "translation" : "weights";
animation_builder.channels.push_back(channel_builder);
}
state.gltfModel.animations.push_back(animation_builder);
}
// Compose hierarchy
for(size_t h_id = 0; h_id < wiscene.hierarchy.GetCount(); ++h_id)
{
auto& hierarchyComponent = wiscene.hierarchy[h_id];
auto hierarchyEntity = wiscene.hierarchy.GetEntity(h_id);
if(wiscene.transforms.Contains(hierarchyComponent.parentID) && wiscene.transforms.Contains(hierarchyEntity))
{
int node_index = (int)wiscene.transforms.GetIndex(hierarchyEntity);
size_t parent_node_index = wiscene.transforms.GetIndex(hierarchyComponent.parentID);
state.gltfModel.nodes[parent_node_index].children.push_back(node_index);
}
}
state.gltfModel.defaultScene = (int)state.gltfModel.scenes.size();
state.gltfModel.scenes.push_back(scene_builder);
state.gltfModel.asset.version = "2.0";
state.gltfModel.asset.generator = "WickedEngine";
auto file_extension = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(filename));
if(file_extension == "GLB")
{
res = writer.WriteGltfSceneToFile(&state.gltfModel, filename, false, true, true, true);
}
else
{
res = writer.WriteGltfSceneToFile(&state.gltfModel, filename, false, false, true, false);
}
assert(res);
// Restore scene world orientation
FlipZAxis(state);
wiscene.Update(0.f);
}