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