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