Skip to content

Commit

Permalink
ecere/gfx/3D: Mesh; E3D: Initial support for morphs
Browse files Browse the repository at this point in the history
  • Loading branch information
jerstlouis committed Mar 14, 2023
1 parent e9ec546 commit a720e0c
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 17 deletions.
118 changes: 118 additions & 0 deletions ecere/src/gfx/3D/Mesh.ec
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,19 @@ public struct MeshPart
uint count;
};

public struct MeshMorph
{
Mesh target;
float weight;
String name;

void OnFree()
{
delete target; // For now, target is owned here
delete name;
}
};

public class Mesh : struct
{
public:
Expand All @@ -295,9 +308,106 @@ public:
set { dupVerts = value; }
get { return dupVerts; }
};
property Array<MeshMorph> morphs
{
set { if(morphs) morphs.Free(), delete morphs; morphs = value; }
get { return morphs; }
}
property Mesh unmorphedMesh
{
set { delete unmorphedMesh; unmorphedMesh = value; }
get { return unmorphedMesh; }
}

#define GPU_SKIN

void ApplyMorphs()
{
Array<MeshMorph> morphs = this.morphs;
if(morphs && !unmorphedMesh)
unmorphedMesh = Copy();
if(morphs)
{
int i;
int nMorphs = morphs.count, m;
int nVertices = Min(this.nVertices, unmorphedMesh.nVertices);
Vector3Df * vertices = this.vertices, * unmVertices = unmorphedMesh.vertices;
int dvCount = dupVerts ? dupVerts.count : 0;
// TODO: apply same computed delta approach to tangents; light vectors?
Vector3Df * unmNormals = unmorphedMesh.normals, * computedUnmorphedNormals = this.computedUnmorphedNormals;
if(!computedUnmorphedNormals)
{
Vector3Df * origUNMNormals = unmNormals;

computedUnmorphedNormals = new Vector3Df[unmorphedMesh.nVertices];
this.computedUnmorphedNormals = computedUnmorphedNormals;
unmorphedMesh.normals = computedUnmorphedNormals;
unmorphedMesh.ComputeNormals2(true, true);
unmorphedMesh.normals = origUNMNormals;
}

memcpy(vertices, unmVertices, (nVertices - dvCount) * sizeof(Vector3Df));

for(m = 0; m < nMorphs; m++)
{
MeshMorph morph = morphs[m];
__attribute__((unused)) const String n = morph.name;
float w = morph.weight;
Mesh target = morph.target;
if(w && target)
{
int nv = Min(nVertices, target.nVertices);
const Vector3Df * sv = unmVertices, * tv = target.vertices;
Vector3Df * v = vertices;

for(i = 0; i < nv; i++, v++, sv++, tv++)
{
float dx = (tv->x - sv->x) * w;
float dy = (tv->y - sv->y) * w;
float dz = (tv->z - sv->z) * w;
v->x += dx, v->y += dy, v->z += dz;
}
}
}

if(dupVerts)
{
Vector3Df * v = vertices + nVertices - dvCount;

for(i = 0; i < dvCount; i++, v++)
{
int dv = dupVerts[i];
*v = vertices[dv];
}
}

ComputeNormals2(true, true);

// Re-orient original normals based on rotation of computed normals
for(i = 0; i < nVertices; i++)
{
Vector3D axis;
double len;
Vector3Df normal1 = computedUnmorphedNormals[i], normal0 = normals[i];

axis.CrossProduct(
{ normal0.x, normal0.y, normal0.z },
{ normal1.x, normal1.y, normal1.z });

len = axis.length;
if(len)
{
double dot = normal0.DotProduct(normal1);
Degrees angle = atan2(len, dot);
Quaternion q; // q is rotation between computed normals
axis.Scale(axis, 1.0 / len);
q.RotationAxis(axis, angle);
normals[i].MultQuaternion(unmNormals[i], q);
}
}
}
}

void ApplySkin()
{
MeshSkin skin = this.skin;
Expand Down Expand Up @@ -635,6 +745,10 @@ public:
driver.FreeMesh(displaySystem, this);
}
delete parts;

delete unmorphedMesh;
delete computedUnmorphedNormals;
if(morphs) morphs.Free(), delete morphs;
}
}

Expand Down Expand Up @@ -2020,6 +2134,10 @@ private:
Array<Matrixf> matBones;
SkinVert * boneData; // For uploading to GPU
#endif

Array<MeshMorph> morphs;
Mesh unmorphedMesh;
Vector3Df * computedUnmorphedNormals; // Normals as computed by ComputeNormals2() for unmorphed mesh
};

void computeNormalWeights(int n, float * vertices, uint vStride, uint * indices, bool ix32Bit, int base, double * weights, Vector3D * edges, Vector3D * rEdges)
Expand Down
21 changes: 21 additions & 0 deletions ecere/src/gfx/3D/Object.ec
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,27 @@ public:
SetMinMaxRadius(false);
}

private bool _ApplyMorphs()
{
bool result = false;
Object o;
if(flags.mesh && mesh && mesh.morphs)
{
mesh.ApplyMorphs();
// flags.morphApplied = true;
result = true;
}
for(o = children.first; o; o = o.next)
result |= o._ApplyMorphs();
return result;
}

public void ApplyMorphs()
{
_ApplyMorphs();
SetMinMaxRadius(false);
}

void ResetPose()
{
Object o;
Expand Down
14 changes: 10 additions & 4 deletions ecere/src/gfx/3D/models/e3d/e3dDefs.ec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ enum E3DBlockType : uint16
meshID = 0x1020,
meshBBox = 0x1021,
meshDuplVerts = 0x1022,
attributes = 0x2000,
attributes = 0x2000, // uint count (limit of 65,536 vertices)
attrVertices = 0x2010, // float x,y,z vertices
attrVerticesDbl = 0x2011, // double x,y,z vertices
attrQVertices = 0x2018, // quantized x,y,z 16-bit vertices (first, deltas)
Expand Down Expand Up @@ -56,15 +56,20 @@ enum E3DBlockType : uint16
skinBoneWeights = 0x1054, // byte 0..7: set of bone weights to use for this skin

parts = 0x1060,

morphs = 0x1070, // integer Count
morph = 0x1071,
morphID = 0x1072,
morphName = 0x1073,
morphWeight = 0x1074, // TODO: Use morph weights uniforms for GPU morphs?
// [meshID (reference)] // TODO: Load as vertex attribute instead for GPU morphs? Keep as meshID for re-use?
nodes = 0x3000,
meshNode = 0x3010,
nodeID = 0x3020,
nodeName = 0x3021,
scaling = 0x3030,
orientation = 0x3031,
position = 0x3032,
skeleton = 0x3040,
skeleton = 0x3040, // Should we change this to have skeletonID, skeletonName blocks?
// Can have sub-nodes!

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

struct E3DBlockHeader
Expand Down Expand Up @@ -283,6 +288,7 @@ class E3DWriteContext : struct
Array<Object> allAnimatedObjects { };
Map<uintptr, int> objectToNodeID { };
uint nodeID;
uint morphID;

~E3DWriteContext()
{
Expand Down
46 changes: 46 additions & 0 deletions ecere/src/gfx/3D/models/e3d/e3dRead.ec
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,12 @@ static void readBlocks(E3DContext ctx, File f, DisplaySystem displaySystem, E3DB
object.SetMinMaxRadius(false);
}
}
else if(containerType == morph)
{
MeshMorph * morph = &mesh.morphs[mesh.morphs.count];
Mesh target = ctx.meshesByID[id];
morph->target = target;
}
else
ctx.meshesByID[id] = data;
break;
Expand Down Expand Up @@ -898,6 +904,44 @@ static void readBlocks(E3DContext ctx, File f, DisplaySystem displaySystem, E3DB
// PrintLn("Duplicate Vertices!");
break;
}
case morphs:
{
int count = 0;
f.Read(&count, sizeof(int), 1);
pos += sizeof(int);
if(count && !mesh.morphs)
{
mesh.morphs = { minAllocSize = count };
readSubBlocks = true;
}
break;
}
case morph:
{
readSubBlocks = true;
break;
}
case morphID:
{
// Assuming sequential ID starting from 0 for now
int id = 0;
f.Read(&id, sizeof(int), 1);
break;
}
case morphName:
{
MeshMorph * morph = &mesh.morphs[mesh.morphs.count];
morph->name = readString(f);
break;
}
case morphWeight:
{
MeshMorph * morph = &mesh.morphs[mesh.morphs.count];
float w = 0;
f.Read(&w, sizeof(float), 1);
morph->weight = w;
break;
}
case skin:
readSubBlocks = true;
break;
Expand Down Expand Up @@ -1007,6 +1051,8 @@ static void readBlocks(E3DContext ctx, File f, DisplaySystem displaySystem, E3DB
}
if(readSubBlocks)
readBlocks(ctx, f, displaySystem, header.type, pos, bEnd, subData);
if(header.type == morph && mesh.morphs && mesh.morphs.minAllocSize >= mesh.morphs.count + 1)
mesh.morphs.count++;
if(header.type == material)
{
Material mat = subData;
Expand Down
Loading

0 comments on commit a720e0c

Please sign in to comment.