Unstaged changes working on Android support
[sdk] / ecere / src / gfx / 3D / Mesh.ec
1 namespace gfx3D;
2
3 import "Display"
4
5 public class MeshFeatures { public bool vertices:1, normals:1, texCoords1:1, texCoords2:1, doubleNormals:1, doubleVertices:1, colors:1; };
6 public class PrimitiveGroupType { public: RenderPrimitiveType primitiveType:8; bool vertexRange:1, indices32bit:1; };
7 public enum RenderPrimitiveType : PrimitiveGroupType
8 {
9    dot, // Point,
10    lines,
11    triangles,
12    triStrip,
13    triFan,
14    quads,
15    quadStrip,
16    lineStrip
17
18    /* ,
19    lineLoop,
20    lineStrip,
21    polygon
22    */
23 };
24
25 public class MaterialFlags { public bool doubleSided:1, translucent:1, tile:1, noFog:1, singleSideLight:1; };
26 public class Material : struct
27 {
28 public:
29    Material prev, next;
30    char * name;
31    float opacity;
32    ColorRGB diffuse;
33    ColorRGB ambient;
34    ColorRGB specular;
35    ColorRGB emissive;
36    float power;
37    Bitmap baseMap;
38    Bitmap bumpMap;
39    Bitmap envMap;
40    MaterialFlags flags;
41    float uScale, vScale;
42
43    void Free()
44    {
45       delete name;
46    }
47 };
48
49 public class PrimitiveGroup : struct
50 {
51 public:
52    PrimitiveGroup prev, next;
53    PrimitiveGroupType type;
54    union
55    {
56       struct { union { uint16 * indices; uint * indices32; }; int nIndices; };
57       struct { int first, nVertices; };
58    };
59    Material material;
60
61 private:
62    void * data;
63 };
64
65 /*
66 public class PrimitiveGroupIndices16 : PrimitiveGroup
67 {
68    property Array<uint16> indices
69    {
70       set { }
71    }
72 }
73
74 public class PrimitiveGroupIndices32 : PrimitiveGroup
75 {
76    property Array<uint> indices
77    {
78       set { }
79    }
80 }
81
82 public class PrimitiveGroupVertexRange : PrimitiveGroup
83 {
84    property int first { set { } }
85    property int nVertices { set { } }
86 }
87 */
88
89 public struct PrimitiveSingle
90 {
91 public:
92    PrimitiveGroupType type;
93    union
94    {
95       struct { union { uint16 * indices; uint * indices32; }; int nIndices; };
96       struct { int first, nVertices; };
97    };
98    Material material;
99
100 private:
101    void * data;
102    Vector3Df middle;
103    Plane plane;
104 };
105
106 public class Mesh : struct
107 {
108 public:
109    property Pointf * texCoords { get { return texCoords; } set { texCoords = value; } };
110    property int nVertices { get { return nVertices; } set { nVertices = value; } };
111    property Vector3Df * vertices { get { return vertices; } set { vertices = value; } };
112    property Vector3Df * normals { get { return normals; } set { normals = value; } };
113    property ColorRGBAf * colors { get { return colors; } set { colors = value; } };
114    property OldList groups { get { value = groups; } };
115    property MeshFeatures flags { get { return flags; } set { flags = value; } };
116
117    void Free(MeshFeatures what)
118    {
119       if(this)
120       {
121          DisplaySystem displaySystem = this.displaySystem;
122          PrimitiveGroup group;
123          if(!what)
124          {
125             int c;
126
127             flags = 0;
128             if(driver)
129                driver.FreeMesh(displaySystem, this);
130             for(;(group = groups.first);)
131                FreePrimitiveGroup(group);
132
133             for(c = 0; c<nPrimitives; c++)
134             {
135                if(primitives[c].data)
136                   driver.FreeIndices(displaySystem, primitives[c].data);
137             }
138
139             delete primitives;
140             nPrimitives = 0;
141             nVertices = 0;
142             this.displaySystem = null;
143             driver = null;
144          }
145          else
146          {
147             flags &= ~what;
148             if(driver)
149                driver.FreeMesh(displaySystem, this);
150          }
151       }
152    }
153
154    bool Allocate(MeshFeatures what, int nVertices, DisplaySystem displaySystem)
155    {
156       bool result = false;
157       if(!this.displaySystem || this.displaySystem == displaySystem)
158       {
159          driver = displaySystem ? displaySystem.driver : (subclass(DisplayDriver))class(LFBDisplayDriver);
160          if(driver.AllocateMesh == DisplayDriver::AllocateMesh) driver = (subclass(DisplayDriver))class(LFBDisplayDriver);
161          if(driver.AllocateMesh(displaySystem, this, what, nVertices))
162          {
163             flags |= what;
164             this.nVertices = nVertices;
165             if(Lock(what))
166                result = true;
167          }
168          if(!result)
169             Free(what);
170          this.displaySystem = displaySystem;
171       }
172       return result;
173    }
174
175    void Unlock(MeshFeatures flags)
176    {
177       if(this && driver)
178          driver.UnlockMesh(displaySystem, this, flags);
179    }
180
181    bool Lock(MeshFeatures flags)
182    {
183       bool result = false;
184       if(this && driver)
185       {
186          if(driver.LockMesh(displaySystem, this, flags))
187             result = true;
188          if(!result)
189             Unlock(flags);
190       }
191       return result;
192    }
193
194    void FreePrimitiveGroup(PrimitiveGroup group)
195    {
196       if(this && group)
197       {
198          if(group.data)
199             driver.FreeIndices(displaySystem, group.data);
200          groups.Delete(group);
201       }
202    }
203
204    PrimitiveGroup AddPrimitiveGroup(PrimitiveGroupType flags, int nIndices)
205    {
206       PrimitiveGroup result = null;
207       PrimitiveGroup group { };
208       if(group)
209       {
210          groups.Add(group);
211          group.type = flags;
212          if(!(flags.vertexRange))
213          {
214             group.nIndices = nIndices;
215             if(driver)
216             {
217                group.data = driver.AllocateIndices(displaySystem, nIndices, flags.indices32bit);
218                if(group.data)
219                {
220                   if(LockPrimitiveGroup(group))
221                      result = group;
222                }
223             }
224             else
225                result = group;
226          }
227          else
228             result = group;
229          if(!result)
230             FreePrimitiveGroup(group);
231       }
232       return result;
233    }
234
235    bool LockPrimitiveGroup(PrimitiveGroup group)
236    {
237       bool result = false;
238       if(this && group)
239       {
240          if(group.data)
241             group.indices = driver.LockIndices(displaySystem, group.data);
242          if(group.indices || group.type.vertexRange)
243             result = true;
244       }
245       return result;
246    }
247
248    void UnlockPrimitiveGroup(PrimitiveGroup group)
249    {
250       if(this && group && group.data)
251       {
252          driver.UnlockIndices(displaySystem, group.data, group.type.indices32bit, group.nIndices);
253          //group.indices = null;
254       }
255    }
256
257    void FreePrimitive(PrimitiveSingle primitive)
258    {
259       if(this && primitive)
260       {
261          if(primitive.data)
262             driver.FreeIndices(displaySystem, primitive.data);
263          primitive.data = null;
264       }
265    }
266
267    bool AllocatePrimitive(PrimitiveSingle primitive, PrimitiveGroupType flags, int nIndices)
268    {
269       bool result = false;
270       if(this && primitive)
271       {
272          primitive.type = flags;
273          primitive.data = driver.AllocateIndices(displaySystem, nIndices, flags.indices32bit);
274          primitive.nIndices = nIndices;
275          if(primitive.data)
276          {
277             if(LockPrimitive(primitive))
278                result = true;
279          }
280          if(!result)
281             FreePrimitive(primitive);
282       }
283       return result;
284    }
285
286    void UnlockPrimitive(PrimitiveSingle primitive)
287    {
288       if(this && primitive)
289       {
290          driver.UnlockIndices(this.displaySystem, primitive.data, primitive.type.indices32bit, primitive.nIndices);
291          //primitive.indices = null;
292       }
293    }
294
295    bool LockPrimitive(PrimitiveSingle primitive)
296    {
297       bool result = false;
298       if(this && primitive)
299       {
300          primitive.indices = driver.LockIndices(displaySystem, primitive.data);
301          if(primitive.indices)
302             result = true;
303       }
304       return result;
305    }
306
307    void ComputeNormals(void)
308    {
309       int c;
310       int * numShared = new0 int[nVertices];
311       PrimitiveGroup group;
312
313       if(Allocate({ normals = true }, nVertices, displaySystem))
314       {
315          FillBytes(normals, 0, nVertices * sizeof(Vector3Df));
316          for(group = groups.first; group; group = group.next)
317          {
318             int c;
319             int offset = 0;
320             int strip = 0;
321             int nPoints, nIndex;
322             uint16 * indices16 = group.indices;
323             uint32 * indices32 = group.indices32;
324             bool i32bit = group.type.indices32bit;
325
326             if(group.type.primitiveType == triangles)
327                nIndex = nPoints = 3;
328             else if(group.type.primitiveType == quads)
329                nIndex = nPoints = 4;
330             else if(group.type.primitiveType == triFan || group.type.primitiveType == triStrip || group.type.primitiveType == quadStrip)
331             {
332                offset = 2;
333                nIndex = 1;
334                nPoints = 3;
335             }
336             else
337                continue;
338             /*
339             int nIndicesPerPrimitive;
340             if(group.type == Triangles)
341                nIndicesPerPrimitive = 3;
342             else if(group.type == Quads)
343                nIndicesPerPrimitive = 4;
344             else
345                continue;
346             */
347             for(c = offset; c<group.nIndices; c += nIndex)
348             {
349                int i;
350                Plane plane;
351                Vector3Df planeNormal;
352
353                if(group.type.primitiveType == triFan)
354                {
355                   plane.FromPointsf(vertices[group.indices[0]],
356                                    vertices[group.indices[c]],
357                                    vertices[group.indices[c-1]]);
358                   planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z };
359
360                   normals[group.indices[0]].Add(normals[group.indices[0]], planeNormal);
361                   numShared[group.indices[0]]++;
362                   normals[group.indices[c-1]].Add(normals[group.indices[c-1]], planeNormal);
363                   numShared[group.indices[c-1]]++;
364                   normals[group.indices[c]].Add(normals[group.indices[c]], planeNormal);
365                   numShared[group.indices[c]]++;
366                }
367                else if(group.type.primitiveType == triStrip || group.type.primitiveType == quadStrip)
368                {
369                   plane.FromPointsf(vertices[group.indices[c-1-strip]],
370                                    vertices[group.indices[c-2+strip]],
371                                    vertices[group.indices[c]]);
372                   planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z };
373
374                   normals[group.indices[c-1-strip]].Add(normals[group.indices[c-1-strip]], planeNormal);
375                   numShared[group.indices[c-1-strip]]++;
376                   normals[group.indices[c-2+strip]].Add(normals[group.indices[c-2+strip]], planeNormal);
377                   numShared[group.indices[c-2+strip]]++;
378                   normals[group.indices[c]].Add(normals[group.indices[c]], planeNormal);
379                   numShared[group.indices[c]]++;
380
381                   strip ^= 1;
382                }
383                else
384                {
385                   if(group.type.vertexRange)
386                   {
387                      plane.FromPointsf(vertices[c+2],
388                                       vertices[c+1],
389                                       vertices[c]);
390                      planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z };
391
392                      for(i = c; i<c+nIndex; i++)
393                      {
394                         normals[i].Add(normals[i], planeNormal);
395                         numShared[i] ++;
396                      }
397                   }
398                   else
399                   {
400                      plane.FromPointsf(vertices[i32bit ? indices32[c+2] : indices16[c+2]],
401                                       vertices[i32bit ? indices32[c+1] : indices16[c+1]],
402                                       vertices[i32bit ? indices32[c] : indices16[c]]);
403                      planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z };
404
405                      for(i = c; i<c+nIndex; i++)
406                      {
407                         int index = i32bit ? indices32[i] : indices16[i];
408                         normals[index].Add(normals[index], planeNormal);
409                         numShared[index] ++;
410                      }
411                   }
412                }
413             }
414          }
415          // NOTE: Here we're currently making the assumption that the primitives are in indices mode (vertexRange = false)
416          for(c = 0; c<nPrimitives; c++)
417          {
418             int i;
419             PrimitiveSingle * primitive = &primitives[c];
420
421             Plane plane;
422             Vector3Df planeNormal;
423             plane.FromPointsf(vertices[primitive->indices[2]],
424                              vertices[primitive->indices[1]],
425                              vertices[primitive->indices[0]]);
426             planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z };
427
428             if(primitive->material.flags.doubleSided && plane.d < 0)
429             {
430                planeNormal.x *= -1;
431                planeNormal.y *= -1;
432                planeNormal.z *= -1;
433             }
434
435             for(i = 0; i<primitive->nIndices; i++)
436             {
437                normals[primitive->indices[i]].Add(normals[primitive->indices[i]], planeNormal);
438                numShared[primitive->indices[i]] ++;
439             }
440          }
441
442          for(c = 0; c<nVertices; c++)
443          {
444             normals[c].Scale(normals[c], 1.0f / numShared[c]);
445             normals[c].Normalize(normals[c]);
446          }
447          delete numShared;
448          Unlock({ normals = true });
449       }
450    }
451
452
453    void ApplyMaterial(Material material)
454    {
455       if(this)
456       {
457          int c;
458          PrimitiveGroup group;
459          for(group = groups.first; group; group = group.next)
460             group.material = material;
461          for(c = 0; c<nPrimitives; c++)
462             primitives[c].material = material;
463       }
464    }
465
466    bool ApplyTranslucency(Object object)
467    {
468       bool result = false;
469       if(this)
470       {
471          PrimitiveGroup group, nextGroup;
472          int c;
473
474          // Merge non translucent primitives into groups
475          for(c = 0; c<nPrimitives; )
476          {
477             PrimitiveSingle * primitive = &primitives[c];
478             Material material = (primitive->material || !object) ? primitive->material : object.material;
479             if(!material || !(material.flags.translucent))
480             {
481                int t;
482                PrimitiveGroup group;
483                int nIndices = primitive->nIndices;
484                for(t = c+1; t<nPrimitives; t++)
485                {
486                   PrimitiveSingle * prim = &primitives[t];
487                   if(prim->type == primitive->type && prim->material == primitive->material)
488                      nIndices += prim->nIndices;
489                }
490                group = AddPrimitiveGroup(primitive->type, nIndices);
491                if(group)
492                {
493                   nIndices = 0;
494                   group.material = material;
495                   for(t = c; t<nPrimitives; t++)
496                   {
497                      primitive = &primitives[t];
498                      if(group.type == primitive->type && group.material == primitive->material)
499                      {
500                         CopyBytesBy2(group.indices + nIndices,primitive->indices,primitive->nIndices);
501                         nIndices +=primitive->nIndices;
502                         CopyBytes(primitives + t, primitives + t + 1, (nPrimitives - t - 1) * sizeof(PrimitiveSingle));
503                         nPrimitives--;
504                         t--;
505                      }
506                   }
507                   UnlockPrimitiveGroup(group);
508                }
509             }
510             else
511                c++;
512          }
513          primitives = renew primitives PrimitiveSingle[this.nPrimitives];
514
515          // Split translucent groups into primitives
516          for(group = groups.first; group; group = nextGroup)
517          {
518             Material material = (group.material || !object) ? group.material : object.material;
519
520             nextGroup = group.next;
521
522             if(material && material.flags.translucent)
523             {
524                int nPrimitives = 0, c;
525                int offset = 0;
526                int strip = 0;
527                int nPoints, nIndex;
528                int groupCount = group.type.vertexRange ? group.nVertices : group.nIndices;
529                if(!groupCount) continue;
530
531                if(group.type.primitiveType == triangles)
532                   nIndex = nPoints = 3;
533                else if(group.type.primitiveType == quads)
534                   nIndex = nPoints = 4;
535                else if(group.type.primitiveType == triFan || group.type.primitiveType == triStrip)
536                {
537                   offset = 2;
538                   nIndex = 1;
539                   nPoints = 3;
540                }
541                else
542                   continue;
543
544                nPrimitives += (groupCount - offset) / nIndex;
545
546                primitives = renew primitives PrimitiveSingle[this.nPrimitives + nPrimitives];
547
548                for(c = offset; c<groupCount; c+= nIndex)
549                {
550                   PrimitiveSingle * primitive = &primitives[this.nPrimitives++];
551
552                   if(AllocatePrimitive(primitive, group.type.primitiveType, nPoints))
553                   {
554                      if(group.type.vertexRange)
555                      {
556                         if(group.type.primitiveType == triangles || group.type.primitiveType == quads)
557                         {
558                            primitive->indices[0] = (uint16)(group.first + c);
559                            primitive->indices[1] = (uint16)(group.first + c+1);
560                            primitive->indices[2] = (uint16)(group.first + c+2);
561                         }
562                         if(group.type.primitiveType == quads)
563                            primitive->indices[3] = (uint16)(group.first + c+3);
564
565                         if(group.type.primitiveType == triFan)
566                         {
567                            primitive->indices[0] = (uint16)group.first;
568                            primitive->indices[1] = (uint16)(group.first + c-1);
569                            primitive->indices[2] = (uint16)(group.first + c);
570                         }
571                         else if(group.type.primitiveType == triStrip)
572                         {
573                            primitive->indices[0] = (uint16)(group.first + c-1-strip);
574                            primitive->indices[1] = (uint16)(group.first + c-2+strip);
575                            primitive->indices[2] = (uint16)(group.first + c);
576                            strip ^= 1;
577                         }
578                      }
579                      else
580                      {
581                         if(group.type.primitiveType == triangles || group.type.primitiveType == quads)
582                            CopyBytesBy2(primitive->indices, group.indices + c, nIndex);
583
584                         if(group.type.primitiveType == triFan)
585                         {
586                            primitive->indices[0] = group.indices[0];
587                            primitive->indices[1] = group.indices[c-1];
588                            primitive->indices[2] = group.indices[c];
589                         }
590                         else if(group.type.primitiveType == triStrip)
591                         {
592                            primitive->indices[0] = group.indices[c-1-strip];
593                            primitive->indices[1] = group.indices[c-2+strip];
594                            primitive->indices[2] = group.indices[c];
595                            strip ^= 1;
596                         }
597                      }
598                      primitive->material = group.material;
599
600                      primitive->plane.FromPointsf(
601                         vertices[primitive->indices[2]],
602                         vertices[primitive->indices[1]],
603                         vertices[primitive->indices[0]]);
604
605                      primitive->middle.Add(vertices[primitive->indices[0]], vertices[primitive->indices[1]]);
606                      primitive->middle.Add(primitive->middle, vertices[primitive->indices[2]]);
607                      if(group.type == quads)
608                         primitive->middle.Add(primitive->middle, vertices[primitive->indices[3]]);
609                      primitive->middle.x /= nPoints;
610                      primitive->middle.y /= nPoints;
611                      primitive->middle.z /= nPoints;
612
613                      UnlockPrimitive(primitive);
614                   }
615                }
616                FreePrimitiveGroup(group);
617             }
618          }
619          result = true;
620
621          if(object)
622             object.flags.translucent = nPrimitives ? true : false;
623       }
624       return result;
625    }
626
627    void * GetData()
628    {
629       return data;
630    }
631
632 private:
633
634    void SetMinMaxRadius(void)
635    {
636       int c;
637
638       float xRadius, yRadius, zRadius;
639
640       min = { MAXFLOAT, MAXFLOAT, MAXFLOAT };
641       max = {-MAXFLOAT,-MAXFLOAT,-MAXFLOAT };
642
643       for(c = 0; c<nVertices; c++)
644       {
645          float x = vertices[c].x, y = vertices[c].y, z = vertices[c].z;
646          if(x.isNan || y.isNan || z.isNan);
647          else if(x > 1E20 || x < -1E20 || y > 1E20 || y < -1E20 || z > 1E20 || z < -1E20);
648          else
649          {
650             min.x = Min(min.x, x);
651             min.y = Min(min.y, y);
652             min.z = Min(min.z, z);
653             max.x = Max(max.x, x);
654             max.y = Max(max.y, y);
655             max.z = Max(max.z, z);
656          }
657       }
658       xRadius = Max(max.x, -min.x);
659       yRadius = Max(max.y, -min.y);
660       zRadius = Max(max.z, -min.z);
661
662       radius = Max(xRadius, yRadius);
663       radius = Max(radius, zRadius);
664    }
665
666    void DoubleSided(bool flag)
667    {
668       if(this)
669       {
670          PrimitiveGroup group;
671          int c;
672          for(group = groups.first; group; group = group.next)
673          {
674             if(group.material)
675             {
676                group.material.flags.doubleSided = flag;
677             }
678          }
679          for(c = 0; c<nPrimitives; c++)
680          {
681             PrimitiveSingle * primitive = &primitives[c];
682             if(primitive->material)
683             {
684                primitive->material.flags.doubleSided = flag;
685             }
686          }
687       }
688    }
689
690    MeshFeatures flags;
691    int nVertices;
692    Vector3Df * vertices;
693    Vector3Df * normals;
694    Pointf * texCoords;
695    ColorRGBAf * colors;
696    OldList groups;
697    int nPrimitives;
698    PrimitiveSingle * primitives;
699    Vector3Df min, max;
700    float radius;
701
702    // Private Data
703    DisplaySystem displaySystem;
704    subclass(DisplayDriver) driver;
705    void * data;
706 };