ecere/gfx/3D/Object: computeLightVector flag to avoid always recomputing uselessly
[sdk] / ecere / src / gfx / 3D / Object.ec
1 namespace gfx3D;
2
3 import "Display"
4
5 public enum FrustumPlacement { outside, inside, intersecting };
6
7 public class ObjectFormat
8 {
9    class_data const char * extension;
10    class_property const char * extension
11    {
12       set { class_data(extension) = value; }
13       get { return class_data(extension); }
14    }
15
16    virtual bool ::Load(Object object, const char * fileName, DisplaySystem displaySystem);
17 };
18
19 // TODO: Review these:
20 public class ObjectFlags
21 {
22 public:
23    bool root:1, viewSpace:1, ownMesh:1, translucent:1, flipWindings:1, keysLoaded:1, transform:1, mesh:1, light:1, camera:1, localMatrixSet:1;
24    bool computeLightVectors:1;
25    int hierarchy:16:16;
26 };
27
28 public struct Transform
29 {
30    Vector3D position;
31    Quaternion orientation;
32    Vector3Df scaling;
33 };
34
35 /*static float ease(float t, float a, float b)
36 {
37    float k;
38    float s = a + b;
39
40    if (s == 0.0f) return t;
41    if (s > 1.0f)
42    {
43       a /= s;
44       b /= s;
45    }
46    k = 1.0f/(2.0f-a-b);
47    if (t < a) return (k/a)*t*t;
48    if (t < 1.0f - b) return k*(2.0f * t - a);
49    t = 1.0f - t;
50    return 1.0f - (k/b)*t*t;
51 }*/
52
53 public enum FrameTrackType : uint16 { position = 1, rotation, scaling, fov, roll, colorChange, morph, hotSpot, fallOff, hide };
54
55 public class FrameTrackBits
56 {
57    FrameTrackType type;
58    bool loop:1;
59 };
60
61 public struct FrameKey
62 {
63    unsigned int frame;
64    float tension, continuity, bias;
65    float easeFrom, easeTo;
66    union
67    {
68       Vector3Df position;
69       Quaternion orientation;
70       Vector3Df scaling;
71       float roll;
72       float fov;
73       ColorRGB color;
74       float hotSpot;
75       float fallOff;
76    };
77 };
78
79 enum SplinePart { splinePoint, splineA, splineB };
80
81 public class FrameTrack : struct
82 {
83    FrameTrack prev, next;
84    FrameTrackBits type;
85    unsigned int numKeys;
86    FrameKey * keys;
87
88    void Free(void)
89    {
90       delete keys;
91    }
92
93    ~FrameTrack()
94    {
95       Free();
96    }
97
98    float GetFloat(SplinePart what, unsigned int n)
99    {
100       float value;
101       FrameKey *kn_1, *kn, *kn1;
102       float pn_1, pn, pn1;
103       int d1, d2;
104
105       kn = &keys[n];
106       pn = kn->roll;
107
108       if(what == splinePoint)
109          value = pn;
110       else
111       {
112          if(n == 0)
113          {
114             kn1 = &keys[1];
115             pn1 = kn1->roll;
116
117             if(numKeys == 2)
118             {
119                value = pn1 - pn;
120                value *= 1.0f - kn->tension;
121                return value;
122             }
123             if(type.loop)
124             {
125                kn_1 = &keys[numKeys-2];
126                d1 = keys[numKeys-1].frame - kn_1->frame;
127                d2 = kn1->frame - kn->frame;
128             }
129             else
130             {
131                float a1;
132                value = pn1 - pn;
133                value *= 1.5f;
134
135                a1 = GetFloat(splineA, 1);
136                a1 *= 0.5f;
137
138                value -= a1;
139                value *= 1.0f - kn->tension;
140                return value;
141             }
142          }
143          else if(n == numKeys-1)
144          {
145             kn_1 = &keys[n-1];
146             pn_1 = kn_1->roll;
147
148             if(numKeys == 2)
149             {
150                value = pn - pn_1;
151                value *= 1.0f - kn->tension;
152                return value;
153             }
154             if(type.loop)
155             {
156                kn1 = &keys[1];
157                d1 = kn->frame - kn_1->frame;
158                d2 = kn1->frame - keys[0].frame;
159             }
160             else
161             {
162                float bn_1;
163                value = pn - pn_1;
164                value *= 1.5f;
165
166                bn_1 = GetFloat(splineB, n-1);
167                bn_1 *= 0.5f;
168
169                value -= bn_1;
170                value *= 1.0f - kn->tension;
171                return value;
172             }
173          }
174          else
175          {
176             kn_1 = &keys[n-1];
177             kn1 = &keys[n+1];
178             d1 = kn->frame - kn_1->frame;
179             d2 = kn1->frame - kn->frame;
180          }
181          {
182             float C, adjust;
183             float part1, part2;
184
185             pn_1 = kn_1->roll;
186             pn1 = kn1->roll;
187
188             if(what == splineA)
189             {
190                C = kn->continuity;
191                adjust = (float)d1;
192             }
193             else
194             {
195                C = -kn->continuity;
196                adjust = (float)d2;
197             }
198             adjust /= d1 + d2;
199             adjust = 0.5f + (1.0f - Abs(C))*(adjust - 0.5f);
200
201             part1 = pn - pn_1;
202             part1 *= (1.0f + kn->bias)*(1.0f - C);
203
204             part2 = pn1 - pn;
205             part2 *= (1.0f - kn->bias)*(1.0f + C);
206
207             value = part1 + part2;
208             value *= (1.0f - kn->tension)*adjust;
209          }
210       }
211       return value;
212    }
213
214    void GetVector(Vector3Df vector, SplinePart what, unsigned int n)
215    {
216       FrameKey *kn_1, *kn, *kn1;
217       Vector3Df *pn_1, *pn, *pn1;
218       int d1, d2;
219
220       kn = &keys[n];
221       pn = &kn->position;
222
223       if(what == splinePoint)
224          vector = *pn;
225       else
226       {
227          if(n == 0)
228          {
229             kn1 = &keys[1];
230             pn1 = &kn1->position;
231
232             if(numKeys == 2)
233             {
234                vector.Subtract(pn1, pn);
235                vector.Scale(vector, 1.0f - kn->tension);
236                return;
237             }
238             if(type.loop)
239             {
240                kn_1 = &keys[numKeys-2];
241                d1 = keys[numKeys-1].frame - kn_1->frame;
242                d2 = kn1->frame - kn->frame;
243             }
244             else
245             {
246                Vector3Df a1;
247                vector.Subtract(pn1, pn);
248                vector.Scale(vector, 1.5f);
249
250                GetVector(a1, splineA, 1);
251                a1.Scale(a1, 0.5f);
252
253                vector.Subtract(vector, a1);
254                vector.Scale(vector, 1.0f - kn->tension);
255                return;
256             }
257          }
258          else if(n == numKeys-1)
259          {
260             kn_1 = &keys[n-1];
261             pn_1 = &kn_1->position;
262
263             if(numKeys == 2)
264             {
265                vector.Subtract(pn, pn_1);
266                vector.Scale(vector, 1.0f - kn->tension);
267                return;
268             }
269             if(type.loop)
270             {
271                kn1 = &keys[1];
272                d1 = kn->frame - kn_1->frame;
273                d2 = kn1->frame - keys[0].frame;
274             }
275             else
276             {
277                Vector3Df bn_1;
278                vector.Subtract(pn, pn_1);
279                vector.Scale(vector, 1.5f);
280
281                GetVector(&bn_1, splineB, n-1);
282                bn_1.Scale(bn_1, 0.5f);
283
284                vector.Subtract(vector, bn_1);
285                vector.Scale(vector, 1.0f - kn->tension);
286                return;
287             }
288          }
289          else
290          {
291             kn_1 = &keys[n-1];
292             kn1 = &keys[n+1];
293             d1 = kn->frame - kn_1->frame;
294             d2 = kn1->frame - kn->frame;
295          }
296          {
297             float C, adjust;
298             Vector3Df part1, part2;
299
300             pn_1 = &kn_1->position;
301             pn1 = &kn1->position;
302
303             if(what == splineA)
304             {
305                C = kn->continuity;
306                adjust = (float)d1;
307             }
308             else
309             {
310                C = -kn->continuity;
311                adjust = (float)d2;
312             }
313             adjust /= d1 + d2;
314             adjust = 0.5f + (1.0f - Abs(C))*(adjust - 0.5f);
315
316             part1.Subtract(pn, pn_1);
317             part1.Scale(part1, (1.0f + kn->bias)*(1.0f - C));
318
319             part2.Subtract(pn1, pn);
320             part2.Scale(part2, (1.0f - kn->bias)*(1.0f + C));
321
322             vector.Add(part1, part2);
323             vector.Scale(vector, (1.0f - kn->tension)*adjust);
324          }
325       }
326    }
327
328    void GetQuaternion(Quaternion quat, SplinePart what, unsigned int n)
329    {
330       FrameKey *kn_1, *kn, *kn1;
331       Quaternion *qn_1, *qn, *qn1;
332       int d1, d2;
333
334       kn = &keys[n];
335       qn = &kn->orientation;
336
337       if (what == splinePoint)
338          quat = *qn;
339       else
340       {
341          if(n == 0)
342          {
343             kn1 = &keys[1];
344
345             if (!(type.loop) || numKeys <= 2)
346             {
347                qn1 = &kn1->orientation;
348                quat.Slerp(qn, qn1, (1.0f - kn->tension)*(1.0f + kn->continuity*kn->bias)/3.0f);
349                return;
350             }
351             else
352             {
353                kn_1= &keys[numKeys-2];
354                d1 = keys[numKeys-1].frame - kn_1->frame;
355                d2 = kn1->frame - kn->frame;
356             }
357          }
358          else if (n == numKeys-1)
359          {
360             kn_1 = &keys[n-1];
361
362             if (!(type.loop) || numKeys <= 2)
363             {
364                qn_1 = &kn_1->orientation;
365                quat.Slerp(qn, qn_1, (1.0f - kn->tension)*(1.0f - kn->continuity*kn->bias)/3.0f);
366                return;
367             }
368             else
369             {
370                kn1 = &keys[1];
371                d1 = kn->frame - kn_1->frame;
372                d2 = kn1->frame - keys[0].frame;
373             }
374          }
375          else
376          {
377             kn_1 = &keys[n-1];
378             kn1 = &keys[n+1];
379             d1 = kn->frame - kn_1->frame;
380             d2 = kn1->frame - kn->frame;
381          }
382          {
383             float f, adjust;
384             Quaternion g1, g2, tmp;
385
386             qn_1 = &kn_1->orientation;
387             qn1 = &kn1->orientation;
388
389             if (what == splineA)
390             {
391                f = 1.0f;
392                adjust = (float)d1;
393             }
394             else
395             {
396                f = -1.0f;
397                adjust = (float)d2;
398             }
399             adjust /= d1 + d2;
400             adjust = 0.5f + (1.0f - Abs(kn->continuity))*(adjust - 0.5f);
401
402             g1.Slerp(qn, qn_1,-(1.0f + kn->bias)/3.0f);
403             g2.Slerp(qn, qn1 , (1.0f - kn->bias)/3.0f);
404             tmp.Slerp(g1, g2, 0.5f + f*0.5f*kn->continuity);
405             quat.Slerp(qn, &tmp, f*(kn->tension - 1.0f)*adjust*2.0f);
406          }
407       }
408    }
409
410    void Interpolate(Vector3Df vector, Vector3Df prevVector, Vector3Df nextVector, int prev, int next, float t)
411    {
412       if(!t)
413          vector = prevVector;
414       else
415       {
416          Vector3Df p1 = prevVector, p2 = nextVector;
417          Vector3Df r1, r2;
418
419          GetVector(r1, splineB, prev);
420          GetVector(r2, splineA, next);
421
422          p1.Scale(p1, 2*t*t*t - 3*t*t + 1);
423          p2.Scale(p2,-2*t*t*t + 3*t*t);
424
425          r1.Scale(r1, t*t*t - 2*t*t + t);
426          r2.Scale(r2, t*t*t -   t*t);
427
428          vector = p1;
429          vector.Add(vector, r1);
430          vector.Add(vector, p2);
431          vector.Add(vector, r2);
432       }
433    }
434
435    void InterpolateQuat(Quaternion quat, Quaternion prevQuat, Quaternion nextQuat, int prev, int next, float t)
436    {
437       if(!t)
438          quat = prevQuat;
439       else
440       {
441          Quaternion a, b;
442          Quaternion q0, q1, q2;
443
444          GetQuaternion(b, splineB, prev);
445          GetQuaternion(a, splineA, next);
446
447          q0.Slerp(prevQuat, b, t);
448          q1.Slerp(b, a, t);
449          q2.Slerp(a, nextQuat, t);
450
451          q0.Slerp(q0, q1, t);
452          q1.Slerp(q1, q2, t);
453
454          quat.Slerp(q0, q1, t);
455       }
456    }
457
458    float InterpolateFloat(float prevValue, float nextValue, int prev, int next, float t)
459    {
460       float value;
461       if(!t)
462          value = prevValue;
463       else
464       {
465          float p1 = prevValue, p2 = nextValue;
466
467          float r1 = GetFloat(splineB, prev);
468          float r2 = GetFloat(splineA, next);
469
470          p1 *= 2*t*t*t - 3*t*t + 1;
471          p2 *= -2*t*t*t + 3*t*t;
472
473          r1 *= t*t*t - 2*t*t + t;
474          r2 *= t*t*t -   t*t;
475
476          value = p1 + r1 + p2 + r2;
477       }
478       return value;
479    }
480 };
481
482 static bool FindMaterialAndType(Mesh mesh, Material material, PrimitiveGroupType type)
483 {
484    PrimitiveGroup group;
485    for(group = mesh.groups.first; group; group = group.next)
486       if(group.material == material && group.type == type)
487          return true;
488    return false;
489 }
490
491 public class Object : struct
492 {
493 public:
494    void SetMinMaxRadius(bool processMesh)
495    {
496       Object child;
497
498       if(flags.mesh && mesh)
499       {
500          if(processMesh)
501             mesh.SetMinMaxRadius();
502          min = mesh.min;
503          max = mesh.max;
504          volume = (max.x >= min.x && max.y >= min.y && max.z >= min.z);
505       }
506       else
507       {
508          min = { MAXFLOAT, MAXFLOAT, MAXFLOAT };
509          max = { -MAXFLOAT, -MAXFLOAT, -MAXFLOAT };
510          volume = false;
511       }
512       for(child = children.first; child; child = child.next)
513       {
514          child.SetMinMaxRadius(processMesh);
515
516          if(child.volume)
517          {
518             // Child Local + Child Object Transform
519             Vector3Df points[8] =
520             {
521                { child.min.x, child.min.y, child.min.z },
522                { child.min.x, child.min.y, child.max.z },
523                { child.min.x, child.max.y, child.min.z },
524                { child.min.x, child.max.y, child.max.z },
525                { child.max.x, child.min.y, child.min.z },
526                { child.max.x, child.min.y, child.max.z },
527                { child.max.x, child.max.y, child.min.z },
528                { child.max.x, child.max.y, child.max.z }
529             };
530             int c;
531             for(c = 0; c<8; c++)
532             {
533                Vector3Df point;
534                point.MultMatrix(points[c], child.localMatrix);
535
536                if(point.x < min.x) min.x = point.x;
537                if(point.y < min.y) min.y = point.y;
538                if(point.z < min.z) min.z = point.z;
539
540                if(point.x > max.x) max.x = point.x;
541                if(point.y > max.y) max.y = point.y;
542                if(point.z > max.z) max.z = point.z;
543             }
544             volume = true;
545          }
546       }
547
548       if(volume)
549       {
550          Vector3Df points[8] =
551          {
552             { min.x, min.y, min.z },
553             { min.x, min.y, max.z },
554             { min.x, max.y, min.z },
555             { min.x, max.y, max.z },
556             { max.x, min.y, min.z },
557             { max.x, min.y, max.z },
558             { max.x, max.y, min.z },
559             { max.x, max.y, max.z }
560          };
561          Vector3Df halfExtent;
562          Vector3D halfExtentd;
563
564          // Local
565          center.Add(min, max);
566          center.Scale(center, 0.5f);
567          halfExtent.Subtract(max, min);
568          halfExtent.Scale(halfExtent, 0.5f);
569          radius = halfExtent.length;
570
571          // World
572          {
573             Vector3D min { MAXFLOAT, MAXFLOAT, MAXFLOAT };
574             Vector3D max { -MAXFLOAT, -MAXFLOAT, -MAXFLOAT };
575             int c;
576             for(c = 0; c<8; c++)
577             {
578                Vector3D point;
579                point.MultMatrixf(points[c], matrix);
580
581                if(point.x < min.x) min.x = point.x;
582                if(point.y < min.y) min.y = point.y;
583                if(point.z < min.z) min.z = point.z;
584
585                if(point.x > max.x) max.x = point.x;
586                if(point.y > max.y) max.y = point.y;
587                if(point.z > max.z) max.z = point.z;
588             }
589             wmin = min;
590             wmax = max;
591          }
592          wcenter.Add(wmin, wmax);
593          wcenter.Scale(wcenter, 0.5f);
594
595          halfExtentd.Subtract(wmax, wmin);
596          halfExtentd.Scale(halfExtentd, 0.5);
597          wradius = halfExtentd.length;
598       }
599    }
600
601    void Duplicate(Object model)
602    {
603       if(model)
604       {
605          Object modelChild;
606
607          name = CopyString(model.name);
608          flags = model.flags;
609          flags.ownMesh = false;
610          flags.transform = false;
611          mesh = model.mesh;
612          /*
613          min = model.min;
614          max = model.max;
615          radius = model.radius;
616          */
617          if(parent && !flags.root)
618             matrix.Multiply(localMatrix, parent.matrix);
619          else
620             matrix = localMatrix;
621
622          for(modelChild = model.children.first; modelChild; modelChild = modelChild.next)
623          {
624             Object child { parent = this };
625             child.localMatrix = modelChild.localMatrix;
626             child.transform = modelChild.transform;
627             child.Duplicate(modelChild);
628             children.AddName(child);
629          }
630       }
631    }
632
633    void Free(DisplaySystem displaySystem)
634    {
635       if(this)
636       {
637          Object child;
638
639          while((child = children.first))
640          {
641             children.Remove(child);
642             child.Free(displaySystem);
643
644             // We did not do this before so as to keep transform on reloading for new DisplaySystem
645             // However since children are removed, it seems that purpose has been gone, we'd need
646             // a new mechanism to handle lost resources reloading...
647             delete child;
648          }
649          if(flags.ownMesh && mesh)
650          {
651             DisplaySystem meshDisplaySystem = mesh.displaySystem;
652             mesh.Free(0);
653             if(meshDisplaySystem)
654                meshDisplaySystem.RemoveMesh(mesh);
655             delete mesh;
656          }
657
658          tracks.Free(FrameTrack::Free);
659
660          delete name;
661       }
662    }
663
664    bool Load(const char * fileName, const char * type, DisplaySystem displaySystem)
665    {
666       char ext[MAX_EXTENSION];
667       subclass(ObjectFormat) format;
668       OldLink link;
669       bool result = false;
670
671       if(!type && fileName)
672          type = strlwr(GetExtension(fileName, ext));
673
674       for(link = class(ObjectFormat).derivatives.first; link; link = link.next)
675       {
676          format = link.data;
677          if(format.extension && !strcmp(format.extension, type))
678             break;
679       }
680       if(!link) format = null;
681
682       if(format)
683       {
684          if((format.Load(this, fileName, displaySystem)))
685             result = true;
686       }
687       /*if(!result)
688           ErrorLogCode(GERR_LOAD_OBJECT_FAILED, fileName);*/
689       return result;
690    }
691
692    void FreeMesh(DisplaySystem displaySystem)
693    {
694       if(this)
695       {
696          Object child;
697          mesh.Free(0);
698          for(child = children.first; child; child = child.next)
699             child.FreeMesh(displaySystem);
700       }
701    }
702
703    Object Find(const char * name)
704    {
705       if(this && name)
706       {
707          Object child;
708
709          if(this.name && !strcmp(this.name, name))
710             return this;
711          else
712          {
713             for(child = children.first; child; child = child.next)
714             {
715                Object result = child.Find(name);
716                if(result)
717                   return result;
718             }
719          }
720       }
721       return null;
722    }
723
724    void Initialize(void)
725    {
726       if(this)
727       {
728          transform.scaling = { 1, 1, 1 };
729          transform.orientation = { 1,0,0,0 };
730          flags.root = true;
731          flags.transform = true;
732          matrix.Identity();
733       }
734    }
735
736    Mesh InitializeMesh(DisplaySystem displaySystem)
737    {
738       if(this)
739       {
740          flags.mesh = true;
741          if(!mesh)
742          {
743             mesh = Mesh { };
744             flags.ownMesh = true;
745          }
746          if(mesh)
747          {
748             FillBytes(mesh, 0, sizeof(class Mesh));
749             displaySystem.AddMesh(mesh);
750          }
751          matrix.Identity();
752          return mesh;
753       }
754       return null;
755    }
756
757    bool AddName(Object object, const char * name)
758    {
759       bool result = false;
760       if(this)
761       {
762          char * newName = CopyString(name);
763          object.name = newName;
764          result = children.AddName(object);
765          if(result)
766             object.parent = this;
767          object.flags.transform = true;
768       }
769       return result;
770    }
771
772    // TODO: Add support to Merge Vertex Colors mesh feature
773    bool Merge(DisplaySystem displaySystem)
774    {
775       bool result = false;
776
777       if(!children.first && (!this.flags.mesh || this.flags.ownMesh))
778          result = true;
779       else
780       {
781          Object child, nextChild;
782          int nVertices = 0;
783          MeshFeatures flags = 0;
784          Mesh objectMesh = this.flags.mesh ? mesh : null;
785          bool freeMesh = this.flags.ownMesh;
786
787          // Count total number of vertices
788          if(objectMesh)
789          {
790             flags |= objectMesh.flags;
791             nVertices += objectMesh.nVertices;
792          }
793
794          for(child = children.first; child; child = child.next)
795          {
796             child.Merge(displaySystem);
797             if(child.flags.mesh && child.mesh)
798             {
799                nVertices += child.mesh.nVertices;
800                flags |= child.mesh.flags;
801                this.flags.computeLightVectors |= child.flags.computeLightVectors;
802             }
803          }
804
805          if(!nVertices)
806             return true;
807
808          if(this.flags.camera)
809             delete camera;
810
811          mesh = Mesh { };
812          this.flags.ownMesh = true;
813          this.flags.mesh = true;
814          displaySystem.AddMesh(mesh);
815
816          if(mesh.Allocate(flags, nVertices, displaySystem))
817          {
818             int c;
819             int nTriangles = 0;
820             int vertexOffset = 0;
821             PrimitiveGroup group = null;
822
823             // Merge vertices
824
825             nVertices = 0;
826             if(objectMesh)
827             {
828                for(c = 0; c<objectMesh.nVertices; c++)
829                {
830                   mesh.vertices[nVertices] = objectMesh.vertices[c];
831
832                   if(objectMesh.normals)
833                      mesh.normals[nVertices] = objectMesh.normals[c];
834                   if(objectMesh.texCoords)
835                      mesh.texCoords[nVertices] = objectMesh.texCoords[c];
836                   if(objectMesh.tangents)
837                   {
838                      mesh.tangents[2*nVertices+0] = objectMesh.tangents[2*c+0];
839                      mesh.tangents[2*nVertices+1] = objectMesh.tangents[2*c+1];
840                   }
841
842                   nVertices++;
843                }
844             }
845
846             for(child = children.first; child; child = child.next)
847             {
848                Matrix matrix, normalMatrix;
849
850                matrix = child.localMatrix;
851                /*
852                matrix.Identity();
853                matrix.Scale(child.transform.scaling.x, child.transform.scaling.y, child.transform.scaling.z);
854                matrix.Rotate(child.transform.orientation);
855                */
856
857                normalMatrix = matrix;
858
859                normalMatrix.m[3][0] = 0;
860                normalMatrix.m[3][1] = 0;
861                normalMatrix.m[3][2] = 0;
862
863                // matrix.Translate(child.transform.position.x, child.transform.position.y, child.transform.position.z);
864
865                if(child.flags.mesh && child.mesh)
866                {
867                   for(c = 0; c < child.mesh.nVertices; c++)
868                   {
869                      mesh.vertices[nVertices].MultMatrix(child.mesh.vertices[c], matrix);
870                      if(child.mesh.normals)
871                         mesh.normals[nVertices].MultMatrix(child.mesh.normals[c], normalMatrix);
872                      if(child.mesh.texCoords)
873                         mesh.texCoords[nVertices] = child.mesh.texCoords[c];
874                      if(child.mesh.tangents)
875                      {
876                         mesh.tangents[2*nVertices+0].MultMatrix(child.mesh.tangents[2*c+0], normalMatrix);
877                         mesh.tangents[2*nVertices+1].MultMatrix(child.mesh.tangents[2*c+1], normalMatrix);
878                      }
879                      nVertices++;
880                   }
881                }
882             }
883
884             // Merge Indexed Primitive Groups
885             while(true)
886             {
887                int nIndices = 0;
888                PrimitiveGroupType type = (PrimitiveGroupType)-1;
889                Material material = null;
890                bool foundGroup = false;
891
892                // Find first group type/material to process and determine how many indices are required
893                if(objectMesh)
894                {
895                   for(group = objectMesh.groups.first; group; group = group.next)
896                   {
897                      if(!foundGroup && !(group.type.vertexRange))
898                      {
899                         if(!FindMaterialAndType(mesh, group.material, group.type))
900                         {
901                            material = group.material;
902                            type = group.type;
903                            nIndices += group.nIndices;
904                            foundGroup = true;
905                         }
906                      }
907                      else if(material == group.material && type == group.type)
908                         nIndices += group.nIndices;
909                   }
910                }
911
912                for(child = children.first; child; child = child.next)
913                {
914                   if(child.flags.mesh && child.mesh)
915                   {
916                      for(group = child.mesh.groups.first; group; group = group.next)
917                      {
918                         if(!foundGroup && !(group.type.vertexRange))
919                         {
920                            if(!FindMaterialAndType(mesh, group.material ? group.material : child.material, group.type))
921                            {
922                               material = group.material ? group.material : child.material;
923                               type = group.type;
924                               nIndices += group.nIndices;
925                               foundGroup = true;
926                            }
927                         }
928                         else if(material == (group.material ? group.material : child.material) && type == group.type)
929                            nIndices += group.nIndices;
930                      }
931                   }
932                }
933
934                // Merge with all similar groups
935                if(foundGroup)
936                {
937                   PrimitiveGroup newGroup = mesh.AddPrimitiveGroup(type, nIndices);
938                   if(newGroup)
939                   {
940                      newGroup.material = material;
941                      nIndices = 0;
942
943                      vertexOffset = 0;
944
945                      if(objectMesh)
946                      {
947                         for(group = objectMesh.groups.first; group; group = group.next)
948                         {
949                            if(newGroup.material == group.material && newGroup.type == group.type)
950                            {
951                               int c;
952                               if(group.type.indices32bit)
953                                  for(c = 0; c<group.nIndices; c++)
954                                     newGroup.indices32[nIndices++] = group.indices32[c] + vertexOffset;
955                               else
956                                  for(c = 0; c<group.nIndices; c++)
957                                     newGroup.indices[nIndices++] = (uint16)(group.indices[c] + vertexOffset);
958                            }
959                         }
960                         vertexOffset += objectMesh.nVertices;
961                      }
962
963                      for(child = children.first; child; child = child.next)
964                      {
965                         if(child.flags.mesh && child.mesh)
966                         {
967                            for(group = child.mesh.groups.first; group; group = group.next)
968                            {
969                               if(newGroup.material == (group.material ? group.material : child.material) &&
970                                  newGroup.type == group.type)
971                               {
972                                  int c;
973                                  if(group.type.indices32bit)
974                                     for(c = 0; c<group.nIndices; c++)
975                                        newGroup.indices32[nIndices++] = group.indices32[c] + vertexOffset;
976                                  else
977                                     for(c = 0; c<group.nIndices; c++)
978                                        newGroup.indices[nIndices++] = (uint16)(group.indices[c] + vertexOffset);
979                               }
980                            }
981                            vertexOffset += child.mesh.nVertices;
982                         }
983                      }
984                      mesh.UnlockPrimitiveGroup(newGroup);
985                   }
986                }
987                else
988                   break;
989             }
990
991             // Merge Non-Indexed Primitive Groups
992             vertexOffset = 0;
993
994             if(objectMesh)
995             {
996                for(group = objectMesh.groups.first; group; group = group.next)
997                {
998                   if(group.type.vertexRange)
999                   {
1000                      PrimitiveGroup newGroup = mesh.AddPrimitiveGroup(group.type, 0);
1001                      if(newGroup)
1002                      {
1003                         newGroup.material = group.material;
1004                         newGroup.nVertices = group.nVertices;
1005                         newGroup.first = group.first + vertexOffset;
1006                      }
1007                   }
1008                }
1009                vertexOffset += objectMesh.nVertices;
1010             }
1011
1012             for(child = children.first; child; child = child.next)
1013             {
1014                if(child.flags.mesh && child.mesh)
1015                {
1016                   for(group = child.mesh.groups.first; group; group = group.next)
1017                   {
1018                      if(group.type.vertexRange)
1019                      {
1020                         PrimitiveGroup newGroup = mesh.AddPrimitiveGroup(group.type, 0);
1021                         if(newGroup)
1022                         {
1023                            newGroup.material = group.material ? group.material : child.material;
1024                            newGroup.nVertices = group.nVertices;
1025                            newGroup.first = group.first + vertexOffset;
1026                         }
1027                      }
1028                   }
1029                   vertexOffset += child.mesh.nVertices;
1030                }
1031             }
1032
1033             // Merge Triangles
1034             if(objectMesh)
1035                nTriangles = objectMesh.nPrimitives;
1036
1037             for(child = children.first; child; child = child.next)
1038             {
1039                if(child.flags.mesh && child.mesh)
1040                   nTriangles += child.mesh.nPrimitives;
1041             }
1042
1043             mesh.primitives = new PrimitiveSingle[nTriangles];
1044             mesh.nPrimitives = 0;
1045             vertexOffset = 0;
1046             if(objectMesh)
1047             {
1048                for(c = 0; c<objectMesh.nPrimitives; c++)
1049                {
1050                   int i;
1051                   PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
1052                   PrimitiveSingle * src = &objectMesh.primitives[c];
1053
1054                   mesh.AllocatePrimitive(triangle, src->type, src->nIndices);
1055                   triangle->material = src->material;
1056                   triangle->middle = src->middle;
1057                   triangle->plane = src->plane;
1058
1059                   //*triangle = *src;
1060                   //src->indices = null;
1061                   //src->data = null;
1062
1063                   if(triangle->type.indices32bit)
1064                      for(i = 0; i<triangle->nIndices; i++)
1065                         triangle->indices32[i] = src->indices32[i] + vertexOffset;
1066                   else
1067                      for(i = 0; i<triangle->nIndices; i++)
1068                         triangle->indices[i] = (uint16)(src->indices[i] + vertexOffset);
1069                   mesh.UnlockPrimitive(triangle);
1070                }
1071                vertexOffset += objectMesh.nVertices;
1072             }
1073
1074             for(child = children.first; child; child = child.next)
1075             {
1076                if(child.flags.mesh && child.mesh)
1077                {
1078                   for(c = 0; c<child.mesh.nPrimitives; c++)
1079                   {
1080                      int i;
1081                      PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
1082                      PrimitiveSingle * src = &child.mesh.primitives[c];
1083
1084                      mesh.AllocatePrimitive(triangle, src->type, src->nIndices);
1085                      triangle->material = src->material ? src->material : child.material;
1086                      triangle->middle = src->middle;
1087                      triangle->plane = src->plane;
1088
1089                      //*triangle = *src;
1090                      //src->indices = null;
1091                      //src->data = null;
1092
1093                      if(triangle->type.indices32bit)
1094                      {
1095                         for(i = 0; i<triangle->nIndices; i++)
1096                            triangle->indices32[i] = src->indices32[i] + vertexOffset;
1097                      }
1098                      else
1099                      {
1100                         for(i = 0; i<triangle->nIndices; i++)
1101                            triangle->indices[i] = (uint16)(src->indices[i] + vertexOffset);
1102                      }
1103                      mesh.UnlockPrimitive(triangle);
1104                   }
1105                   vertexOffset += child.mesh.nVertices;
1106                }
1107             }
1108
1109             // Free children
1110             for(child = children.first; child; child = nextChild)
1111             {
1112                nextChild = child.next;
1113                children.Remove(child);
1114                child.Free(displaySystem);
1115                delete child;
1116             }
1117
1118             mesh.ApplyTranslucency(this);
1119             // this.flags.translucent = true;
1120
1121             result = true;
1122
1123             mesh.Unlock(flags);
1124          }
1125          if(freeMesh && objectMesh)
1126          {
1127             if(objectMesh.displaySystem)
1128                objectMesh.displaySystem.RemoveMesh(objectMesh);
1129             delete objectMesh;
1130          }
1131          SetMinMaxRadius(true);
1132       }
1133       return result;
1134    }
1135
1136    void RotateEuler(Euler rotation, Euler min, Euler max)
1137    {
1138       // WARNING: 'eulerOrientation' is only updated by this function
1139       Euler euler = eulerOrientation;//transform.orientation;
1140       euler.Add(euler, rotation);
1141
1142       if(min && max)
1143       {
1144          if(min.pitch && max.pitch)
1145             euler.pitch = Min(Max(euler.pitch, min.pitch), max.pitch);
1146          if(min.yaw && max.yaw)
1147             euler.yaw = Min(Max(euler.yaw, min.yaw), max.yaw);
1148          if(min.roll && max.roll)
1149             euler.roll = Min(Max(euler.roll, min.roll), max.roll);
1150       }
1151
1152       eulerOrientation = euler;
1153       transform.orientation = euler;
1154       UpdateTransform();
1155    }
1156
1157    void Move(Vector3D direction)
1158    {
1159       Matrix matrix;
1160       Vector3D offset;
1161
1162       matrix.RotationQuaternion(transform.orientation);
1163       offset.MultMatrix(direction, matrix);
1164       transform.position.Add(transform.position, offset);
1165       UpdateTransform();
1166    }
1167
1168    void UpdateTransform(void)
1169    {
1170       SetTransformDirty();
1171       _UpdateTransform();
1172       SetMinMaxRadius(false);
1173    }
1174
1175    void Animate(unsigned int frame)
1176    {
1177       if(this && startFrame != endFrame)
1178       {
1179          while(frame < startFrame) frame += (endFrame - startFrame + 1);
1180          while(frame > endFrame)   frame -= (endFrame - startFrame + 1);
1181
1182          this.frame = frame;
1183          _Animate(frame);
1184          _UpdateTransform();
1185          SetMinMaxRadius(false);
1186       }
1187    }
1188
1189    void DoubleSided(bool flag)
1190    {
1191       if(this)
1192       {
1193          Object child;
1194          mesh.DoubleSided(flag);
1195          for(child = children.first; child; child = child.next)
1196             child.DoubleSided(flag);
1197       }
1198    }
1199
1200    bool IntersectsGroundPolygon(int count, Pointf * pointfs)
1201    {
1202       bool result = false;
1203
1204       Pointf * p1;
1205       Pointf * p2;
1206       double minX = wmin.x, maxX = wmax.x;
1207       double minY = wmin.z, maxY = wmax.z;
1208       double delta = (maxX - minX)/2;
1209       double x = (maxX + minX)/2, y = (maxY + minY)/2;
1210       int c;
1211       for(c = 0; c<count; c++)
1212       {
1213          double d;
1214          p1 = &pointfs[c];
1215          p2 = &pointfs[(c == count-1) ? 0 : (c+1)];
1216
1217          if( (p1->x < minX) &&  (p2->x < minX) )
1218          {
1219             if((p1->y <= y) && (p2->y >  y) )
1220                result ^= true;
1221             else if( (p1->y >  y) && (p2->y <= y) )
1222                result ^= true;
1223          }
1224          else if(!((p1->x > maxX && p2->x > maxX) || (p1->y < minY && p2->y < minY) || (p1->y > maxY && p2->y > maxY)))
1225          {
1226             if(p1->y == p2->y)
1227             {
1228                d = y - p1->y;
1229                if (d < 0) d = -d;
1230                if (d < delta) return true;
1231             }
1232             else if(p1->x == p2->x)
1233             {
1234                d = x - p1->x;
1235                if(d < 0) d = -d;
1236                if(d < delta) return true;
1237                else if(p1->x > x) ;
1238                else if( (p1->y <= y) && (p2->y >  y) )
1239                   result ^= true;
1240                else if( (p1->y >  y) && (p2->y <= y) )
1241                   result ^= true;
1242             }
1243             else
1244             {
1245                float a, b, dy, dx;
1246
1247                a = p2->y - p1->y;
1248                b = p1->x - p2->x;
1249                dy =  a;
1250                dx = -b;
1251                d = a * x + b * y + (p2->x * p1->y) - (p2->y * p1->x);
1252                if (a < 0) a = -a;
1253                if (b < 0) b = -b;
1254                if (d < 0) d = -d;
1255
1256                if(d < a * delta)
1257                   return true;
1258                else if (d < b * delta)
1259                   return true;
1260                else if( ( (p1->y <= y) && (p2->y >  y) ) || ( (p1->y >  y) && (p2->y <= y) ) )
1261                {
1262                   double xdy;
1263
1264                   xdy = (dx * (y - p1->y)) + (dy * p1->x);
1265                   if(dy < 0)
1266                   {
1267                      if(xdy > x * dy)
1268                         result ^= true;
1269                   }
1270                   else if(xdy < x * dy)
1271                      result ^= true;
1272                }
1273             }
1274          }
1275       }
1276       return result;
1277    }
1278
1279    property Transform transform { set { transform = value; eulerOrientation = transform.orientation; } get { value = transform; } };
1280    property Material material { set { material = value; } get { return material; } };
1281    property Vector3Df max { get { value = max; } };
1282    property Vector3Df min { get { value = min; } };
1283    property Vector3Df center { get { value = center; } };
1284    property float radius { get { return radius; } };
1285
1286    property Vector3D wmax { get { value = wmax; } };
1287    property Vector3D wmin { get { value = wmin; } };
1288    property Vector3D wcenter { get { value = wcenter; } };
1289    property double wradius { get { return wradius; } };
1290
1291    property void * tag { set { tag = value; } get { return tag; } };
1292    property int frame { set { Animate(value); } get { return frame; } };
1293    property int startFrame { set { startFrame = value; } get { return startFrame; } };
1294    property int endFrame { set { endFrame = value; } get { return endFrame; } };
1295
1296    property Mesh mesh { set { mesh = value; } get { return mesh; } };
1297    property Camera camera { get { return camera; } }; // Fix this with inheritance? camera inherit from Object?
1298    property Object firstChild { get { return children.first; } };
1299    property Object next { get { return next; } };
1300    property const char * name { get { return name; } };
1301    property Matrix matrix { get { value = matrix; } };
1302    property Object cameraTarget { set { cameraTarget = value; } get { return cameraTarget; } };
1303    property OldList * tracks { /* set { tracks = value; } */ get { return &tracks; } };
1304    property ObjectFlags flags { set { flags = value; } get { return flags; } };
1305
1306    // TOFIX: 32-bit compiling with 64-bit SDK cannot access public members properly
1307    property Object parent          { get { return parent; } }
1308    property uint numChildren { get { return children.count; } }
1309
1310    property Matrix * localMatrixPtr   { get { return &localMatrix; } }
1311    property Matrix * matrixPtr        { get { return &matrix; } }
1312
1313 private:
1314    Object()
1315    {
1316       children.offset = (uint)(uintptr)&((Object)0).prev;
1317       transform.scaling = { 1, 1, 1 };
1318       transform.orientation = { 1,0,0,0 };
1319       flags.transform = true;
1320       localMatrix.Identity();
1321    }
1322
1323    ~Object()
1324    {
1325       Free(null);
1326    }
1327
1328    void SetTransformDirty()
1329    {
1330       Object child;
1331       flags.transform = true;
1332       for(child = children.first; child; child = child.next)
1333          child.SetTransformDirty();
1334    }
1335
1336    void _UpdateTransform()
1337    {
1338       if(flags.transform)
1339       {
1340          Object child;
1341
1342          // Cameras / Spot Lights must update their target first
1343          if(flags.camera && cameraTarget && cameraTarget.flags.transform)
1344             cameraTarget.UpdateTransform();
1345          else if(flags.light && light.flags.spot && light.target && light.target.flags.transform)
1346             light.target._UpdateTransform();
1347
1348          if(flags.camera && cameraTarget)
1349          {
1350             // Determine angle to look at target
1351             Vector3D position, direction;
1352             if(flags.root || !parent)
1353                position = transform.position;
1354             else
1355                position.MultMatrix(transform.position, parent.matrix);
1356
1357             direction.Subtract((Vector3D *)cameraTarget.matrix.m[3], position);
1358             transform.orientation.RotationDirection(direction);
1359
1360             // Add the roll
1361             transform.orientation.RotateRoll(roll);
1362          }
1363
1364          if(flags.light && light.flags.spot)
1365          {
1366             // Determine angle to look at target
1367             Vector3D position;
1368             if(flags.root || !parent)
1369                position = transform.position;
1370             else
1371                position.MultMatrix(transform.position, parent.matrix);
1372
1373             if(light.target)
1374             {
1375                light.direction.Subtract((Vector3D *) light.target.matrix.m[3], position);
1376                light.direction.Normalize(light.direction);
1377             }
1378          }
1379
1380          if(!flags.localMatrixSet)
1381          {
1382             localMatrix.Identity();
1383             localMatrix.Scale(transform.scaling.x, transform.scaling.y, transform.scaling.z);
1384             localMatrix.Rotate(transform.orientation);
1385             localMatrix.Translate(transform.position.x, transform.position.y, transform.position.z);
1386          }
1387
1388          // Compute transform (with ancestors)
1389          if(flags.root || !parent)
1390             matrix = localMatrix;
1391          else
1392             matrix.Multiply(localMatrix, parent.matrix);
1393
1394          flags.transform = false;
1395
1396          for(child = children.first; child; child = child.next)
1397          {
1398             if(child.flags.transform)
1399                child._UpdateTransform();
1400          }
1401       }
1402    }
1403
1404    void _Animate(unsigned int frame)
1405    {
1406       Object child;
1407       FrameTrack track;
1408
1409       for(track = tracks.first; track; track = track.next)
1410       {
1411          unsigned int c;
1412
1413          if(track.numKeys)
1414          {
1415             unsigned int prev = 0, next = track.numKeys - 1;
1416             FrameKey * prevKey = &track.keys[prev], * nextKey = &track.keys[next];
1417             float t = 0;
1418
1419             for(c = 0; c<track.numKeys; c++)
1420             {
1421                FrameKey * key = track.keys + c;
1422                if(key->frame <= frame) { prevKey = key; prev = c; }
1423                if(key->frame >= frame) { nextKey = key; next = c; break; }
1424             }
1425
1426             if(nextKey->frame != prevKey->frame)
1427                t = ease((float) (frame - prevKey->frame) / (nextKey->frame - prevKey->frame), prevKey->easeFrom, nextKey->easeTo);
1428
1429             switch(track.type.type)
1430             {
1431                case position:
1432                {
1433                   Vector3Df position;
1434                   track.Interpolate(position, prevKey->position, nextKey->position, prev, next, t);
1435                   transform.position = { (double)position.x, (double)position.y, (double)position.z };
1436                   break;
1437                }
1438                case scaling:
1439                   track.Interpolate(transform.scaling, prevKey->scaling, &nextKey->scaling, prev, next, t);
1440                   break;
1441                case rotation:
1442                   track.InterpolateQuat(transform.orientation, prevKey->orientation, nextKey->orientation, prev, next, t);
1443                   break;
1444                // Cameras
1445                case roll:
1446                   roll = track.InterpolateFloat(prevKey->roll, nextKey->roll, prev, next, t);
1447                   break;
1448                case fov:
1449                {
1450                   camera.fov = track.InterpolateFloat(prevKey->fov, nextKey->fov, prev, next, t);
1451                   /*
1452                   double mm = (camera.fov - 5.05659508373109) / 1.13613250717301;
1453                   camera.fov = 1248.58921609766 * pow(mm, -0.895625414990581);
1454                   */
1455                   //camera.Setup(camera.width, camera.height, camera.origin);
1456                   break;
1457                }
1458                // Lights
1459                case colorChange:
1460                {
1461                   track.Interpolate((Vector3Df *)&light.diffuse,
1462                      (Vector3Df *)&prevKey->color, (Vector3Df *)&nextKey->color, prev, next, t);
1463                   light.specular = light.diffuse;
1464                   break;
1465                }
1466                case fallOff:
1467                {
1468                   light.fallOff = track.InterpolateFloat(prevKey->fallOff, nextKey->fallOff, prev, next, t);
1469                   break;
1470                }
1471                case hotSpot:
1472                {
1473                   light.hotSpot = track.InterpolateFloat(prevKey->hotSpot, nextKey->hotSpot, prev, next, t);
1474                   break;
1475                }
1476             }
1477          }
1478       }
1479
1480       for(child = children.first; child; child = child.next)
1481          child._Animate(frame);
1482
1483       flags.transform = true;
1484    }
1485
1486    // Private for now
1487    FrustumPlacement InsideFrustum(Plane * planes)
1488    {
1489       FrustumPlacement result = inside;
1490
1491       int p;
1492       // First test: Sphere
1493       for(p = 0; p<6; p++)
1494       {
1495          Plane * plane = &planes[p];
1496          double dot = plane->normal.DotProduct(wcenter);
1497          double distance = dot + plane->d;
1498          if(distance < -wradius)
1499          {
1500             result = outside;
1501             break;
1502          }
1503          if(Abs(distance) < wradius)
1504             result = intersecting;
1505       }
1506
1507       if(result == intersecting)
1508       {
1509          // Second test: Bounding Box
1510          Vector3D box[8] =
1511          {
1512             { wmin.x, wmin.y, wmin.z },
1513             { wmin.x, wmin.y, wmax.z },
1514             { wmin.x, wmax.y, wmin.z },
1515             { wmin.x, wmax.y, wmax.z },
1516             { wmax.x, wmin.y, wmin.z },
1517             { wmax.x, wmin.y, wmax.z },
1518             { wmax.x, wmax.y, wmin.z },
1519             { wmax.x, wmax.y, wmax.z }
1520          };
1521            int numPlanesAllIn = 0;
1522          for(p = 0; p < 6; p++)
1523          {
1524             Plane * plane = &planes[p];
1525             int i;
1526             int numGoodPoints = 0;
1527             for(i = 0; i < 8; ++i)
1528             {
1529                double dot = plane->normal.DotProduct(box[i]);
1530                double distance = dot + plane->d;
1531                            if(distance > -1)
1532                   numGoodPoints++;
1533                    }
1534                    if(!numGoodPoints)
1535             {
1536                result = outside;
1537                break;
1538             }
1539             if(numGoodPoints == 8)
1540                numPlanesAllIn++;
1541            }
1542            if(numPlanesAllIn == 6)
1543                    result = inside;
1544       }
1545       return result;
1546    }
1547
1548    Object prev, next;
1549    char * name;
1550    Object parent;
1551    OldList children;
1552
1553    ObjectFlags flags;
1554
1555    OldList tracks;
1556    unsigned startFrame, endFrame;
1557    int frame;
1558    Vector3Df pivot;
1559
1560    public Transform transform;
1561    Matrix matrix;
1562    Matrix localMatrix;
1563
1564    void * tag;
1565    Vector3Df min, max, center;
1566    Vector3D wmin, wmax, wcenter;
1567
1568    bool volume;
1569    float radius;
1570    double wradius;
1571    ColorRGB ambient;
1572
1573    /*public */union
1574    {
1575       // Mesh
1576       struct
1577       {
1578          Mesh mesh;
1579          Material material;
1580       };
1581
1582       // Light
1583       Light light;
1584
1585       // Camera
1586       struct
1587       {
1588          Camera camera;
1589          Object cameraTarget;
1590          double roll;
1591       };
1592    };
1593
1594    public property Light light
1595    {
1596       set
1597       {
1598          light = value;
1599       }
1600       get
1601       {
1602          value = light;
1603       }
1604    }
1605
1606    Euler eulerOrientation;
1607 };