T3D Morph Calculation Fix. (colladaAppMesh.cpp)
by Kevin Mitchell · 10/24/2012 (8:25 pm) · 2 comments
This is an issue found in the T3D Code source where the calculation of morph data is calculation too high past the limits set in the morph target data.
It seems best to show than to type 3 paragraphs of visual description.

This is only after dialing up one morph to 100%. Notice how the normals and UV data looks like its washing out as well. This i believed to also be caused by the scaling down done in the function fixed below.

This screwed individual is the results of 2 100% dials and one 50% dial. Note that anything more than 1 100% dial will start a separation of UV mapping and character stretching. This being manly because how the calculations are being applied to the source data far too early.
If you are thinking what I thought and that this might be because of incorrect storing of the float points in the morph weights, this is not the case as showed below.
After changing the way things are calculated I get better results as I did when using my old morph methods in my Character Generator 2.

The complete morph with out the wash out of normal and UVs starting to appear.

How the morph looks after change with the same weights applied.
Now for the code. I have added a #define to revert the change back easily at any time in the case that it affects something you are working on.
It seems best to show than to type 3 paragraphs of visual description.

This is only after dialing up one morph to 100%. Notice how the normals and UV data looks like its washing out as well. This i believed to also be caused by the scaling down done in the function fixed below.

This screwed individual is the results of 2 100% dials and one 50% dial. Note that anything more than 1 100% dial will start a separation of UV mapping and character stretching. This being manly because how the calculations are being applied to the source data far too early.
If you are thinking what I thought and that this might be because of incorrect storing of the float points in the morph weights, this is not the case as showed below.
<float_array id="geom-Rename_Base_Body_01-morph1-weights-array" count="5">1 0.50495 0 1 0</float_array>
After changing the way things are calculated I get better results as I did when using my old morph methods in my Character Generator 2.

The complete morph with out the wash out of normal and UVs starting to appear.

How the morph looks after change with the same weights applied.
Now for the code. I have added a #define to revert the change back easily at any time in the case that it affects something you are working on.
//#define COLLADA_FIX_DISABLED
void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const MatrixF& objectOffset,
Vector<Point3F>& v_points,
Vector<Point3F>& v_norms,
Vector<ColorI>& v_colors,
Vector<Point2F>& v_uvs,
Vector<Point2F>& v_uv2s)
{
// @todo: Could the base geometry (or any target geometry) also be a morph?
// Get the target geometries and weights (could be animated)
Vector<const domGeometry*> targetGeoms;
domListOfFloats targetWeights;
for (int iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) {
const domInputLocal* input = morph->getTargets()->getInput_array()[iInput];
const domSource* source = daeSafeCast<domSource>(findInputSource(input));
if (dStrEqual(input->getSemantic(), "MORPH_TARGET")) {
// Get the morph targets
_SourceReader srcTargets;
srcTargets.initFromSource(source);
for (int iTarget = 0; iTarget < srcTargets.size(); iTarget++) {
// Lookup the element and add to the targets list
daeIDRef idref(srcTargets.getStringValue(iTarget));
idref.setContainer(morph->getDocument()->getDomRoot());
targetGeoms.push_back(daeSafeCast<domGeometry>(idref.getElement()));
}
}
else if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) {
// Get the (possibly animated) morph weight
targetWeights = AnimatedFloatList(source->getFloat_array()).getValue(time);
}
}
// Check that we have a weight for each target
if (targetGeoms.size() != targetWeights.getCount())
{
domController* ctrl = daeSafeCast<domController>(const_cast<domMorph*>(morph)->getParent());
Con::warnf("Mismatched morph targets and weights in %s.", _GetNameOrId(ctrl));
// Set unused targets to zero weighting (unused weights are ignored)
while (targetGeoms.size() > targetWeights.getCount())
targetWeights.append(0.0f);
}
// Get the base geometry and vertex data
const domGeometry* baseGeometry = daeSafeCast<domGeometry>(morph->getSource().getElement());
if (!baseGeometry)
return;
getPrimitives(baseGeometry);
getVertexData(baseGeometry, time, objectOffset, v_points, v_norms, v_colors, v_uvs, v_uv2s, true);
// Get pointers to the arrays of base geometry data
Point3F* points_array = &v_points[v_points.size() - vertTuples.size()];
Point3F* norms_array = &v_norms[v_norms.size() - vertTuples.size()];
Point2F* uvs_array = &v_uvs[v_uvs.size() - vertTuples.size()];
ColorI* colors_array = v_colors.size() ? &v_colors[v_colors.size() - vertTuples.size()] : 0;
Point2F* uv2s_array = v_uv2s.size() ? &v_uv2s[v_uv2s.size() - vertTuples.size()] : 0;
// Normalize base vertex data?
if (morph->getMethod() == MORPHMETHODTYPE_NORMALIZED) {
F32 weightSum = 0.0f;
for (int iWeight = 0; iWeight < targetWeights.getCount(); iWeight++) {
weightSum += targetWeights[iWeight];
}
// Result = Base*(1.0-w1-w2 ... -wN) + w1*Target1 + w2*Target2 ... + wN*TargetN
weightSum = mClampF(1.0f - weightSum, 0.0f, 1.0f);
#ifdef COLLADA_FIX_DISABLED
for (int iVert = 0; iVert < vertTuples.size(); iVert++) {
points_array[iVert] *= weightSum;
norms_array[iVert] *= weightSum;
uvs_array[iVert] *= weightSum;
}
if (uv2s_array) {
for (int iVert = 0; iVert < vertTuples.size(); iVert++)
uv2s_array[iVert] *= weightSum;
}
#else
//KEVIN MITCHELL 10-24-2012 - START
//REMOVEDE THE SCALE DOWN HERE AS THIS CONTRIBUTED TO
//THE WEIRD DEFORMATIONS CUASE BY MULTI MESH MORPHING
/*
for (int iVert = 0; iVert < vertTuples.size(); iVert++) {
points_array[iVert] *= weightSum;
norms_array[iVert] *= weightSum;
uvs_array[iVert] *= weightSum;
}
if (uv2s_array) {
for (int iVert = 0; iVert < vertTuples.size(); iVert++)
uv2s_array[iVert] *= weightSum;
}*/
//KEVIN MITCHELL 10-24-2012 - END
#endif
}
#ifndef COLLADA_FIX_DISABLED
//KEVIN MITCHELL 10-24-2012 - START
//ADDED THESE TEMP VARIABLES TO CALCULATE THE ACCUMILATED MORPH DIFFERENCES BETWEEN ALL TARGETS IN THE MORPH STACK
Vector<Point3F> _points_array;
Vector<Point3F> _norms_array;
Vector<Point2F> _uvs_array;
Vector<ColorI> _colors_array;
Vector<Point2F> _uv2s_array;
Point3F empty3F;
Point2F empty2F;
ColorI emptyI;
empty3F.set(0.0f,0.0f,0.0f);
empty2F.set(0.0f,0.0f);
emptyI.set(0,0,0,0);
//NOTE: Used a loop after failing to do a fill function to set the whole object to 0
for(unsigned int morphIndexes=0; morphIndexes<vertTuples.size();morphIndexes++){
_points_array.push_back(empty3F);
_norms_array.push_back(empty3F);
_uvs_array.push_back(empty2F);
_colors_array.push_back(emptyI);
_uv2s_array.push_back(empty2F);
}
//KEVIN MITCHELL 10-24-2012 - END
#endif
// Interpolate using the target geometry and weights
for (int iTarget = 0; iTarget < targetGeoms.size(); iTarget++) {
// Ignore empty weights
if (targetWeights[iTarget] == 0.0f)
continue;
// Get target geometry data into temporary arrays
Vector<Point3F> targetPoints;
Vector<Point3F> targetNorms;
Vector<Point2F> targetUvs;
Vector<ColorI> targetColors;
Vector<Point2F> targetUv2s;
// Copy base geometry into target geometry (will be used if target does
// not define normals or uvs)
targetPoints.set(points_array, vertTuples.size());
targetNorms.set(norms_array, vertTuples.size());
targetUvs.set(uvs_array, vertTuples.size());
if (colors_array)
targetColors.set(colors_array, vertTuples.size());
if (uv2s_array)
targetUv2s.set(uv2s_array, vertTuples.size());
getVertexData(targetGeoms[iTarget], time, objectOffset, targetPoints, targetNorms, targetColors, targetUvs, targetUv2s, false);
// Combine with base geometry
#ifdef COLLADA_FIX_DISABLED
for (int iVert = 0; iVert < vertTuples.size(); iVert++) {
points_array[iVert] += targetPoints[iVert] * targetWeights[iTarget];
norms_array[iVert] += targetNorms[iVert] * targetWeights[iTarget];
uvs_array[iVert] += targetUvs[iVert] * targetWeights[iTarget];
}
if (uv2s_array) {
for (int iVert = 0; iVert < vertTuples.size(); iVert++)
uv2s_array[iVert] += targetUv2s[iVert] * targetWeights[iTarget];
}
if (colors_array) {
for (int iVert = 0; iVert < vertTuples.size(); iVert++)
colors_array[iVert] += targetColors[iVert] * (F32)targetWeights[iTarget];
}
#else
//KEVIN MITCHELL 10-24-2012 - START
//MODIFIED THIS SECTION TO ACCUMILATE THE DIFFERENCE DATA FROM ALL LEVELS OF MORPHS.
//ACCUMILATE THE TOTAL AND DO NOT MODIFY THE SOURCE DATA UNTIL ALL VERT DIFFERENCES HAVE BEEN ACCUMILATED
// Total Difference For Deformation = (Morph Verts POS - Source Verts POS) * Weight Parcentage ;
for (int iVert = 0; iVert < vertTuples.size(); iVert++) {
_points_array[iVert] += (targetPoints[iVert]-points_array[iVert]) * targetWeights[iTarget];
_norms_array[iVert] += (targetNorms[iVert]-norms_array[iVert]) * targetWeights[iTarget];
_uvs_array[iVert] += (targetUvs[iVert]-uvs_array[iVert]) * targetWeights[iTarget];
}
if (uv2s_array) {
for (int iVert = 0; iVert < vertTuples.size(); iVert++)
_uv2s_array[iVert] += (targetUv2s[iVert]-uv2s_array[iVert]) * targetWeights[iTarget];
}
if (colors_array) {
for (int iVert = 0; iVert < vertTuples.size(); iVert++){
_colors_array[iVert].alpha += (targetColors[iVert].alpha-colors_array[iVert].alpha) * (F32)targetWeights[iTarget];
_colors_array[iVert].green += (targetColors[iVert].green-colors_array[iVert].green) * (F32)targetWeights[iTarget];
_colors_array[iVert].blue += (targetColors[iVert].blue-colors_array[iVert].blue) * (F32)targetWeights[iTarget];
_colors_array[iVert].red += (targetColors[iVert].red-colors_array[iVert].red) * (F32)targetWeights[iTarget];
}
}
//KEVIN MITCHELL 10-24-2012 - END
#endif
}
#ifndef COLLADA_FIX_DISABLED
//KEVIN MITCHELL 10-24-2012 - START
// Combine with base geometry
for (int iVert = 0; iVert < vertTuples.size(); iVert++) {
points_array[iVert] += _points_array[iVert];
norms_array[iVert] += _norms_array[iVert];
uvs_array[iVert] += _uvs_array[iVert];
if (uv2s_array) {
for (int iVert = 0; iVert < vertTuples.size(); iVert++)
uv2s_array[iVert] += _uv2s_array[iVert];
}
if (colors_array) {
for (int iVert = 0; iVert < vertTuples.size(); iVert++){
colors_array[iVert] += _colors_array[iVert];
}
}
}
//KEVIN MITCHELL 10-24-2012 - END
#endif
}About the author
Riding Solo since 2005. Current Project: Fated World 2005-Present RPG Engine Tool Kit - Now available.

Torque Owner Lukas Joergensen
WinterLeaf Entertainment
Tbh tho nice changes, if this can be confirmed as an issue you should pull request it!