Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3ds max morph target auto rebuild #928

Merged
merged 18 commits into from
Feb 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 93 additions & 7 deletions 3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
using System.Globalization;

namespace Max2Babylon
{
Expand Down Expand Up @@ -511,38 +512,67 @@ private BabylonNode ExportMasterMesh(IIGameScene scene, IIGameNode meshNode, Bab
var babylonMorphTargets = new List<BabylonMorphTarget>();
// All morphers are considered identical
// Their targets are concatenated
int m = 0;
morphers.ForEach(morpher =>
{
m++;
for (int i = 0; i < morpher.NumberOfMorphTargets; i++)
{
// Morph target
var maxMorphTarget = morpher.GetMorphTarget(i);
bool mustRebuildMorphTarget = maxMorphTarget == null;
if (mustRebuildMorphTarget)
{
string actionStr = exportParameters.rebuildMorphTarget ? $" trying to rebuild {i}." : string.Empty;
RaiseWarning($"Morph target [{i}] is not available anymore - ie: has been deleted in max and is baked into the scene.{actionStr}",3);
}

// Ensure target still exists (green color legend)
if (maxMorphTarget != null)
// Target geometry - this is where we rebuild the target if necessary
var targetVertices = ExtractMorphTargetVertices(babylonMesh, vertices, offsetTM, i, maxMorphTarget, optimizeVertices, faceIndexes);

if (targetVertices != null && targetVertices.Any())
{
var babylonMorphTarget = new BabylonMorphTarget
{
name = maxMorphTarget.Name
// the name is reconstructed if we have to rebuild the target
name = maxMorphTarget?.Name ?? $"{meshNode.Name}.morpher({m}).target({i})"
};
babylonMorphTargets.Add(babylonMorphTarget);
RaiseMessage($"Morph target {babylonMorphTarget.name} added.",3);

// TODO - Influence
babylonMorphTarget.influence = 0f;
babylonMorphTarget.influence = 0f;

// Target geometry
var targetVertices = ExtractVertices(babylonMesh, maxMorphTarget, optimizeVertices, faceIndexes);
babylonMorphTarget.positions = targetVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();

if (exportParameters.exportMorphNormals)
{
babylonMorphTarget.normals = targetVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();
if (mustRebuildMorphTarget)
{
// we do not recontruct the normals
RaiseWarning("we do not have morph normals when morph target has been rebuilded.",4);
babylonMorphTarget.normals = null;
}
else
{
babylonMorphTarget.normals = targetVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();
}
}

// Tangent
if (exportParameters.exportTangents && exportParameters.exportMorphTangents)
{
babylonMorphTarget.tangents = targetVertices.SelectMany(v => v.Tangent).ToArray();
if (mustRebuildMorphTarget)
{
// we do not recontruct the tangents
RaiseWarning("Rebuilt morph targets will not have tangent information.", 4);
babylonMorphTarget.tangents = null;
}
else
{
babylonMorphTarget.tangents = targetVertices.SelectMany(v => v.Tangent).ToArray();
}
}

// Animations
Expand Down Expand Up @@ -576,6 +606,62 @@ private BabylonNode ExportMasterMesh(IIGameScene scene, IIGameNode meshNode, Bab
return babylonMesh;
}

private IEnumerable<GlobalVertex> ExtractMorphTargetVertices(BabylonAbstractMesh babylonAbstractMesh, List<GlobalVertex> vertices, IMatrix3 offsetTM, int morphIndex, IIGameNode maxMorphTarget, bool optimizeVertices, List<int> faceIndexes)
{
if (maxMorphTarget != null)
{
foreach(var v in ExtractVertices(babylonAbstractMesh, maxMorphTarget, optimizeVertices, faceIndexes))
{
yield return v;
}
yield break;
}
// rebuild Morph Target
if (exportParameters.rebuildMorphTarget)
{
var points = ExtractMorphTargetPoints(babylonAbstractMesh, morphIndex, offsetTM).ToList();
for (int i = 0; i != vertices.Count; i++)
{
int bi = vertices[i].BaseIndex;
yield return new GlobalVertex()
{
BaseIndex = bi,
Position = points[bi]
};
}
}
}

private IEnumerable<IPoint3> ExtractMorphTargetPoints(BabylonAbstractMesh babylonAbstractMesh, int morphIndex, IMatrix3 offsetTM)
{
// this is the place where we reconstruct the vertices.
// the needed function is not available on the .net SDK, then we have to use Max Script.
// TODO : use direct instance instead of manipulate string
var script = $"with printAllElements on (for k in 0 to (WM3_MC_NumMPts ${babylonAbstractMesh.name}.Morpher {morphIndex}) collect (WM3_MC_GetMorphPoint ${babylonAbstractMesh.name}.morpher {morphIndex} k)) as string";
var str = ManagedServices.MaxscriptSDK.ExecuteStringMaxscriptQuery(script);
if (!String.IsNullOrEmpty(str))
{
// we obtain a list of Point3 as string in a format of #([5.69523,-58.2409,65.1479],...)
int i = str.IndexOf('[');
if (i != -1)
{
do
{
int j = str.IndexOf(']', i++);
var p3Str = str.Substring(i, j - i);
var xyz = p3Str.Split(',').Select(s => float.Parse(s, CultureInfo.InvariantCulture)).ToArray();
var p = Loader.Global.Point3.Create(xyz[0], xyz[1], xyz[2]);
p = offsetTM.PointTransform(p);
// we have to reverse y and z
p = Loader.Global.Point3.Create(p[0] * scaleFactorToMeters, p[2] * scaleFactorToMeters, p[1] * scaleFactorToMeters);
yield return p;
i = str.IndexOf('[', j);
} while (i != -1);
}
}
yield break;
}

private void ExportUserData(IIGameNode meshNode, BabylonAbstractMesh babylonMesh)
{
string userProp = "";
Expand Down
8 changes: 6 additions & 2 deletions SharedProjects/BabylonExport.Entities/ExportParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ public class ExportParameters
public bool autoSaveSceneFile = false;
public bool exportTangents = true;
public bool exportSkins = true;
public bool exportMorphTangents = true;
public bool exportMorphNormals = true;
public long txtQuality = 100;
public bool mergeAOwithMR = true;
public bool enableKHRLightsPunctual = false;
Expand Down Expand Up @@ -99,5 +97,11 @@ public class ExportParameters
public bool dracoCompression = false;
public DracoParameters dracoParams = null;
#endregion

#region Morph
public bool rebuildMorphTarget = true;
public bool exportMorphTangents = true;
public bool exportMorphNormals = true;
#endregion
}
}