@@ -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+
1425func 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+
116157func 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