5272 lines
189 KiB
C++
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 = align(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);
|
|
}
|