Skip to content

Commit a720e0c

Browse files
committed
ecere/gfx/3D: Mesh; E3D: Initial support for morphs
1 parent e9ec546 commit a720e0c

File tree

5 files changed

+274
-17
lines changed

5 files changed

+274
-17
lines changed

ecere/src/gfx/3D/Mesh.ec

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,19 @@ public struct MeshPart
270270
uint count;
271271
};
272272

273+
public struct MeshMorph
274+
{
275+
Mesh target;
276+
float weight;
277+
String name;
278+
279+
void OnFree()
280+
{
281+
delete target; // For now, target is owned here
282+
delete name;
283+
}
284+
};
285+
273286
public class Mesh : struct
274287
{
275288
public:
@@ -295,9 +308,106 @@ public:
295308
set { dupVerts = value; }
296309
get { return dupVerts; }
297310
};
311+
property Array<MeshMorph> morphs
312+
{
313+
set { if(morphs) morphs.Free(), delete morphs; morphs = value; }
314+
get { return morphs; }
315+
}
316+
property Mesh unmorphedMesh
317+
{
318+
set { delete unmorphedMesh; unmorphedMesh = value; }
319+
get { return unmorphedMesh; }
320+
}
298321

299322
#define GPU_SKIN
300323

324+
void ApplyMorphs()
325+
{
326+
Array<MeshMorph> morphs = this.morphs;
327+
if(morphs && !unmorphedMesh)
328+
unmorphedMesh = Copy();
329+
if(morphs)
330+
{
331+
int i;
332+
int nMorphs = morphs.count, m;
333+
int nVertices = Min(this.nVertices, unmorphedMesh.nVertices);
334+
Vector3Df * vertices = this.vertices, * unmVertices = unmorphedMesh.vertices;
335+
int dvCount = dupVerts ? dupVerts.count : 0;
336+
// TODO: apply same computed delta approach to tangents; light vectors?
337+
Vector3Df * unmNormals = unmorphedMesh.normals, * computedUnmorphedNormals = this.computedUnmorphedNormals;
338+
if(!computedUnmorphedNormals)
339+
{
340+
Vector3Df * origUNMNormals = unmNormals;
341+
342+
computedUnmorphedNormals = new Vector3Df[unmorphedMesh.nVertices];
343+
this.computedUnmorphedNormals = computedUnmorphedNormals;
344+
unmorphedMesh.normals = computedUnmorphedNormals;
345+
unmorphedMesh.ComputeNormals2(true, true);
346+
unmorphedMesh.normals = origUNMNormals;
347+
}
348+
349+
memcpy(vertices, unmVertices, (nVertices - dvCount) * sizeof(Vector3Df));
350+
351+
for(m = 0; m < nMorphs; m++)
352+
{
353+
MeshMorph morph = morphs[m];
354+
__attribute__((unused)) const String n = morph.name;
355+
float w = morph.weight;
356+
Mesh target = morph.target;
357+
if(w && target)
358+
{
359+
int nv = Min(nVertices, target.nVertices);
360+
const Vector3Df * sv = unmVertices, * tv = target.vertices;
361+
Vector3Df * v = vertices;
362+
363+
for(i = 0; i < nv; i++, v++, sv++, tv++)
364+
{
365+
float dx = (tv->x - sv->x) * w;
366+
float dy = (tv->y - sv->y) * w;
367+
float dz = (tv->z - sv->z) * w;
368+
v->x += dx, v->y += dy, v->z += dz;
369+
}
370+
}
371+
}
372+
373+
if(dupVerts)
374+
{
375+
Vector3Df * v = vertices + nVertices - dvCount;
376+
377+
for(i = 0; i < dvCount; i++, v++)
378+
{
379+
int dv = dupVerts[i];
380+
*v = vertices[dv];
381+
}
382+
}
383+
384+
ComputeNormals2(true, true);
385+
386+
// Re-orient original normals based on rotation of computed normals
387+
for(i = 0; i < nVertices; i++)
388+
{
389+
Vector3D axis;
390+
double len;
391+
Vector3Df normal1 = computedUnmorphedNormals[i], normal0 = normals[i];
392+
393+
axis.CrossProduct(
394+
{ normal0.x, normal0.y, normal0.z },
395+
{ normal1.x, normal1.y, normal1.z });
396+
397+
len = axis.length;
398+
if(len)
399+
{
400+
double dot = normal0.DotProduct(normal1);
401+
Degrees angle = atan2(len, dot);
402+
Quaternion q; // q is rotation between computed normals
403+
axis.Scale(axis, 1.0 / len);
404+
q.RotationAxis(axis, angle);
405+
normals[i].MultQuaternion(unmNormals[i], q);
406+
}
407+
}
408+
}
409+
}
410+
301411
void ApplySkin()
302412
{
303413
MeshSkin skin = this.skin;
@@ -635,6 +745,10 @@ public:
635745
driver.FreeMesh(displaySystem, this);
636746
}
637747
delete parts;
748+
749+
delete unmorphedMesh;
750+
delete computedUnmorphedNormals;
751+
if(morphs) morphs.Free(), delete morphs;
638752
}
639753
}
640754

@@ -2020,6 +2134,10 @@ private:
20202134
Array<Matrixf> matBones;
20212135
SkinVert * boneData; // For uploading to GPU
20222136
#endif
2137+
2138+
Array<MeshMorph> morphs;
2139+
Mesh unmorphedMesh;
2140+
Vector3Df * computedUnmorphedNormals; // Normals as computed by ComputeNormals2() for unmorphed mesh
20232141
};
20242142

20252143
void computeNormalWeights(int n, float * vertices, uint vStride, uint * indices, bool ix32Bit, int base, double * weights, Vector3D * edges, Vector3D * rEdges)

ecere/src/gfx/3D/Object.ec

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,27 @@ public:
17611761
SetMinMaxRadius(false);
17621762
}
17631763

1764+
private bool _ApplyMorphs()
1765+
{
1766+
bool result = false;
1767+
Object o;
1768+
if(flags.mesh && mesh && mesh.morphs)
1769+
{
1770+
mesh.ApplyMorphs();
1771+
// flags.morphApplied = true;
1772+
result = true;
1773+
}
1774+
for(o = children.first; o; o = o.next)
1775+
result |= o._ApplyMorphs();
1776+
return result;
1777+
}
1778+
1779+
public void ApplyMorphs()
1780+
{
1781+
_ApplyMorphs();
1782+
SetMinMaxRadius(false);
1783+
}
1784+
17641785
void ResetPose()
17651786
{
17661787
Object o;

ecere/src/gfx/3D/models/e3d/e3dDefs.ec

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ enum E3DBlockType : uint16
1515
meshID = 0x1020,
1616
meshBBox = 0x1021,
1717
meshDuplVerts = 0x1022,
18-
attributes = 0x2000,
18+
attributes = 0x2000, // uint count (limit of 65,536 vertices)
1919
attrVertices = 0x2010, // float x,y,z vertices
2020
attrVerticesDbl = 0x2011, // double x,y,z vertices
2121
attrQVertices = 0x2018, // quantized x,y,z 16-bit vertices (first, deltas)
@@ -56,15 +56,20 @@ enum E3DBlockType : uint16
5656
skinBoneWeights = 0x1054, // byte 0..7: set of bone weights to use for this skin
5757

5858
parts = 0x1060,
59-
59+
morphs = 0x1070, // integer Count
60+
morph = 0x1071,
61+
morphID = 0x1072,
62+
morphName = 0x1073,
63+
morphWeight = 0x1074, // TODO: Use morph weights uniforms for GPU morphs?
64+
// [meshID (reference)] // TODO: Load as vertex attribute instead for GPU morphs? Keep as meshID for re-use?
6065
nodes = 0x3000,
6166
meshNode = 0x3010,
6267
nodeID = 0x3020,
6368
nodeName = 0x3021,
6469
scaling = 0x3030,
6570
orientation = 0x3031,
6671
position = 0x3032,
67-
skeleton = 0x3040,
72+
skeleton = 0x3040, // Should we change this to have skeletonID, skeletonName blocks?
6873
// Can have sub-nodes!
6974

7075
cameraNode = 0x3011,
@@ -144,7 +149,7 @@ enum E3DBlockType : uint16
144149
ftkLightFallOff = 0xA2A0, // Light fall off -- float light fall off (in degrees) per key
145150
ftkLightColor = 0xA2B0, // Light color -- 3 (r,g,b) float 0..1 light color per key
146151
ftkHide = 0xA2C0, // Hide node -- 1 boolean byte (0: displayed, 1: hidden) per key
147-
ftkMorph = 0xA300 // Morph -- Reserved for morph definition (per key)
152+
ftkMorph = 0xA300 // Morph -- int morph index, float weight (per key)
148153
};
149154

150155
struct E3DBlockHeader
@@ -283,6 +288,7 @@ class E3DWriteContext : struct
283288
Array<Object> allAnimatedObjects { };
284289
Map<uintptr, int> objectToNodeID { };
285290
uint nodeID;
291+
uint morphID;
286292

287293
~E3DWriteContext()
288294
{

ecere/src/gfx/3D/models/e3d/e3dRead.ec

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,12 @@ static void readBlocks(E3DContext ctx, File f, DisplaySystem displaySystem, E3DB
592592
object.SetMinMaxRadius(false);
593593
}
594594
}
595+
else if(containerType == morph)
596+
{
597+
MeshMorph * morph = &mesh.morphs[mesh.morphs.count];
598+
Mesh target = ctx.meshesByID[id];
599+
morph->target = target;
600+
}
595601
else
596602
ctx.meshesByID[id] = data;
597603
break;
@@ -898,6 +904,44 @@ static void readBlocks(E3DContext ctx, File f, DisplaySystem displaySystem, E3DB
898904
// PrintLn("Duplicate Vertices!");
899905
break;
900906
}
907+
case morphs:
908+
{
909+
int count = 0;
910+
f.Read(&count, sizeof(int), 1);
911+
pos += sizeof(int);
912+
if(count && !mesh.morphs)
913+
{
914+
mesh.morphs = { minAllocSize = count };
915+
readSubBlocks = true;
916+
}
917+
break;
918+
}
919+
case morph:
920+
{
921+
readSubBlocks = true;
922+
break;
923+
}
924+
case morphID:
925+
{
926+
// Assuming sequential ID starting from 0 for now
927+
int id = 0;
928+
f.Read(&id, sizeof(int), 1);
929+
break;
930+
}
931+
case morphName:
932+
{
933+
MeshMorph * morph = &mesh.morphs[mesh.morphs.count];
934+
morph->name = readString(f);
935+
break;
936+
}
937+
case morphWeight:
938+
{
939+
MeshMorph * morph = &mesh.morphs[mesh.morphs.count];
940+
float w = 0;
941+
f.Read(&w, sizeof(float), 1);
942+
morph->weight = w;
943+
break;
944+
}
901945
case skin:
902946
readSubBlocks = true;
903947
break;
@@ -1007,6 +1051,8 @@ static void readBlocks(E3DContext ctx, File f, DisplaySystem displaySystem, E3DB
10071051
}
10081052
if(readSubBlocks)
10091053
readBlocks(ctx, f, displaySystem, header.type, pos, bEnd, subData);
1054+
if(header.type == morph && mesh.morphs && mesh.morphs.minAllocSize >= mesh.morphs.count + 1)
1055+
mesh.morphs.count++;
10101056
if(header.type == material)
10111057
{
10121058
Material mat = subData;

0 commit comments

Comments
 (0)