Initial git commit -- Transition from CodeGuard repository
[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 char * extension;
10    class_property char * extension
11    {
12       set { class_data(extension) = value; }
13       get { return class_data(extension); }
14    }
15
16    virtual bool ::Load(Object object, 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;
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    bool loop;
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
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 = true;
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 < this.min.x) this.min.x = point.x;
537                if(point.y < this.min.y) this.min.y = point.y;
538                if(point.z < this.min.z) this.min.z = point.z;
539
540                if(point.x > this.max.x) this.max.x = point.x;
541                if(point.y > this.max.y) this.max.y = point.y;
542                if(point.z > this.max.z) this.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 = model.name;
608          flags = model.flags;
609          flags.ownMesh = false;
610          mesh = model.mesh;
611          /*
612          min = model.min;
613          max = model.max;
614          radius = model.radius;
615          */
616          transform = model.transform;
617
618          for(modelChild = model.children.first; modelChild; modelChild = modelChild.next)
619          {
620             Object child { parent = this };
621             child.Duplicate(modelChild);
622             children.AddName(child);
623          }
624       }
625    }
626
627    void Free(DisplaySystem displaySystem)
628    {
629       if(this)
630       {
631          Object child;
632
633          while((child = children.first))
634          {
635             children.Remove(child);
636             child.Free(displaySystem);
637          }
638          if(flags.ownMesh)
639          {
640             DisplaySystem meshDisplaySystem = mesh.displaySystem;
641             mesh.Free(0);
642             if(meshDisplaySystem)
643                meshDisplaySystem.RemoveMesh(mesh);
644             delete mesh;
645          }
646
647          tracks.Free(FrameTrack::Free);
648          
649          delete name;
650       }
651    }
652
653    bool Load(char * fileName, char * type, DisplaySystem displaySystem)
654    {
655       char ext[MAX_EXTENSION];
656       subclass(ObjectFormat) format;
657       OldLink link;
658       bool result = false;
659
660       if(!type && fileName) 
661       {
662          type = GetExtension(fileName, ext);
663          strlwr(type);
664       }
665
666       for(link = class(ObjectFormat).derivatives.first; link; link = link.next)
667       {
668          format = link.data;
669          if(format.extension && !strcmp(format.extension, type))
670             break;
671       }
672       if(!link) format = null;
673
674       if(format)
675       {
676          if((format.Load(this, fileName, displaySystem)))
677             result = true;
678       }
679       /*if(!result)
680           ErrorLogCode(GERR_LOAD_OBJECT_FAILED, fileName);*/
681       return result;
682    }
683
684    void FreeMesh(DisplaySystem displaySystem)
685    {
686       if(this)
687       {
688          Object child;
689          mesh.Free(0);
690          for(child = children.first; child; child = child.next)
691             child.FreeMesh(displaySystem);
692       }
693    }
694
695    Object Find(char * name)
696    {
697       if(this && name)
698       {
699          Object child;
700
701          if(this.name && !strcmp(this.name, name))
702             return this;
703          else
704          {
705             for(child = children.first; child; child = child.next)
706             {
707                Object result = child.Find(name);
708                if(result) 
709                   return result;
710             }
711          }
712       }
713       return null;
714    }
715
716    void Initialize(void)
717    {
718       if(this)
719       {
720          transform.scaling = { 1, 1, 1 };
721          transform.orientation = { 1,0,0,0 };
722          flags.root = true;
723          flags.transform = true;
724          matrix.Identity();
725       }
726    }
727
728    Mesh InitializeMesh(DisplaySystem displaySystem)
729    {
730       if(this)
731       {
732          flags.mesh = true;
733          if(!mesh) 
734          {
735             mesh = Mesh { };
736             flags.ownMesh = true;
737          }
738          if(mesh)
739          {
740             FillBytes(mesh, 0, sizeof(class Mesh));
741             displaySystem.AddMesh(mesh);
742          }
743          matrix.Identity();
744          return mesh;
745       }
746       return null;
747    }
748
749    void AddName(Object object, char * name)
750    {
751       // TODO: Watch Out For this
752       // Object object = children.FindName(name, false);
753       if(this)
754       {
755          char * newName = CopyString(name);
756          object.name = newName;
757          if(object)
758             children.AddName(object);
759          else
760             children.Add(object);
761          object.parent = this;
762          object.flags.transform = true;
763       }
764    }
765
766    // TODO: Add support to Merge Vertex Colors mesh feature
767    bool Merge(DisplaySystem displaySystem)
768    {
769       bool result = false;
770
771       if(!children.first)
772          result = true;
773       else
774       {
775          Object child, nextChild;
776          int nVertices = 0;
777          MeshFeatures flags = 0;
778          Mesh objectMesh = mesh;
779          bool freeMesh = this.flags.ownMesh;
780
781          mesh = Mesh { };
782          this.flags.ownMesh = true;
783          this.flags.mesh = true;
784          displaySystem.AddMesh(mesh);
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.mesh)
797             {
798                nVertices += child.mesh.nVertices;
799                flags |= child.mesh.flags;
800             }
801          }
802
803          if(mesh.Allocate(flags, nVertices, displaySystem))
804          {
805             int c;
806             int nTriangles = 0;
807             int vertexOffset = 0;
808             PrimitiveGroup group = null;
809
810             // Merge vertices
811
812             nVertices = 0;
813             if(objectMesh)
814             {
815                for(c = 0; c<objectMesh.nVertices; c++)
816                {
817                   mesh.vertices[nVertices] = objectMesh.vertices[c];
818                   
819                   if(objectMesh.normals)
820                      mesh.normals[nVertices] = objectMesh.normals[c];
821                   if(objectMesh.texCoords)
822                      mesh.texCoords[nVertices] = objectMesh.texCoords[c];
823
824                   nVertices++;
825                }
826             }
827
828             for(child = children.first; child; child = child.next)
829             {
830                Matrix matrix, normalMatrix;
831
832                matrix.Identity();
833                matrix.Scale(child.transform.scaling.x, child.transform.scaling.y, child.transform.scaling.z);
834
835                matrix.Rotate(child.transform.orientation);
836
837                normalMatrix = matrix;
838
839                matrix.Translate(child.transform.position.x, child.transform.position.y, child.transform.position.z);
840                if(child.mesh)
841                {
842                   for(c = 0; c<child.mesh.nVertices; c++)
843                   {
844                      mesh.vertices[nVertices].MultMatrix(child.mesh.vertices[c], matrix);
845                      if(child.mesh.normals)
846                         mesh.normals[nVertices].MultMatrix(child.mesh.normals[c], normalMatrix);
847                      if(child.mesh.texCoords)
848                         mesh.texCoords[nVertices] = child.mesh.texCoords[c];
849                      nVertices++;
850                   }
851                }
852             }
853
854             // Merge Indexed Primitive Groups 
855             while(true)
856             {
857                int nIndices = 0;
858                PrimitiveGroupType type;
859                Material material = null;
860                bool foundGroup = false;
861             
862                // Find first group type/material to process and determine how many indices are required
863                if(objectMesh)
864                {
865                   for(group = objectMesh.groups.first; group; group = group.next)
866                   {
867                      if(!foundGroup && !(group.type.vertexRange))
868                      {
869                         if(!FindMaterialAndType(mesh, group.material, group.type))
870                         {
871                            material = group.material;
872                            type = group.type;
873                            nIndices += group.nIndices;
874                            foundGroup = true;
875                         }
876                      }
877                      else if(material == group.material && type == group.type)
878                         nIndices += group.nIndices;
879                   }
880                }
881
882                for(child = children.first; child; child = child.next)
883                {
884                   if(child.mesh)
885                   {
886                      for(group = child.mesh.groups.first; group; group = group.next)
887                      {
888                         if(!foundGroup && !(group.type.vertexRange))
889                         {
890                            if(!FindMaterialAndType(mesh, group.material ? group.material : child.material, group.type))
891                            {
892                               material = group.material ? group.material : child.material;
893                               type = group.type;
894                               nIndices += group.nIndices;
895                               foundGroup = true;
896                            }
897                         }
898                         else if(material == (group.material ? group.material : child.material) && type == group.type)
899                            nIndices += group.nIndices;
900                      }
901                   }
902                }
903
904                // Merge with all similar groups
905                if(foundGroup)
906                {
907                   PrimitiveGroup newGroup = mesh.AddPrimitiveGroup(type, nIndices);
908                   if(newGroup)
909                   {
910                      newGroup.material = material;
911                      nIndices = 0;
912
913                      vertexOffset = 0;
914                      
915                      if(objectMesh)
916                      {
917                         for(group = objectMesh.groups.first; group; group = group.next)
918                         {
919                            if(newGroup.material == group.material && newGroup.type == group.type)
920                            {
921                               int c;
922                               for(c = 0; c<group.nIndices; c++)
923                                  newGroup.indices[nIndices++] = group.indices[c] + (uint16)vertexOffset;
924                            }
925                         }
926                         vertexOffset += objectMesh.nVertices;
927                      }
928
929                      for(child = children.first; child; child = child.next)
930                      {
931                         if(child.mesh)
932                         {
933                            for(group = child.mesh.groups.first; group; group = group.next)
934                            {
935                               if(newGroup.material == (group.material ? group.material : child.material) && 
936                                  newGroup.type == group.type)
937                               {
938                                  int c;
939                                  for(c = 0; c<group.nIndices; c++)
940                                     newGroup.indices[nIndices++] = group.indices[c] + (uint16)vertexOffset;
941                               }
942                            }
943                            vertexOffset += child.mesh.nVertices;
944                         }
945                      }
946                      mesh.UnlockPrimitiveGroup(newGroup);
947                   }
948                }
949                else
950                   break;                         
951             }
952
953             // Merge Non-Indexed Primitive Groups 
954             vertexOffset = 0;
955             
956             if(objectMesh)
957             {
958                for(group = objectMesh.groups.first; group; group = group.next)
959                {
960                   if(group.type.vertexRange)
961                   {
962                      PrimitiveGroup newGroup = mesh.AddPrimitiveGroup(group.type, 0);
963                      if(newGroup)
964                      {
965                         newGroup.material = group.material;
966                         newGroup.nVertices = group.nVertices;
967                         newGroup.first = group.first + vertexOffset;
968                      }
969                   }
970                }
971                vertexOffset += objectMesh.nVertices;
972             }
973
974             for(child = children.first; child; child = child.next)
975             {
976                if(child.mesh)
977                {
978                   for(group = child.mesh.groups.first; group; group = group.next)
979                   {
980                      if(group.type.vertexRange)
981                      {
982                         PrimitiveGroup newGroup = mesh.AddPrimitiveGroup(group.type, 0);
983                         if(newGroup)
984                         {
985                            newGroup.material = group.material ? group.material : child.material;
986                            newGroup.nVertices = group.nVertices;
987                            newGroup.first = group.first + vertexOffset;
988                         }
989                      }
990                   }
991                   vertexOffset += child.mesh.nVertices;
992                }
993             }
994             
995             // Merge Triangles
996             if(objectMesh)
997                nTriangles = objectMesh.nPrimitives;
998             
999             for(child = children.first; child; child = child.next)
1000             {
1001                if(child.mesh)
1002                   nTriangles += child.mesh.nPrimitives;
1003             }
1004             mesh.primitives = new PrimitiveSingle[nTriangles];
1005             mesh.nPrimitives = 0;
1006             vertexOffset = 0;
1007             if(objectMesh)
1008             {
1009                for(c = 0; c<objectMesh.nPrimitives; c++)
1010                {
1011                   int i;
1012                   PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
1013                   
1014                   mesh.AllocatePrimitive(triangle, objectMesh.primitives[c].type, objectMesh.primitives[c].nIndices);
1015                   triangle->material = objectMesh.primitives[c].material;
1016                   triangle->middle = objectMesh.primitives[c].middle;
1017                   triangle->plane = objectMesh.primitives[c].plane;
1018
1019                   memcpy(triangle->indices, objectMesh.primitives[c].indices, objectMesh.primitives[c].nIndices * sizeof(uint16));
1020                   
1021                   /*
1022                   *triangle = objectMesh.primitives[c];
1023                   objectMesh.primitives[c].indices = null;
1024                   objectMesh.primitives[c].data = null;
1025                   */
1026
1027                   for(i = 0; i<triangle->nIndices; i++)
1028                      triangle->indices[i] += (uint16)vertexOffset;
1029                   mesh.UnlockPrimitive(triangle);
1030                }
1031                vertexOffset += objectMesh.nVertices;
1032             }
1033             for(child = children.first; child; child = child.next)
1034             {
1035                if(child.mesh)
1036                {
1037                   for(c = 0; c<child.mesh.nPrimitives; c++)
1038                   {
1039                      int i;
1040                      PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
1041
1042                      mesh.AllocatePrimitive(triangle, child.mesh.primitives[c].type, child.mesh.primitives[c].nIndices);
1043                      triangle->material = child.mesh.primitives[c].material ? child.mesh.primitives[c].material : child.material;
1044                      triangle->middle = child.mesh.primitives[c].middle;
1045                      triangle->plane = child.mesh.primitives[c].plane;
1046                      memcpy(triangle->indices, child.mesh.primitives[c].indices, child.mesh.primitives[c].nIndices * sizeof(uint16));
1047
1048                      /*
1049                      *triangle = child.mesh.primitives[c];
1050                      child.mesh.primitives[c].indices = null;
1051                      child.mesh.primitives[c].data = null;
1052                      */
1053
1054                      for(i = 0; i<triangle->nIndices; i++)
1055                         triangle->indices[i] += (uint16)vertexOffset;
1056                      mesh.UnlockPrimitive(triangle);
1057                   }
1058                   vertexOffset += child.mesh.nVertices;
1059                }
1060             }
1061
1062             // Free children
1063             for(child = children.first; child; child = nextChild)
1064             {
1065                nextChild = child.next;
1066                children.Remove(child);
1067                child.Free(displaySystem);
1068             }
1069
1070             mesh.ApplyTranslucency(this);
1071             // this.flags.translucent = true;
1072
1073             result = true;
1074
1075             mesh.Unlock(flags);
1076          }
1077          if(freeMesh)
1078          {
1079             if(objectMesh.displaySystem)
1080                objectMesh.displaySystem.RemoveMesh(objectMesh);
1081             delete objectMesh;
1082          }
1083          SetMinMaxRadius(true);
1084       }
1085       return result;
1086    }
1087
1088    void RotateEuler(Euler rotation, Euler min, Euler max)
1089    {
1090       Euler euler = eulerOrientation;//transform.orientation;
1091       euler.Add(euler, rotation);
1092
1093       if(min && max)
1094       {
1095          if(min.pitch && max.pitch)
1096             euler.pitch = Min(Max(euler.pitch, min.pitch), max.pitch);
1097          if(min.yaw && max.yaw)
1098             euler.yaw = Min(Max(euler.yaw, min.yaw), max.yaw);
1099          if(min.roll && max.roll)
1100             euler.roll = Min(Max(euler.roll, min.roll), max.roll);
1101       }
1102
1103       eulerOrientation = euler;
1104       transform.orientation = euler;
1105       UpdateTransform();
1106    }
1107
1108    void Move(Vector3D direction)
1109    {
1110       Matrix matrix;
1111       Vector3D offset;
1112
1113       matrix.RotationQuaternion(transform.orientation);
1114       offset.MultMatrix(direction, matrix);
1115       transform.position.Add(transform.position, offset);
1116       UpdateTransform();
1117    }
1118
1119    void UpdateTransform(void)
1120    {
1121       SetTransformDirty();
1122       _UpdateTransform();
1123       SetMinMaxRadius(false);
1124    }
1125
1126    void Animate(unsigned int frame)
1127    {
1128       if(this && startFrame != endFrame)
1129       {
1130          while(frame < startFrame) frame += (endFrame - startFrame + 1);
1131          while(frame > endFrame)   frame -= (endFrame - startFrame + 1);
1132
1133          this.frame = frame;
1134          _Animate(frame);
1135          _UpdateTransform();
1136          SetMinMaxRadius(false);
1137       }
1138    }
1139
1140    void DoubleSided(bool flag)
1141    {
1142       if(this)
1143       {
1144          Object child;
1145          mesh.DoubleSided(flag);
1146          for(child = children.first; child; child = child.next)
1147             child.DoubleSided(flag);
1148       }
1149    }
1150
1151    bool IntersectsGroundPolygon(int count, Pointf * pointfs)
1152    {
1153       bool result = false;
1154    
1155       Pointf * p1;
1156       Pointf * p2;
1157       double minX = wmin.x, maxX = wmax.x;
1158       double minY = wmin.z, maxY = wmax.z;
1159       double delta = (maxX - minX)/2;
1160       double x = (maxX + minX)/2, y = (maxY + minY)/2;
1161       int c;
1162       for(c = 0; c<count; c++)
1163       {
1164          double d;
1165          p1 = &pointfs[c];
1166          p2 = &pointfs[(c == count-1) ? 0 : (c+1)];
1167
1168          if( (p1->x < minX) &&  (p2->x < minX) )
1169          {
1170             if((p1->y <= y) && (p2->y >  y) )
1171                result ^= true;
1172             else if( (p1->y >  y) && (p2->y <= y) )
1173                result ^= true;
1174          }
1175          else if(!((p1->x > maxX && p2->x > maxX) || (p1->y < minY && p2->y < minY) || (p1->y > maxY && p2->y > maxY)))
1176          {
1177             if(p1->y == p2->y)
1178             {
1179                d = y - p1->y;
1180                if (d < 0) d = -d;
1181                if (d < delta) return true;
1182             }
1183             else if(p1->x == p2->x)
1184             {
1185                d = x - p1->x;
1186                if(d < 0) d = -d;
1187                if(d < delta) return true;
1188                else if(p1->x > x) ;
1189                else if( (p1->y <= y) && (p2->y >  y) )
1190                   result ^= true;
1191                else if( (p1->y >  y) && (p2->y <= y) )
1192                   result ^= true;
1193             }
1194             else
1195             {
1196                float a, b, dy, dx;
1197                
1198                a = p2->y - p1->y;
1199                b = p1->x - p2->x;
1200                dy =  a;
1201                dx = -b;
1202                d = a * x + b * y + (p2->x * p1->y) - (p2->y * p1->x);
1203                if (a < 0) a = -a;
1204                if (b < 0) b = -b;
1205                if (d < 0) d = -d;
1206                
1207                if(d < a * delta)
1208                   return true;
1209                else if (d < b * delta)
1210                   return true;
1211                else if( ( (p1->y <= y) && (p2->y >  y) ) || ( (p1->y >  y) && (p2->y <= y) ) )
1212                {
1213                   double xdy;
1214                   
1215                   xdy = (dx * (y - p1->y)) + (dy * p1->x);
1216                   if(dy < 0)
1217                   {
1218                      if(xdy > x * dy)
1219                         result ^= true;
1220                   }
1221                   else if(xdy < x * dy)
1222                      result ^= true;
1223                }
1224             }
1225          }
1226       }
1227       return result;
1228    }
1229
1230    property Transform transform { set { transform = value; } get { value = transform; } };
1231    property Material material { set { material = value; } get { return material; } };
1232    property Vector3Df max { get { value = max; } };
1233    property Vector3Df min { get { value = min; } };
1234    property Vector3Df center { get { value = center; } };
1235    property float radius { get { return radius; } };
1236
1237    property Vector3D wmax { get { value = wmax; } };
1238    property Vector3D wmin { get { value = wmin; } };
1239    property Vector3D wcenter { get { value = wcenter; } };
1240    property double wradius { get { return wradius; } };
1241
1242    property void * tag { set { tag = value; } get { return tag; } };
1243    property int frame { set { Animate(value); } get { return frame; } };
1244    property int startFrame { set { startFrame = value; } get { return startFrame; } };
1245    property int endFrame { set { endFrame = value; } get { return endFrame; } };
1246
1247    property Mesh mesh { set { mesh = value; } get { return mesh; } };
1248    property Camera camera { get { return camera; } }; // Fix this with inheritance? camera inherit from Object?
1249    property Object firstChild { get { return children.first; } };
1250    property Object next { get { return next; } };
1251    property char * name { get { return name; } };
1252    property Matrix matrix { get { value = matrix; } };
1253    property Object cameraTarget { set { cameraTarget = value; } get { return cameraTarget; } };
1254    property OldList * tracks { /* set { tracks = value; } */ get { return &tracks; } };
1255    property ObjectFlags flags { set { flags = value; } get { return flags; } };
1256
1257 private:
1258    Object()
1259    {
1260       children.offset = (uint)&((Object)0).prev;
1261       transform.scaling = { 1, 1, 1 };
1262       transform.orientation = { 1,0,0,0 };
1263       flags.transform = true;
1264    }
1265
1266    ~Object()
1267    {
1268       Free(null);
1269    }
1270
1271    void SetTransformDirty()
1272    {
1273       Object child;
1274       flags.transform = true;
1275       for(child = children.first; child; child = child.next)
1276          child.SetTransformDirty();
1277    }
1278
1279    void _UpdateTransform()
1280    {
1281       if(flags.transform)
1282       {
1283          Object child;
1284          Matrix matrix;
1285
1286          // Cameras / Spot Lights must update their target first
1287          if(flags.camera && cameraTarget && cameraTarget.flags.transform)
1288             cameraTarget.UpdateTransform();
1289          else if(flags.light && light.flags.spot && light.target.flags.transform)
1290             light.target._UpdateTransform();
1291
1292          if(flags.camera && cameraTarget)
1293          {
1294             // DeterMine angle to look at target
1295             Vector3D position, direction;
1296             if(flags.root || !parent)
1297                position = transform.position;
1298             else
1299                position.MultMatrix(transform.position, parent.matrix);
1300
1301             direction.Subtract((Vector3D *)cameraTarget.matrix.m[3], position);
1302             transform.orientation.RotationDirection(direction);
1303
1304             // Add the roll
1305             transform.orientation.RotateRoll(roll);
1306          }
1307
1308          if(flags.light && light.flags.spot)
1309          {
1310             // DeterMine angle to look at target
1311             Vector3D position;
1312             if(flags.root || !parent)
1313                position = transform.position;
1314             else
1315                position.MultMatrix(transform.position, parent.matrix);
1316
1317             light.direction.Subtract((Vector3D *) light.target.matrix.m[3], position);
1318             light.direction.Normalize(light.direction);
1319             transform.orientation.RotationDirection(light.direction);
1320          }
1321
1322          matrix.Identity();
1323          matrix.Scale(transform.scaling.x, transform.scaling.y, transform.scaling.z);
1324          matrix.Rotate(transform.orientation);
1325          matrix.Translate(transform.position.x, transform.position.y, transform.position.z);
1326
1327          localMatrix = matrix;
1328
1329          // Compute transform (with ancestors)
1330          if(flags.root || !parent)
1331             this.matrix = matrix;
1332          else
1333             this.matrix.Multiply(matrix, parent.matrix);
1334
1335          flags.transform = false;
1336
1337          for(child = children.first; child; child = child.next)
1338          {
1339             if(child.flags.transform)
1340                child._UpdateTransform();
1341          }
1342       }
1343    }
1344
1345    void _Animate(unsigned int frame)
1346    {
1347       Object child;
1348       FrameTrack track;
1349
1350       for(track = tracks.first; track; track = track.next)
1351       {
1352          unsigned int c;
1353       
1354          if(track.numKeys)
1355          {
1356             unsigned int prev = 0, next = track.numKeys - 1;
1357             FrameKey * prevKey = &track.keys[prev], * nextKey = &track.keys[next];
1358             float t = 0;
1359
1360             for(c = 0; c<track.numKeys; c++)
1361             {
1362                FrameKey * key = track.keys + c;
1363                if(key->frame <= frame) { prevKey = key; prev = c; }
1364                if(key->frame >= frame) { nextKey = key; next = c; break; }
1365             }
1366
1367             if(nextKey->frame != prevKey->frame)
1368                t = ease((float) (frame - prevKey->frame) / (nextKey->frame - prevKey->frame), prevKey->easeFrom, nextKey->easeTo);
1369
1370             switch(track.type.type)
1371             {
1372                case position: 
1373                {
1374                   Vector3Df position;
1375                   track.Interpolate(position, prevKey->position, nextKey->position, prev, next, t);
1376                   transform.position = { (double)position.x, (double)position.y, (double)position.z };
1377                   break;
1378                }
1379                case scaling:
1380                   track.Interpolate(transform.scaling, prevKey->scaling, &nextKey->scaling, prev, next, t);
1381                   break;
1382                case rotation:
1383                   track.InterpolateQuat(transform.orientation, prevKey->orientation, nextKey->orientation, prev, next, t);
1384                   break;
1385                // Cameras
1386                case roll:
1387                   roll = track.InterpolateFloat(prevKey->roll, nextKey->roll, prev, next, t);
1388                   break;
1389                case fov:
1390                {
1391                   camera.fov = track.InterpolateFloat(prevKey->fov, nextKey->fov, prev, next, t);
1392                   /*
1393                   double mm = (camera.fov - 5.05659508373109) / 1.13613250717301;
1394                   camera.fov = 1248.58921609766 * pow(mm, -0.895625414990581);
1395                   */
1396                   //camera.Setup(camera.width, camera.height, camera.origin);
1397                   break;
1398                }
1399                // Lights
1400                case colorChange:
1401                {
1402                   track.Interpolate((Vector3Df *)&light.diffuse, 
1403                      (Vector3Df *)&prevKey->color, (Vector3Df *)&nextKey->color, prev, next, t);
1404                   light.specular = light.diffuse;
1405                   break;
1406                }
1407                case fallOff:
1408                {
1409                   light.fallOff = track.InterpolateFloat(prevKey->fallOff, nextKey->fallOff, prev, next, t);
1410                   break;
1411                }
1412                case hotSpot:
1413                {
1414                   light.hotSpot = track.InterpolateFloat(prevKey->hotSpot, nextKey->hotSpot, prev, next, t);
1415                   break;
1416                }
1417             }
1418          }
1419       }
1420
1421       for(child = children.first; child; child = child.next)
1422          child._Animate(frame);
1423
1424       flags.transform = true;
1425    }
1426
1427    // Private for now
1428    FrustumPlacement InsideFrustum(Plane * planes)
1429    {
1430       FrustumPlacement result = inside;
1431
1432       int p;
1433       // First test: Sphere
1434       for(p = 0; p<6; p++)
1435       {
1436          Plane * plane = &planes[p];
1437          double dot = plane->normal.DotProduct(wcenter);
1438          double distance = dot + plane->d;
1439          if(distance < -wradius)
1440          {
1441             result = outside;
1442             break;
1443          }
1444          if(Abs(distance) < wradius)
1445             result = intersecting;
1446       }
1447
1448       if(result == intersecting)
1449       {
1450          // Second test: Bounding Box
1451          Vector3D box[8] =
1452          {
1453             { wmin.x, wmin.y, wmin.z },
1454             { wmin.x, wmin.y, wmax.z },
1455             { wmin.x, wmax.y, wmin.z },
1456             { wmin.x, wmax.y, wmax.z },
1457             { wmax.x, wmin.y, wmin.z },
1458             { wmax.x, wmin.y, wmax.z },
1459             { wmax.x, wmax.y, wmin.z },
1460             { wmax.x, wmax.y, wmax.z }
1461          };
1462            int numPlanesAllIn = 0;
1463            for(p = 0; p < 6; p++) 
1464          {
1465             Plane * plane = &planes[p];
1466             int i;
1467             int numGoodPoints = 0;
1468            for(i = 0; i < 8; ++i) 
1469             {
1470                double dot = plane->normal.DotProduct(box[i]);
1471                double distance = dot + plane->d;
1472                            if(distance > -1)
1473                   numGoodPoints++;
1474                    }
1475                    if(!numGoodPoints)
1476             {
1477                result = outside;
1478                break;
1479             }
1480             if(numGoodPoints == 8)
1481                numPlanesAllIn++;
1482            }
1483            if(numPlanesAllIn == 6)
1484                    result = inside;
1485       }
1486       return result;
1487    }
1488
1489    Object prev, next;
1490    char * name;
1491    Object parent;
1492    OldList children;
1493
1494    ObjectFlags flags;
1495
1496    OldList tracks;
1497    unsigned startFrame, endFrame;
1498    int frame;
1499    Vector3Df pivot;
1500    Transform transform;
1501    Matrix matrix;
1502    Matrix localMatrix;
1503    void * tag;
1504    Vector3Df min, max, center;
1505    Vector3D wmin, wmax, wcenter;
1506
1507    bool volume;
1508    float radius;
1509    double wradius;
1510    ColorRGB ambient;
1511
1512    /*public */union
1513    {
1514       // Mesh
1515       struct
1516       {
1517          Mesh mesh;
1518          Material material;
1519       };
1520
1521       // Light
1522       Light light;
1523
1524       // Camera
1525       struct
1526       {
1527          Camera camera;
1528          Object cameraTarget;
1529          double roll;
1530       };
1531    };
1532
1533    public property Light light
1534    {
1535       set
1536       {
1537          light = value;
1538       }
1539       get
1540       {
1541          value = light;
1542       }
1543    }
1544
1545    Euler eulerOrientation;
1546 };