Skip to content

Commit a6c6d64

Browse files
committed
gltf parser now follows hierarchy (with temporary scaling fix)
1 parent 2baffcb commit a6c6d64

1 file changed

Lines changed: 86 additions & 37 deletions

File tree

pkg/file_handlers/gltf.go

Lines changed: 86 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,22 @@ import (
66
"io"
77
"math"
88

9-
"github.com/Patch2PDF/GDTF-Mesh-Reader/v2/pkg/MeshTypes"
109
Types "github.com/Patch2PDF/GDTF-Mesh-Reader/v2/pkg/MeshTypes"
1110
"github.com/qmuntal/gltf"
1211
)
1312

13+
type gltfNode struct {
14+
matrix Types.Matrix
15+
mesh *gltf.Mesh
16+
}
17+
18+
var conversion = Types.Matrix{ // coordinate system conversion matrix
19+
X00: 1, X01: 0, X02: 0, X03: 0,
20+
X10: 0, X11: 0, X12: -1, X13: 0,
21+
X20: 0, X21: 1, X22: 0, X23: 0,
22+
X30: 0, X31: 0, X32: 0, X33: 1,
23+
}
24+
1425
func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
1526
var doc gltf.Document
1627
gltf.NewDecoder(file).Decode(&doc)
@@ -19,40 +30,33 @@ func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
1930

2031
transformationMatrices := map[int]Types.Matrix{}
2132

22-
for _, node := range doc.Nodes {
23-
if node.Mesh == nil {
24-
continue
25-
}
26-
matrix := node.MatrixOrDefault()
27-
if matrix != gltf.DefaultMatrix {
28-
transformationMatrices[*node.Mesh] = Types.Matrix{
29-
X00: matrix[0], X01: matrix[4], X02: matrix[8], X03: matrix[12],
30-
X10: matrix[1], X11: matrix[5], X12: matrix[9], X13: matrix[13],
31-
X20: matrix[2], X21: matrix[6], X22: matrix[10], X23: matrix[14],
32-
X30: matrix[3], X31: matrix[7], X32: matrix[11], X33: matrix[15],
33-
}
34-
} else {
35-
transformationMatrices[*node.Mesh] = gltfParseScaleRotationTranslation(node.RotationOrDefault(), node.ScaleOrDefault(), node.TranslationOrDefault())
33+
gltfNodes := map[int]gltfNode{}
34+
35+
for _, scene := range doc.Scenes {
36+
for _, node := range scene.Nodes {
37+
handleGLTFNode(gltfNodes, doc, node, transformationMatrices, Types.IdentityMatrix())
3638
}
3739
}
3840

41+
// TODO: refine this as pure rotation breaks bounding box -> possibly rotate all 8 outer points of bounding box and redetermine
3942
// calculate outer dimensions
40-
min := Types.Vector{}
41-
max := Types.Vector{}
42-
for meshindex, m := range doc.Meshes {
43+
min := Types.Vector{X: math.Inf(1), Y: math.Inf(1), Z: math.Inf(1)}
44+
max := Types.Vector{X: math.Inf(-1), Y: math.Inf(-1), Z: math.Inf(-1)}
45+
for node_index, node := range gltfNodes {
46+
m := node.mesh
4347
for _, p := range m.Primitives {
4448
// contains Min and Max attr (for dimension calc)
4549
posAccessor := doc.Accessors[p.Attributes[gltf.POSITION]]
4650
// do transformation before getting the outer dimensions
47-
tempMin := transformationMatrices[meshindex].MulPosition(Types.Vector{
51+
tempMin := transformationMatrices[node_index].MulPosition(Types.Vector{
4852
X: posAccessor.Min[0],
49-
Y: posAccessor.Min[2],
50-
Z: posAccessor.Min[1],
53+
Y: posAccessor.Min[1],
54+
Z: posAccessor.Min[2],
5155
})
52-
tempMax := transformationMatrices[meshindex].MulPosition(Types.Vector{
56+
tempMax := transformationMatrices[node_index].MulPosition(Types.Vector{
5357
X: posAccessor.Max[0],
54-
Y: posAccessor.Max[2],
55-
Z: posAccessor.Max[1],
58+
Y: posAccessor.Max[1],
59+
Z: posAccessor.Max[2],
5660
})
5761
min = min.Min(tempMin)
5862
max = max.Max(tempMax)
@@ -62,20 +66,26 @@ func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
6266
scaling := Types.Vector{X: 1, Y: 1, Z: 1}
6367
if desiredSize != nil {
6468
scaling = desiredSize.Div(max.Sub(min))
69+
// following is a temporary fix, TODO: remove once bounding box is refined
70+
scaling.X = math.Abs(scaling.X)
71+
scaling.Y = math.Abs(scaling.Y)
72+
scaling.Z = math.Abs(scaling.Z)
6573
}
6674

67-
for meshindex, m := range doc.Meshes {
75+
for _, node := range gltfNodes {
76+
m := node.mesh
77+
determinant := node.matrix.Determinant()
6878
for _, p := range m.Primitives {
6979
posAccessor := doc.Accessors[p.Attributes[gltf.POSITION]]
70-
positions, err := gltfVec3(&doc, posAccessor, transformationMatrices[meshindex], scaling)
80+
positions, err := gltfVec3(&doc, posAccessor, node.matrix, scaling)
7181
if err != nil {
7282
return nil, err
7383
}
7484

7585
var normals []Types.Vector
7686
if nIdx, ok := p.Attributes[gltf.NORMAL]; ok {
7787
normalAccessor := doc.Accessors[nIdx]
78-
normals, err = gltfVec3(&doc, normalAccessor, transformationMatrices[meshindex], Types.Vector{X: 1, Y: 1, Z: 1})
88+
normals, err = gltfVec3(&doc, normalAccessor, node.matrix, Types.Vector{X: 1, Y: 1, Z: 1})
7989
if err != nil {
8090
return nil, err
8191
}
@@ -103,7 +113,11 @@ func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
103113
v1 := positions[indices[i+1]].ToVertex(n1)
104114
v2 := positions[indices[i+2]].ToVertex(n2)
105115

106-
mesh.AddTriangle(Types.Triangle{V0: v0, V1: v1, V2: v2})
116+
if determinant < 0 {
117+
mesh.AddTriangle(Types.Triangle{V0: v0, V1: v2, V2: v1})
118+
} else {
119+
mesh.AddTriangle(Types.Triangle{V0: v0, V1: v1, V2: v2})
120+
}
107121
}
108122

109123
meshes.Add(&mesh)
@@ -113,6 +127,33 @@ func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
113127
return meshes, nil
114128
}
115129

130+
func handleGLTFNode(nodes map[int]gltfNode, doc gltf.Document, node_id int, transformationMatrices map[int]Types.Matrix, parentMatrix Types.Matrix) {
131+
node := doc.Nodes[node_id]
132+
gltf_matrix := node.MatrixOrDefault()
133+
var matrix Types.Matrix = parentMatrix
134+
if gltf_matrix != gltf.DefaultMatrix {
135+
matrix = matrix.Mul(Types.Matrix{
136+
X00: gltf_matrix[0], X01: gltf_matrix[4], X02: gltf_matrix[8], X03: gltf_matrix[12],
137+
X10: gltf_matrix[1], X11: gltf_matrix[5], X12: gltf_matrix[9], X13: gltf_matrix[13],
138+
X20: gltf_matrix[2], X21: gltf_matrix[6], X22: gltf_matrix[10], X23: gltf_matrix[14],
139+
X30: gltf_matrix[3], X31: gltf_matrix[7], X32: gltf_matrix[11], X33: gltf_matrix[15],
140+
})
141+
} else {
142+
matrix = matrix.Mul(gltfParseScaleRotationTranslation(node.RotationOrDefault(), node.ScaleOrDefault(), node.TranslationOrDefault()))
143+
}
144+
if node.Mesh != nil {
145+
world_matrix := conversion.Mul(matrix)
146+
transformationMatrices[node_id] = world_matrix
147+
nodes[node_id] = gltfNode{
148+
matrix: world_matrix,
149+
mesh: doc.Meshes[*node.Mesh],
150+
}
151+
}
152+
for _, child_node := range node.Children {
153+
handleGLTFNode(nodes, doc, child_node, transformationMatrices, matrix)
154+
}
155+
}
156+
116157
func gltfVec3(doc *gltf.Document, acc *gltf.Accessor, transformationMatrix Types.Matrix, scaling Types.Vector) ([]Types.Vector, error) {
117158
bufView := doc.BufferViews[*acc.BufferView]
118159
buffer := doc.Buffers[bufView.Buffer]
@@ -124,18 +165,12 @@ func gltfVec3(doc *gltf.Document, acc *gltf.Accessor, transformationMatrix Types
124165
vectors := make([]Types.Vector, acc.Count)
125166
for i := 0; i < acc.Count; i++ {
126167
base := i * 12
127-
// axes inverted to convert to correct coordinate system
128168
vec := Types.Vector{
129169
X: float64(math.Float32frombits(binary.LittleEndian.Uint32(raw[base+0:]))),
130170
Y: float64(math.Float32frombits(binary.LittleEndian.Uint32(raw[base+4:]))),
131171
Z: float64(math.Float32frombits(binary.LittleEndian.Uint32(raw[base+8:]))),
132172
}
133-
transformed := transformationMatrix.MulPosition(vec)
134-
scaled := Types.Vector{
135-
X: transformed.X,
136-
Y: -transformed.Z,
137-
Z: transformed.Y,
138-
}.Mult(scaling)
173+
scaled := transformationMatrix.MulPosition(vec).Mult(scaling)
139174
vectors[i] = scaled // vec.Mult(scaling)
140175
}
141176

@@ -170,8 +205,22 @@ func gltfIndices(doc *gltf.Document, acc *gltf.Accessor) ([]int, error) {
170205
return out, nil
171206
}
172207

173-
func gltfParseScaleRotationTranslation(rotation [4]float64, scale [3]float64, translation [3]float64) MeshTypes.Matrix {
174-
return MeshTypes.Matrix{
208+
func normalizeQuaternion(q [4]float64) [4]float64 {
209+
// Calculate mag squared
210+
magSq := q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]
211+
212+
// Check for near-zero
213+
if magSq < 1e-12 {
214+
return [4]float64{0, 0, 0, 1}
215+
}
216+
217+
mag := math.Sqrt(magSq)
218+
return [4]float64{q[0] / mag, q[1] / mag, q[2] / mag, q[3] / mag}
219+
}
220+
221+
func gltfParseScaleRotationTranslation(rotation [4]float64, scale [3]float64, translation [3]float64) Types.Matrix {
222+
rotation = normalizeQuaternion(rotation)
223+
return Types.Matrix{
175224
X00: (1 - 2*(rotation[1]*rotation[1]+rotation[2]*rotation[2])) * scale[0], X01: 2 * (rotation[0]*rotation[1] - rotation[3]*rotation[2]) * scale[1], X02: 2 * (rotation[0]*rotation[2] + rotation[3]*rotation[1]) * scale[2], X03: translation[0],
176225
X10: 2 * (rotation[0]*rotation[1] + rotation[3]*rotation[2]) * scale[0], X11: (1 - 2*(rotation[0]*rotation[0]+rotation[2]*rotation[2])) * scale[1], X12: 2 * (rotation[1]*rotation[2] - rotation[3]*rotation[0]) * scale[2], X13: translation[1],
177226
X20: 2 * (rotation[0]*rotation[2] - rotation[3]*rotation[1]) * scale[0], X21: 2 * (rotation[1]*rotation[2] + rotation[3]*rotation[0]) * scale[1], X22: (1 - 2*(rotation[0]*rotation[0]+rotation[1]*rotation[1])) * scale[2], X23: translation[2],

0 commit comments

Comments
 (0)