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