mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-08 11:13:50 +00:00
Compare commits
13 Commits
harden-api
...
skinning
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04d8d637fc | ||
|
|
f02c504481 | ||
|
|
5c47eda8be | ||
|
|
876f6e194e | ||
|
|
1b85cb8c59 | ||
|
|
134d660722 | ||
|
|
a15bfdc85d | ||
|
|
39a309c416 | ||
|
|
4402576a91 | ||
|
|
26a7a9f525 | ||
|
|
d180641246 | ||
|
|
0c0b993639 | ||
|
|
0de4d7c05f |
@@ -99,6 +99,28 @@ void Matrix::LookAt(float m[4][4], float eye[3], float lookat[3],
|
||||
#endif
|
||||
}
|
||||
|
||||
void Matrix::Identity(float m[4][4]) {
|
||||
m[0][0] = 1.0f;
|
||||
m[0][1] = 0.0f;
|
||||
m[0][2] = 0.0f;
|
||||
m[0][3] = 0.0f;
|
||||
|
||||
m[1][0] = 0.0f;
|
||||
m[1][1] = 1.0f;
|
||||
m[1][2] = 0.0f;
|
||||
m[1][3] = 0.0f;
|
||||
|
||||
m[2][0] = 0.0f;
|
||||
m[2][1] = 0.0f;
|
||||
m[2][2] = 1.0f;
|
||||
m[2][3] = 0.0f;
|
||||
|
||||
m[3][0] = 0.0f;
|
||||
m[3][1] = 0.0f;
|
||||
m[3][2] = 0.0f;
|
||||
m[3][3] = 1.0f;
|
||||
}
|
||||
|
||||
void Matrix::Inverse(float m[4][4]) {
|
||||
/*
|
||||
* codes from intel web
|
||||
@@ -195,7 +217,16 @@ void Matrix::Inverse(float m[4][4]) {
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix::Mult(float dst[4][4], float m0[4][4], float m1[4][4]) {
|
||||
void Matrix::Add(float dst[4][4], const float m0[4][4], const float m1[4][4]) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst[i][j] += m0[i][j] + m1[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Matrix::Mult(float dst[4][4], const float m0[4][4], const float m1[4][4]) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst[i][j] = 0;
|
||||
@@ -206,7 +237,7 @@ void Matrix::Mult(float dst[4][4], float m0[4][4], float m1[4][4]) {
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix::MultV(float dst[3], float m[4][4], float v[3]) {
|
||||
void Matrix::MultV(float dst[3], const float m[4][4], const float v[3]) {
|
||||
// printf("v = %f, %f, %f\n", v[0], v[1], v[2]);
|
||||
dst[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
|
||||
dst[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
|
||||
@@ -214,3 +245,10 @@ void Matrix::MultV(float dst[3], float m[4][4], float v[3]) {
|
||||
// printf("m = %f, %f, %f\n", m[3][0], m[3][1], m[3][2]);
|
||||
// printf("dst = %f, %f, %f\n", dst[0], dst[1], dst[2]);
|
||||
}
|
||||
|
||||
void Matrix::MultV4(float dst[4], const float m[4][4], const float v[4]) {
|
||||
dst[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3];
|
||||
dst[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3];
|
||||
dst[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3];
|
||||
dst[3] = m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3];
|
||||
}
|
||||
|
||||
@@ -10,8 +10,11 @@ public:
|
||||
static void LookAt(float m[4][4], float eye[3], float lookat[3],
|
||||
float up[3]);
|
||||
static void Inverse(float m[4][4]);
|
||||
static void Mult(float dst[4][4], float m0[4][4], float m1[4][4]);
|
||||
static void MultV(float dst[3], float m[4][4], float v[3]);
|
||||
static void Identity(float m[4][4]);
|
||||
static void Add(float dst[4][4], const float m0[4][4], const float m1[4][4]);
|
||||
static void Mult(float dst[4][4], const float m0[4][4], const float m1[4][4]);
|
||||
static void MultV(float dst[3], const float m[4][4], const float v[3]);
|
||||
static void MultV4(float dst[4], const float m[4][4], const float v[4]);
|
||||
};
|
||||
|
||||
#endif //
|
||||
|
||||
@@ -52,11 +52,6 @@
|
||||
#include <math.h>
|
||||
#include "trackball.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4244)
|
||||
#pragma warning(disable : 4305)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This size should really be based on the distance from the center of
|
||||
* rotation to the point on the object underneath the mouse. That
|
||||
@@ -173,11 +168,11 @@ void trackball(float q[4], float p1x, float p1y, float p2x, float p2y) {
|
||||
/*
|
||||
* Avoid problems with out-of-control values...
|
||||
*/
|
||||
if (t > 1.0f)
|
||||
t = 1.0f;
|
||||
if (t < -1.0f)
|
||||
t = -1.0f;
|
||||
phi = 2.0f * asinf(t);
|
||||
if (t > 1.0)
|
||||
t = 1.0;
|
||||
if (t < -1.0)
|
||||
t = -1.0;
|
||||
phi = 2.0 * asin(t);
|
||||
|
||||
axis_to_quat(a, phi, q);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
//#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
#define BUFFER_OFFSET(i) (reinterpret_cast<void *>(i))
|
||||
|
||||
#define CheckGLErrors(desc) \
|
||||
{ \
|
||||
@@ -251,7 +252,7 @@ static void SetupMeshState(tinygltf::Model &model, GLuint progId) {
|
||||
const tinygltf::BufferView &bufferView = model.bufferViews[i];
|
||||
if (bufferView.target == 0) {
|
||||
std::cout << "WARN: bufferView.target is zero" << std::endl;
|
||||
continue; // Unsupported bufferView.
|
||||
continue; // Unsupported or not directly used bufferView.
|
||||
}
|
||||
|
||||
const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer];
|
||||
@@ -528,6 +529,13 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) {
|
||||
for (; it != itEnd; it++) {
|
||||
assert(it->second >= 0);
|
||||
const tinygltf::Accessor &accessor = model.accessors[it->second];
|
||||
const tinygltf::BufferView &bufferView = model.bufferViews[accessor.bufferView];
|
||||
|
||||
if (bufferView.target == 0) {
|
||||
// Unsupported or not directly used buffer
|
||||
continue;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, gBufferState[accessor.bufferView].vb);
|
||||
CheckErrors("bind buffer");
|
||||
int size = 1;
|
||||
@@ -667,7 +675,10 @@ static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
|
||||
// std::cout << it->first << std::endl;
|
||||
// FIXME(syoyo): Refactor.
|
||||
// DrawCurves(scene, it->second);
|
||||
DrawMesh(model, model.meshes[node.mesh]);
|
||||
|
||||
if ((node.mesh >= 0) && (node.mesh < int(model.meshes.size()))) {
|
||||
DrawMesh(model, model.meshes[node.mesh]);
|
||||
}
|
||||
|
||||
// Draw child nodes.
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
@@ -677,7 +688,7 @@ static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
static void DrawModel(tinygltf::Model &model) {
|
||||
static void DrawModel(tinygltf::Model &model, size_t scene_idx) {
|
||||
#if 0
|
||||
std::map<std::string, tinygltf::Mesh>::const_iterator it(scene.meshes.begin());
|
||||
std::map<std::string, tinygltf::Mesh>::const_iterator itEnd(scene.meshes.end());
|
||||
@@ -688,9 +699,8 @@ static void DrawModel(tinygltf::Model &model) {
|
||||
}
|
||||
#else
|
||||
|
||||
// TODO(syoyo): Support non-default scenes.
|
||||
assert(model.defaultScene >= 0);
|
||||
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
|
||||
assert(scene_idx < model.scenes.size());
|
||||
const tinygltf::Scene &scene = model.scenes[scene_idx];
|
||||
for (size_t i = 0; i < scene.nodes.size(); i++) {
|
||||
DrawNode(model, model.nodes[scene.nodes[i]]);
|
||||
}
|
||||
@@ -760,8 +770,18 @@ int main(int argc, char **argv) {
|
||||
|
||||
Init();
|
||||
|
||||
if (model.scenes.empty()) {
|
||||
std::cerr << "glTF model does not have scenes" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// DBG
|
||||
PrintNodes(model.scenes[model.defaultScene]);
|
||||
size_t scene_idx = size_t(model.defaultScene);
|
||||
if (model.defaultScene == -1) {
|
||||
scene_idx = 0;
|
||||
}
|
||||
|
||||
PrintNodes(model.scenes[scene_idx]);
|
||||
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "Failed to initialize GLFW." << std::endl;
|
||||
@@ -853,7 +873,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
DrawModel(model);
|
||||
DrawModel(model, scene_idx);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
newoption {
|
||||
trigger = "asan",
|
||||
description = "Enable Address Sanitizer(gcc5+ ang clang only)"
|
||||
}
|
||||
|
||||
solution "glview"
|
||||
-- location ( "build" )
|
||||
configurations { "Debug", "Release" }
|
||||
platforms {"native", "x64", "x32"}
|
||||
|
||||
|
||||
project "glview"
|
||||
|
||||
-- Use clang for better asan expericen
|
||||
if _OPTIONS["asan"] then
|
||||
toolset "clang"
|
||||
end
|
||||
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
cppdialect "C++11"
|
||||
@@ -13,6 +23,12 @@ solution "glview"
|
||||
includedirs { "../../" }
|
||||
|
||||
configuration { "linux" }
|
||||
|
||||
if _OPTIONS["asan"] then
|
||||
buildoptions { "-fsanitize=address,undefined" }
|
||||
linkoptions { "-fsanitize=address,undefined" }
|
||||
end
|
||||
|
||||
linkoptions { "`pkg-config --libs glfw3`" }
|
||||
links { "GL", "GLU", "m", "GLEW", "X11", "Xrandr", "Xinerama", "Xi", "Xxf86vm", "Xcursor", "dl" }
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ static void vsub(const float *src1, const float *src2, float *dst) {
|
||||
}
|
||||
|
||||
static void vcopy(const float *v1, float *v2) {
|
||||
register int i;
|
||||
int i;
|
||||
for (i = 0; i < 3; i++)
|
||||
v2[i] = v1[i];
|
||||
}
|
||||
|
||||
2507
examples/skinning/HandmadeMath.h
Normal file
2507
examples/skinning/HandmadeMath.h
Normal file
File diff suppressed because it is too large
Load Diff
19
examples/skinning/README.md
Normal file
19
examples/skinning/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Simple glTF skinning sample with CPU skinning implementation.
|
||||
|
||||
Example use CPU implementation of skinning for the explanation of how to process skin property in glTF format.
|
||||
|
||||
Animation and skinning code is based on SacchaWillems' Vulkan-glTF-PBR: https://github.com/SaschaWillems/Vulkan-glTF-PBR
|
||||
|
||||
OpenGL is still used to display renderings.
|
||||
|
||||
## Build on Linux and macOS
|
||||
|
||||
```
|
||||
$ premake5 gmake
|
||||
$ make
|
||||
$ ./bin/native/Debug/skinning simple-skin.gltf
|
||||
```
|
||||
|
||||
## Note on asset
|
||||
|
||||
`simple-skin.gltf` is grabbed from gltfTutorial https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_019_SimpleSkin.md
|
||||
1043
examples/skinning/main.cc
Normal file
1043
examples/skinning/main.cc
Normal file
File diff suppressed because it is too large
Load Diff
22
examples/skinning/morph-targets.cc
Normal file
22
examples/skinning/morph-targets.cc
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
void MorthTargets(std::vector<float> &weights,
|
||||
std::vector<std::vector<float>> &targets,
|
||||
std::vector<float> *output)
|
||||
{
|
||||
assert(weights.size() > 0);
|
||||
assert(targets.size() > 0);
|
||||
assert(weights.size() == targets.size());
|
||||
|
||||
// Assume all position has same number of vertices;
|
||||
|
||||
// TODO(parallelize)
|
||||
for (size_t v = 0; v < targets[0].size(); v++) { // for each vertex
|
||||
(*output)[v] = 0.0f;
|
||||
for (size_t i = 0; i < weights.size(); i++) {
|
||||
(*output)[v] += weights[i] * targets[i][v];
|
||||
}
|
||||
}
|
||||
}
|
||||
59
examples/skinning/premake5.lua
Normal file
59
examples/skinning/premake5.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
newoption {
|
||||
trigger = "asan",
|
||||
description = "Enable Address Sanitizer(gcc5+ ang clang only)"
|
||||
}
|
||||
|
||||
solution "skinning"
|
||||
-- location ( "build" )
|
||||
configurations { "Debug", "Release" }
|
||||
platforms {"native", "x64", "x32"}
|
||||
|
||||
project "skinning"
|
||||
|
||||
-- Use clang for better asan expericen
|
||||
if _OPTIONS["asan"] then
|
||||
toolset "clang"
|
||||
end
|
||||
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
cppdialect "C++11"
|
||||
files { "main.cc", "skinning.cc", "morph-targets.cc", "../common/trackball.cc", "../common/matrix.cc" }
|
||||
includedirs { "./" }
|
||||
includedirs { "../../" }
|
||||
|
||||
configuration { "linux" }
|
||||
|
||||
if _OPTIONS["asan"] then
|
||||
buildoptions { "-fsanitize=address" }
|
||||
linkoptions { "-fsanitize=address" }
|
||||
end
|
||||
|
||||
linkoptions { "`pkg-config --libs glfw3`" }
|
||||
links { "GL", "GLU", "m", "GLEW", "X11", "Xrandr", "Xinerama", "Xi", "Xxf86vm", "Xcursor", "dl" }
|
||||
|
||||
configuration { "windows" }
|
||||
-- Edit path to glew and GLFW3 fit to your environment.
|
||||
includedirs { "../../../../local/glew-1.13.0/include/" }
|
||||
includedirs { "../../../../local/glfw-3.2.bin.WIN32/include/" }
|
||||
libdirs { "../../../../local/glew-1.13.0/lib/Release/Win32/" }
|
||||
libdirs { "../../../../local/glfw-3.2.bin.WIN32/lib-vc2013/" }
|
||||
links { "glfw3", "gdi32", "winmm", "user32", "glew32", "glu32","opengl32", "kernel32" }
|
||||
defines { "_CRT_SECURE_NO_WARNINGS" }
|
||||
|
||||
configuration { "macosx" }
|
||||
includedirs { "/usr/local/include" }
|
||||
buildoptions { "-Wno-deprecated-declarations" }
|
||||
libdirs { "/usr/local/lib" }
|
||||
links { "glfw", "GLEW" }
|
||||
linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" }
|
||||
|
||||
configuration "Debug"
|
||||
defines { "DEBUG" }
|
||||
symbols "On"
|
||||
warnings "Extra"
|
||||
|
||||
configuration "Release"
|
||||
defines { "NDEBUG" }
|
||||
optimize "On"
|
||||
warnings "Extra"
|
||||
16
examples/skinning/shader.frag
Normal file
16
examples/skinning/shader.frag
Normal file
@@ -0,0 +1,16 @@
|
||||
uniform sampler2D diffuseTex;
|
||||
uniform int uIsCurve;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec2 texcoord;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
//gl_FragColor = vec4(0.5 * normalize(normal) + 0.5, 1.0);
|
||||
//gl_FragColor = vec4(texcoord, 0.0, 1.0);
|
||||
if (uIsCurve > 0) {
|
||||
gl_FragColor = texture2D(diffuseTex, texcoord);
|
||||
} else {
|
||||
gl_FragColor = vec4(0.5 * normalize(normal) + 0.5, 1.0);
|
||||
}
|
||||
}
|
||||
16
examples/skinning/shader.vert
Normal file
16
examples/skinning/shader.vert
Normal file
@@ -0,0 +1,16 @@
|
||||
attribute vec3 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_texcoord;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec2 texcoord;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 p = gl_ModelViewProjectionMatrix * vec4(in_vertex, 1);
|
||||
gl_Position = p;
|
||||
vec4 nn = gl_ModelViewMatrixInverseTranspose * vec4(normalize(in_normal), 0);
|
||||
normal = nn.xyz;
|
||||
|
||||
texcoord = in_texcoord;
|
||||
}
|
||||
148
examples/skinning/simple-skin.gltf
Normal file
148
examples/skinning/simple-skin.gltf
Normal file
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"scenes" : [ {
|
||||
"nodes" : [ 0 ]
|
||||
} ],
|
||||
|
||||
"nodes" : [ {
|
||||
"skin" : 0,
|
||||
"mesh" : 0,
|
||||
"children" : [ 1 ]
|
||||
}, {
|
||||
"children" : [ 2 ],
|
||||
"translation" : [ 0.0, 1.0, 0.0 ]
|
||||
}, {
|
||||
"rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
|
||||
} ],
|
||||
|
||||
"meshes" : [ {
|
||||
"primitives" : [ {
|
||||
"attributes" : {
|
||||
"POSITION" : 1,
|
||||
"JOINTS_0" : 2,
|
||||
"WEIGHTS_0" : 3
|
||||
},
|
||||
"indices" : 0
|
||||
} ]
|
||||
} ],
|
||||
|
||||
"skins" : [ {
|
||||
"inverseBindMatrices" : 4,
|
||||
"joints" : [ 1, 2 ]
|
||||
} ],
|
||||
|
||||
"animations" : [ {
|
||||
"channels" : [ {
|
||||
"sampler" : 0,
|
||||
"target" : {
|
||||
"node" : 2,
|
||||
"path" : "rotation"
|
||||
}
|
||||
} ],
|
||||
"samplers" : [ {
|
||||
"input" : 5,
|
||||
"interpolation" : "LINEAR",
|
||||
"output" : 6
|
||||
} ]
|
||||
} ],
|
||||
|
||||
"buffers" : [ {
|
||||
"uri" : "data:application/gltf-buffer;base64,AAABAAMAAAADAAIAAgADAAUAAgAFAAQABAAFAAcABAAHAAYABgAHAAkABgAJAAgAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAD8AAAAAAACAPwAAAD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAwD8AAAAAAACAPwAAwD8AAAAAAAAAAAAAAEAAAAAAAACAPwAAAEAAAAAA",
|
||||
"byteLength" : 168
|
||||
}, {
|
||||
"uri" : "data:application/gltf-buffer;base64,AAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAABAPwAAgD4AAAAAAAAAAAAAQD8AAIA+AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAPwAAAD8AAAAAAAAAAAAAgD4AAEA/AAAAAAAAAAAAAIA+AABAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAA=",
|
||||
"byteLength" : 320
|
||||
}, {
|
||||
"uri" : "data:application/gltf-buffer;base64,AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAvwAAgL8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAL8AAIC/AAAAAAAAgD8=",
|
||||
"byteLength" : 128
|
||||
}, {
|
||||
"uri" : "data:application/gltf-buffer;base64,AAAAAAAAAD8AAIA/AADAPwAAAEAAACBAAABAQAAAYEAAAIBAAACQQAAAoEAAALBAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAPT9ND/0/TQ/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAPT9NL/0/TQ/AAAAAAAAAAD0/TS/9P00PwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAAAAAAAAAIA/",
|
||||
"byteLength" : 240
|
||||
} ],
|
||||
|
||||
"bufferViews" : [ {
|
||||
"buffer" : 0,
|
||||
"byteOffset" : 0,
|
||||
"byteLength" : 48,
|
||||
"target" : 34963
|
||||
}, {
|
||||
"buffer" : 0,
|
||||
"byteOffset" : 48,
|
||||
"byteLength" : 120,
|
||||
"target" : 34962
|
||||
}, {
|
||||
"buffer" : 1,
|
||||
"byteOffset" : 0,
|
||||
"byteLength" : 320,
|
||||
"byteStride" : 16
|
||||
}, {
|
||||
"buffer" : 2,
|
||||
"byteOffset" : 0,
|
||||
"byteLength" : 128
|
||||
}, {
|
||||
"buffer" : 3,
|
||||
"byteOffset" : 0,
|
||||
"byteLength" : 240
|
||||
} ],
|
||||
|
||||
"accessors" : [ {
|
||||
"bufferView" : 0,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5123,
|
||||
"count" : 24,
|
||||
"type" : "SCALAR",
|
||||
"max" : [ 9 ],
|
||||
"min" : [ 0 ]
|
||||
}, {
|
||||
"bufferView" : 1,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 10,
|
||||
"type" : "VEC3",
|
||||
"max" : [ 1.0, 2.0, 0.0 ],
|
||||
"min" : [ 0.0, 0.0, 0.0 ]
|
||||
}, {
|
||||
"bufferView" : 2,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5123,
|
||||
"count" : 10,
|
||||
"type" : "VEC4",
|
||||
"max" : [ 0, 1, 0, 0 ],
|
||||
"min" : [ 0, 1, 0, 0 ]
|
||||
}, {
|
||||
"bufferView" : 2,
|
||||
"byteOffset" : 160,
|
||||
"componentType" : 5126,
|
||||
"count" : 10,
|
||||
"type" : "VEC4",
|
||||
"max" : [ 1.0, 1.0, 0.0, 0.0 ],
|
||||
"min" : [ 0.0, 0.0, 0.0, 0.0 ]
|
||||
}, {
|
||||
"bufferView" : 3,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 2,
|
||||
"type" : "MAT4",
|
||||
"max" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.5, -1.0, 0.0, 1.0 ],
|
||||
"min" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.5, -1.0, 0.0, 1.0 ]
|
||||
}, {
|
||||
"bufferView" : 4,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 12,
|
||||
"type" : "SCALAR",
|
||||
"max" : [ 5.5 ],
|
||||
"min" : [ 0.0 ]
|
||||
}, {
|
||||
"bufferView" : 4,
|
||||
"byteOffset" : 48,
|
||||
"componentType" : 5126,
|
||||
"count" : 12,
|
||||
"type" : "VEC4",
|
||||
"max" : [ 0.0, 0.0, 0.707, 1.0 ],
|
||||
"min" : [ 0.0, 0.0, -0.707, 0.707 ]
|
||||
} ],
|
||||
|
||||
"asset" : {
|
||||
"version" : "2.0"
|
||||
}
|
||||
}
|
||||
252
examples/skinning/skinning.cc
Normal file
252
examples/skinning/skinning.cc
Normal file
@@ -0,0 +1,252 @@
|
||||
#include "skinning.h"
|
||||
|
||||
#include "../common/matrix.h"
|
||||
#include "../common/trackball.h" // for quaternion
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Weverything"
|
||||
#endif
|
||||
|
||||
#define HANDMADE_MATH_IMPLEMENTATION
|
||||
#include "HandmadeMath.h"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
namespace example {
|
||||
|
||||
struct Node {
|
||||
|
||||
float translation[3] = {0.0f, 0.0f, 0.0f};
|
||||
float scale[4] = {1.0f, 1.0f, 1.0f};
|
||||
float rotation[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
|
||||
void update() {
|
||||
}
|
||||
};
|
||||
|
||||
static inline vec4 mix(vec4 x, vec4 y, float a) {
|
||||
vec4 v;
|
||||
v.f[0] = (1.0f - a) * x.f[0] + a * y.f[0];
|
||||
v.f[1] = (1.0f - a) * x.f[1] + a * y.f[1];
|
||||
v.f[2] = (1.0f - a) * x.f[2] + a * y.f[2];
|
||||
v.f[3] = (1.0f - a) * x.f[3] + a * y.f[3];
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void BuildTransofrmMatrix(const float translate[3],
|
||||
const float rotation[4], // as quaternion in glTF
|
||||
const float scale[3], mat4 *transform_matrix) {
|
||||
float T[4][4];
|
||||
T[0][0] = 1.0f;
|
||||
T[0][1] = 0.0f;
|
||||
T[0][2] = 0.0f;
|
||||
T[0][3] = 0.0f;
|
||||
|
||||
T[1][0] = 0.0f;
|
||||
T[1][1] = 1.0f;
|
||||
T[1][2] = 0.0f;
|
||||
T[1][3] = 0.0f;
|
||||
|
||||
T[2][0] = 0.0f;
|
||||
T[2][1] = 0.0f;
|
||||
T[2][2] = 1.0f;
|
||||
T[2][3] = 0.0f;
|
||||
|
||||
T[3][0] = translate[0];
|
||||
T[3][1] = translate[1];
|
||||
T[3][2] = translate[2];
|
||||
T[3][3] = 1.0f;
|
||||
|
||||
float R[4][4];
|
||||
|
||||
build_rotmatrix(R, rotation);
|
||||
|
||||
float S[4][4];
|
||||
S[0][0] = scale[0];
|
||||
S[0][1] = 0.0f;
|
||||
S[0][2] = 0.0f;
|
||||
S[0][3] = 0.0f;
|
||||
|
||||
S[1][0] = 0.0f;
|
||||
S[1][1] = scale[1];
|
||||
S[1][2] = 0.0f;
|
||||
S[1][3] = 0.0f;
|
||||
|
||||
S[2][0] = 0.0f;
|
||||
S[2][1] = 0.0f;
|
||||
S[2][2] = scale[2];
|
||||
S[2][3] = 0.0f;
|
||||
|
||||
S[3][0] = 0.0f;
|
||||
S[3][1] = 0.0f;
|
||||
S[3][2] = 0.0f;
|
||||
S[3][3] = 1.0f;
|
||||
|
||||
float RS[4][4];
|
||||
|
||||
Matrix::Mult(RS, R, S);
|
||||
|
||||
Matrix::Mult(transform_matrix->m, T, RS);
|
||||
}
|
||||
|
||||
void ComputeJointMatrices(
|
||||
const std::vector<mat4> global_transform_of_nodes,
|
||||
const std::vector<mat4> global_transform_of_joint_nodes,
|
||||
const std::vector<mat4> inverse_bind_matrix_for_joints,
|
||||
std::vector<mat4> *output_joint_matrices) {
|
||||
const size_t n = global_transform_of_nodes.size();
|
||||
|
||||
output_joint_matrices->resize(n);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
mat4 g_inv = global_transform_of_nodes[i];
|
||||
Matrix::Inverse(g_inv.m);
|
||||
|
||||
mat4 g_joint = global_transform_of_joint_nodes[i];
|
||||
mat4 inverse_bind_matrix = inverse_bind_matrix_for_joints[i];
|
||||
|
||||
float a[4][4]; // temp matrix
|
||||
Matrix::Mult(a, g_joint.m, inverse_bind_matrix.m);
|
||||
|
||||
Matrix::Mult((*output_joint_matrices)[i].m, g_inv.m, a);
|
||||
}
|
||||
}
|
||||
|
||||
void Skining(const std::vector<float> vertices,
|
||||
const std::vector<float> weights, const std::vector<size_t> joints,
|
||||
const size_t num_skinning_weights,
|
||||
const std::vector<mat4> joint_matrices, const float t,
|
||||
std::vector<float> *skinned_vertices) {
|
||||
assert((vertices.size() % 4) == 0);
|
||||
const size_t num_vertices = vertices.size() / 4;
|
||||
|
||||
skinned_vertices->resize(vertices.size());
|
||||
|
||||
// TODO(syoyo): Ensure sum(weights) = 1.0;
|
||||
|
||||
for (size_t v = 0; v < num_vertices; v++) {
|
||||
const float *w_p = weights.data() + v * num_skinning_weights;
|
||||
const size_t *j_p = joints.data() + v * num_skinning_weights;
|
||||
|
||||
mat4 skin_mat;
|
||||
memset(skin_mat.m, 0, sizeof(float) * 4 * 4);
|
||||
|
||||
for (size_t k = 0; k < num_skinning_weights; k++) {
|
||||
const float w = w_p[k];
|
||||
const mat4 &m = joint_matrices[j_p[k]];
|
||||
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
skin_mat.m[j][i] += w * m.m[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// M = lerp I and skin_mat
|
||||
mat4 M;
|
||||
|
||||
mat4 I;
|
||||
Matrix::Identity(I.m);
|
||||
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
M.m[j][i] = I.m[j][i] * t + (1.0f - t) * skin_mat.m[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
float vtx[4];
|
||||
vtx[0] = vertices[4 * v + 0];
|
||||
vtx[1] = vertices[4 * v + 1];
|
||||
vtx[2] = vertices[4 * v + 2];
|
||||
vtx[3] = vertices[4 * v + 3];
|
||||
|
||||
float ret[4];
|
||||
Matrix::MultV4(ret, M.m, vtx);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAnimation(std::vector<Animation> &animations, uint32_t index,
|
||||
float time, std::vector<Node*> &nodes) {
|
||||
if (index > uint32_t(animations.size() - 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Animation &animation = animations[index];
|
||||
|
||||
bool updated = false;
|
||||
for (auto &channel : animation.channels) {
|
||||
const AnimationSampler &sampler = animation.samplers[channel.samplerIndex];
|
||||
if (sampler.inputs.size() > sampler.outputsVec4.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO(LTE): support interpolation other than LINEAR
|
||||
for (size_t i = 0; i < sampler.inputs.size() - 1; i++) {
|
||||
if ((time >= sampler.inputs[i]) && (time <= sampler.inputs[i + 1])) {
|
||||
float u = std::max(0.0f, time - sampler.inputs[i]) /
|
||||
(sampler.inputs[i + 1] - sampler.inputs[i]);
|
||||
if (u <= 1.0f) {
|
||||
switch (channel.path) {
|
||||
case AnimationChannel::PathType::TRANSLATION: {
|
||||
example::vec4 trans =
|
||||
mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
|
||||
channel.node->translation[0] = trans.f[0];
|
||||
channel.node->translation[1] = trans.f[1];
|
||||
channel.node->translation[2] = trans.f[2];
|
||||
// drop w
|
||||
break;
|
||||
}
|
||||
case AnimationChannel::PathType::SCALE: {
|
||||
example::vec4 scale =
|
||||
mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
|
||||
channel.node->scale[0] = scale.f[0];
|
||||
channel.node->scale[1] = scale.f[1];
|
||||
channel.node->scale[2] = scale.f[2];
|
||||
break;
|
||||
}
|
||||
case AnimationChannel::PathType::ROTATION: {
|
||||
|
||||
hmm_quaternion q1 = HMM_Quaternion(
|
||||
sampler.outputsVec4[i].f[0],
|
||||
sampler.outputsVec4[i].f[1],
|
||||
sampler.outputsVec4[i].f[2],
|
||||
sampler.outputsVec4[i].f[3]);
|
||||
|
||||
hmm_quaternion q2 = HMM_Quaternion(
|
||||
sampler.outputsVec4[i + 1].f[0],
|
||||
sampler.outputsVec4[i + 1].f[1],
|
||||
sampler.outputsVec4[i + 1].f[2],
|
||||
sampler.outputsVec4[i + 1].f[3]);
|
||||
|
||||
hmm_quaternion q = HMM_NormalizeQuaternion(HMM_Slerp(q1, u, q2));
|
||||
|
||||
channel.node->rotation[0] = q.Elements[0];
|
||||
channel.node->rotation[1] = q.Elements[1];
|
||||
channel.node->rotation[2] = q.Elements[2];
|
||||
channel.node->rotation[3] = q.Elements[3];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
for (auto &node : nodes) {
|
||||
node->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace example
|
||||
95
examples/skinning/skinning.h
Normal file
95
examples/skinning/skinning.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifndef EXAMPLE_SKINNING_H_
|
||||
#define EXAMPLE_SKINNING_H_
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
|
||||
namespace example {
|
||||
|
||||
struct mat4 {
|
||||
float m[4][4];
|
||||
};
|
||||
|
||||
struct vec4 {
|
||||
float f[4];
|
||||
};
|
||||
|
||||
// glTF node
|
||||
struct Node;
|
||||
|
||||
|
||||
struct AnimationChannel {
|
||||
enum PathType { TRANSLATION, ROTATION, SCALE };
|
||||
PathType path;
|
||||
Node *node;
|
||||
uint32_t samplerIndex;
|
||||
};
|
||||
|
||||
struct AnimationSampler {
|
||||
enum InterpolationType { LINEAR, STEP, CUBICSPLINE };
|
||||
InterpolationType interpolation;
|
||||
std::vector<float> inputs;
|
||||
std::vector<example::vec4> outputsVec4;
|
||||
};
|
||||
|
||||
struct Animation {
|
||||
std::string name;
|
||||
std::vector<AnimationSampler> samplers;
|
||||
std::vector<AnimationChannel> channels;
|
||||
float start = std::numeric_limits<float>::max();
|
||||
float end = std::numeric_limits<float>::min();
|
||||
};
|
||||
|
||||
///
|
||||
/// Utility function to build transformation matrix from translate/rotation/scale
|
||||
///
|
||||
/// https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md
|
||||
///
|
||||
/// M = T * R * S
|
||||
///
|
||||
void BuildTransofrmMatrix(
|
||||
const float translate[3],
|
||||
const float rotation[4], // as quaternion in glTF
|
||||
const float scale[3],
|
||||
mat4 *transform_matrix);
|
||||
|
||||
|
||||
///
|
||||
/// Compute joint matrices.
|
||||
///
|
||||
/// jointMatrix(j) =
|
||||
/// globalTransformOfNodeThatTheMeshIsAttachedTo^-1 *
|
||||
/// globalTransformOfJointNode(j) *
|
||||
/// inverseBindMatrixForJoint(j);
|
||||
///
|
||||
|
||||
void ComputeJointMatrices(
|
||||
const std::vector<mat4> global_transform_of_nodes,
|
||||
const std::vector<mat4> global_transform_of_joint_nodes,
|
||||
const std::vector<mat4> inverse_bind_matrix_for_joints,
|
||||
std::vector<mat4> output_joint_matrices);
|
||||
|
||||
|
||||
///
|
||||
///
|
||||
/// @param[in] vertices Input vertices(# of elements = num_vertices * 4(xyzw))
|
||||
/// @param[in] weights Linearized weights(# of elements = num_vertices * num_skinning_weights)
|
||||
/// @param[in] weights Linearized weights(# of elements = num_vertices * num_skinning_weights)
|
||||
/// @param[in] num_weights Linearized weights(# of elements = num_vertices *
|
||||
/// @param[in] joint_matrices Array of joint matricies.
|
||||
/// @param[in] t Interpolator. [0.0, 1.0]
|
||||
/// @param[in] skinned_vertices Resulting skinned vertices
|
||||
///
|
||||
void Skining(const std::vector<float> vertices,
|
||||
const std::vector<float> weights, const std::vector<size_t> joints,
|
||||
const size_t num_skinning_weights,
|
||||
const std::vector<mat4> joint_matrices,
|
||||
const float t,
|
||||
std::vector<float> *skinned_vertices);
|
||||
|
||||
} // namespace example
|
||||
|
||||
#endif // EXAMPLE_SKINNING_H_
|
||||
@@ -30,7 +30,8 @@ static std::string PrintMode(int mode) {
|
||||
} else if (mode == TINYGLTF_MODE_TRIANGLE_STRIP) {
|
||||
return "TRIANGLE_STRIP";
|
||||
}
|
||||
return "**UNKNOWN**";
|
||||
|
||||
return "**UNKNOWN**(" + std::to_string(mode) + ")";
|
||||
}
|
||||
|
||||
static std::string PrintTarget(int target) {
|
||||
@@ -39,7 +40,7 @@ static std::string PrintTarget(int target) {
|
||||
} else if (target == 34963) {
|
||||
return "GL_ELEMENT_ARRAY_BUFFER";
|
||||
} else {
|
||||
return "**UNKNOWN**";
|
||||
return "**UNKNOWN**(" + std::to_string(target) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ static std::string PrintType(int ty) {
|
||||
} else if (ty == TINYGLTF_TYPE_MAT4) {
|
||||
return "MAT4";
|
||||
}
|
||||
return "**UNKNOWN**";
|
||||
return "**UNKNOWN**(" + std::to_string(ty) + ")";
|
||||
}
|
||||
|
||||
static std::string PrintComponentType(int ty) {
|
||||
@@ -85,7 +86,7 @@ static std::string PrintComponentType(int ty) {
|
||||
return "DOUBLE";
|
||||
}
|
||||
|
||||
return "**UNKNOWN**";
|
||||
return "**UNKNOWN**(" + std::to_string(ty) + ")";
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -147,7 +148,7 @@ static std::string PrintWrapMode(int mode) {
|
||||
return "MIRRORED_REPEAT";
|
||||
}
|
||||
|
||||
return "**UNKNOWN**";
|
||||
return "**UNKNOWN**(" + std::to_string(mode) + ")";
|
||||
}
|
||||
|
||||
static std::string PrintFilterMode(int mode) {
|
||||
@@ -164,7 +165,7 @@ static std::string PrintFilterMode(int mode) {
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR) {
|
||||
return "LINEAR_MIPMAP_LINEAR";
|
||||
}
|
||||
return "**UNKNOWN**";
|
||||
return "**UNKNOWN**(" + std::to_string(mode) + ")";
|
||||
}
|
||||
|
||||
static std::string PrintIntArray(const std::vector<int> &arr) {
|
||||
|
||||
Reference in New Issue
Block a user