Compare commits

...

13 Commits

Author SHA1 Message Date
Syoyo Fujita
04d8d637fc Add HandmadeMath.
Add simple morphing code.
2018-10-08 14:20:45 +09:00
Syoyo Fujita
f02c504481 Merge branch 'skinning' of github.com:syoyo/tinygltf into skinning 2018-10-07 21:27:12 +09:00
Syoyo Fujita
5c47eda8be Implement some animation. 2018-10-07 21:25:19 +09:00
Syoyo Fujita
876f6e194e Fix build. 2018-09-25 02:21:53 +09:00
Syoyo Fujita
1b85cb8c59 Implement some skinnig stuff. 2018-09-24 22:41:15 +09:00
Syoyo Fujita
134d660722 Merge branch 'skinning' of github.com:syoyo/tinygltf into skinning 2018-09-24 21:09:46 +09:00
Syoyo Fujita
a15bfdc85d Add missing file. 2018-09-24 21:09:24 +09:00
Syoyo Fujita
39a309c416 Merge branch 'skinning' of github.com:syoyo/tinygltf into skinning 2018-09-24 21:03:49 +09:00
Syoyo Fujita
4402576a91 Check bufferView.target 2018-09-24 16:18:12 +09:00
Syoyo Fujita
26a7a9f525 Implement some vertex skinning . 2018-09-24 02:00:43 +09:00
Syoyo Fujita
d180641246 Do not draw skinned mesh(node) otherwise glview crashes. 2018-09-23 18:24:19 +09:00
Syoyo Fujita
0c0b993639 Show value when printing UNKNOWN 2018-09-23 17:57:00 +09:00
Syoyo Fujita
0de4d7c05f Initial skinning example(W.I.P). 2018-09-22 17:51:05 +09:00
17 changed files with 4281 additions and 31 deletions

View File

@@ -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];
}

View File

@@ -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 //

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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" }

View File

@@ -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];
}

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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];
}
}
}

View 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"

View 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);
}
}

View 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;
}

View 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"
}
}

View 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

View 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_

View File

@@ -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) {