@@ -10,6 +10,18 @@ import (
1010 "github.com/qmuntal/gltf"
1111)
1212
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+
1325func LoadGLTF (file io.Reader , desiredSize * Types.Vector ) (* Types.Mesh , error ) {
1426 var doc gltf.Document
1527 gltf .NewDecoder (file ).Decode (& doc )
@@ -18,39 +30,48 @@ func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
1830
1931 transformationMatrices := map [int ]Types.Matrix {}
2032
21- for _ , node := range doc .Nodes {
22- if node .Mesh == nil {
23- continue
24- }
25- matrix := node .MatrixOrDefault ()
26- transformationMatrices [* node .Mesh ] = Types.Matrix {
27- X00 : matrix [0 ], X01 : matrix [4 ], X02 : matrix [8 ], X03 : matrix [12 ],
28- X10 : matrix [1 ], X11 : matrix [5 ], X12 : matrix [9 ], X13 : matrix [13 ],
29- X20 : matrix [2 ], X21 : matrix [6 ], X22 : matrix [10 ], X23 : matrix [14 ],
30- X30 : matrix [3 ], X31 : matrix [7 ], X32 : matrix [11 ], X33 : matrix [15 ],
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 ())
3138 }
3239 }
3340
3441 // calculate outer dimensions
35- min := Types.Vector {}
36- max := Types.Vector {}
37- for meshindex , m := range doc .Meshes {
42+ min := Types.Vector {X : math .Inf (1 ), Y : math .Inf (1 ), Z : math .Inf (1 )}
43+ max := Types.Vector {X : math .Inf (- 1 ), Y : math .Inf (- 1 ), Z : math .Inf (- 1 )}
44+ for node_index , node := range gltfNodes {
45+ m := node .mesh
3846 for _ , p := range m .Primitives {
3947 // contains Min and Max attr (for dimension calc)
4048 posAccessor := doc.Accessors [p.Attributes [gltf.POSITION ]]
41- // do transformation before getting the outer dimensions
42- tempMin := transformationMatrices [meshindex ].MulPosition (Types.Vector {
49+ tempMin := Types.Vector {
4350 X : posAccessor .Min [0 ],
44- Y : posAccessor .Min [2 ],
45- Z : posAccessor .Min [1 ],
46- })
47- tempMax := transformationMatrices [ meshindex ]. MulPosition ( Types.Vector {
51+ Y : posAccessor .Min [1 ],
52+ Z : posAccessor .Min [2 ],
53+ }
54+ tempMax := Types.Vector {
4855 X : posAccessor .Max [0 ],
49- Y : posAccessor .Max [2 ],
50- Z : posAccessor .Max [1 ],
51- })
52- min = min .Min (tempMin )
53- max = max .Max (tempMax )
56+ Y : posAccessor .Max [1 ],
57+ Z : posAccessor .Max [2 ],
58+ }
59+ // determine all outer points as min/max might switch due to transformation
60+ outer_points := [8 ]Types.Vector {
61+ {X : tempMin .X , Y : tempMax .Y , Z : tempMax .Z }, // up front left
62+ {X : tempMin .X , Y : tempMax .Y , Z : tempMin .Z }, // up back left
63+ {X : tempMin .X , Y : tempMin .Y , Z : tempMax .Z }, // down front left
64+ {X : tempMin .X , Y : tempMin .Y , Z : tempMin .Z }, // down back left
65+ {X : tempMax .X , Y : tempMax .Y , Z : tempMax .Z }, // up front right
66+ {X : tempMax .X , Y : tempMax .Y , Z : tempMin .Z }, // up back right
67+ {X : tempMax .X , Y : tempMin .Y , Z : tempMax .Z }, // down front right
68+ {X : tempMax .X , Y : tempMin .Y , Z : tempMin .Z }, // down back right
69+ }
70+ for _ , vec := range outer_points {
71+ vec = transformationMatrices [node_index ].MulPosition (vec )
72+ min = min .Min (vec )
73+ max = max .Max (vec )
74+ }
5475 }
5576 }
5677
@@ -59,18 +80,20 @@ func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
5980 scaling = desiredSize .Div (max .Sub (min ))
6081 }
6182
62- for meshindex , m := range doc .Meshes {
83+ for _ , node := range gltfNodes {
84+ m := node .mesh
85+ determinant := node .matrix .Determinant ()
6386 for _ , p := range m .Primitives {
6487 posAccessor := doc.Accessors [p.Attributes [gltf.POSITION ]]
65- positions , err := gltfVec3 (& doc , posAccessor , transformationMatrices [ meshindex ] , scaling )
88+ positions , err := gltfVec3 (& doc , posAccessor , node . matrix , scaling )
6689 if err != nil {
6790 return nil , err
6891 }
6992
7093 var normals []Types.Vector
7194 if nIdx , ok := p .Attributes [gltf .NORMAL ]; ok {
7295 normalAccessor := doc .Accessors [nIdx ]
73- normals , err = gltfVec3 (& doc , normalAccessor , transformationMatrices [ meshindex ] , Types.Vector {X : 1 , Y : 1 , Z : 1 })
96+ normals , err = gltfVec3 (& doc , normalAccessor , node . matrix , Types.Vector {X : 1 , Y : 1 , Z : 1 })
7497 if err != nil {
7598 return nil , err
7699 }
@@ -98,7 +121,11 @@ func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
98121 v1 := positions [indices [i + 1 ]].ToVertex (n1 )
99122 v2 := positions [indices [i + 2 ]].ToVertex (n2 )
100123
101- mesh .AddTriangle (Types.Triangle {V0 : v0 , V1 : v1 , V2 : v2 })
124+ if determinant < 0 {
125+ mesh .AddTriangle (Types.Triangle {V0 : v0 , V1 : v2 , V2 : v1 })
126+ } else {
127+ mesh .AddTriangle (Types.Triangle {V0 : v0 , V1 : v1 , V2 : v2 })
128+ }
102129 }
103130
104131 meshes .Add (& mesh )
@@ -108,6 +135,33 @@ func LoadGLTF(file io.Reader, desiredSize *Types.Vector) (*Types.Mesh, error) {
108135 return meshes , nil
109136}
110137
138+ func handleGLTFNode (nodes map [int ]gltfNode , doc gltf.Document , node_id int , transformationMatrices map [int ]Types.Matrix , parentMatrix Types.Matrix ) {
139+ node := doc .Nodes [node_id ]
140+ gltf_matrix := node .MatrixOrDefault ()
141+ var matrix Types.Matrix = parentMatrix
142+ if gltf_matrix != gltf .DefaultMatrix {
143+ matrix = matrix .Mul (Types.Matrix {
144+ X00 : gltf_matrix [0 ], X01 : gltf_matrix [4 ], X02 : gltf_matrix [8 ], X03 : gltf_matrix [12 ],
145+ X10 : gltf_matrix [1 ], X11 : gltf_matrix [5 ], X12 : gltf_matrix [9 ], X13 : gltf_matrix [13 ],
146+ X20 : gltf_matrix [2 ], X21 : gltf_matrix [6 ], X22 : gltf_matrix [10 ], X23 : gltf_matrix [14 ],
147+ X30 : gltf_matrix [3 ], X31 : gltf_matrix [7 ], X32 : gltf_matrix [11 ], X33 : gltf_matrix [15 ],
148+ })
149+ } else {
150+ matrix = matrix .Mul (gltfParseScaleRotationTranslation (node .RotationOrDefault (), node .ScaleOrDefault (), node .TranslationOrDefault ()))
151+ }
152+ if node .Mesh != nil {
153+ world_matrix := conversion .Mul (matrix )
154+ transformationMatrices [node_id ] = world_matrix
155+ nodes [node_id ] = gltfNode {
156+ matrix : world_matrix ,
157+ mesh : doc .Meshes [* node .Mesh ],
158+ }
159+ }
160+ for _ , child_node := range node .Children {
161+ handleGLTFNode (nodes , doc , child_node , transformationMatrices , matrix )
162+ }
163+ }
164+
111165func gltfVec3 (doc * gltf.Document , acc * gltf.Accessor , transformationMatrix Types.Matrix , scaling Types.Vector ) ([]Types.Vector , error ) {
112166 bufView := doc .BufferViews [* acc .BufferView ]
113167 buffer := doc .Buffers [bufView .Buffer ]
@@ -119,18 +173,12 @@ func gltfVec3(doc *gltf.Document, acc *gltf.Accessor, transformationMatrix Types
119173 vectors := make ([]Types.Vector , acc .Count )
120174 for i := 0 ; i < acc .Count ; i ++ {
121175 base := i * 12
122- // axes inverted to convert to correct coordinate system
123176 vec := Types.Vector {
124177 X : float64 (math .Float32frombits (binary .LittleEndian .Uint32 (raw [base + 0 :]))),
125178 Y : float64 (math .Float32frombits (binary .LittleEndian .Uint32 (raw [base + 4 :]))),
126179 Z : float64 (math .Float32frombits (binary .LittleEndian .Uint32 (raw [base + 8 :]))),
127180 }
128- transformed := transformationMatrix .MulPosition (vec )
129- scaled := Types.Vector {
130- X : transformed .X ,
131- Y : - transformed .Z ,
132- Z : transformed .Y ,
133- }.Mult (scaling )
181+ scaled := transformationMatrix .MulPosition (vec ).Mult (scaling )
134182 vectors [i ] = scaled // vec.Mult(scaling)
135183 }
136184
@@ -164,3 +212,26 @@ func gltfIndices(doc *gltf.Document, acc *gltf.Accessor) ([]int, error) {
164212
165213 return out , nil
166214}
215+
216+ func normalizeQuaternion (q [4 ]float64 ) [4 ]float64 {
217+ // Calculate mag squared
218+ magSq := q [0 ]* q [0 ] + q [1 ]* q [1 ] + q [2 ]* q [2 ] + q [3 ]* q [3 ]
219+
220+ // Check for near-zero
221+ if magSq < 1e-12 {
222+ return [4 ]float64 {0 , 0 , 0 , 1 }
223+ }
224+
225+ mag := math .Sqrt (magSq )
226+ return [4 ]float64 {q [0 ] / mag , q [1 ] / mag , q [2 ] / mag , q [3 ] / mag }
227+ }
228+
229+ func gltfParseScaleRotationTranslation (rotation [4 ]float64 , scale [3 ]float64 , translation [3 ]float64 ) Types.Matrix {
230+ rotation = normalizeQuaternion (rotation )
231+ return Types.Matrix {
232+ 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 ],
233+ 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 ],
234+ 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 ],
235+ X30 : 0 , X31 : 0 , X32 : 0 , X33 : 1 ,
236+ }
237+ }
0 commit comments