public void ImportFrom(DsonTypes.Modifier modifier) { DsonTypes.Morph dsonMorph = modifier.morph; if (dsonMorph == null) { return; } float[][] dsonDeltas = dsonMorph.deltas?.values; if (dsonDeltas == null || dsonDeltas.Length == 0) { return; } if (modifier.channel.id != "value") { throw new InvalidOperationException("expected channel id to be 'value'"); } string channel = modifier.name + "?value"; int deltaCount = dsonDeltas.Length; MorphDelta[] deltas = new MorphDelta[deltaCount]; for (int i = 0; i < deltaCount; ++i) { float[] dsonDelta = dsonDeltas[i]; int vertexIdx = (int)dsonDelta[0]; Vector3 positionOffset = new Vector3(dsonDelta[1], dsonDelta[2], dsonDelta[3]); deltas[i] = new MorphDelta(vertexIdx, positionOffset); } var hdUrl = ExtractHdUrl(dsonMorph.hd_url); MorphRecipe recipe = new MorphRecipe { Channel = channel, Deltas = deltas, HdUrl = hdUrl }; morphRecipes.Add(recipe); }
private MorphRecipe Rewrite(MorphRecipe morphRecipe, Figure parentFigure, Morph parentMorph) { Vector3[] parentDeltas = new Vector3[parentFigure.Geometry.VertexCount]; foreach (var delta in parentMorph.Deltas) { parentDeltas[delta.VertexIdx] += delta.PositionOffset; } int vertexCount = BaseDeltaWeights.Count; Vector3[] deltas = new Vector3[BaseDeltaWeights.Count]; foreach (var delta in morphRecipe.Deltas) { deltas[delta.VertexIdx] += delta.PositionOffset; } for (int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) { foreach (var baseDeltaWeight in BaseDeltaWeights.GetElements(vertexIdx)) { deltas[vertexIdx] -= baseDeltaWeight.Weight * parentDeltas[baseDeltaWeight.Index]; } } List <MorphDelta> rewrittenDeltas = new List <MorphDelta>(vertexCount); for (int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) { Vector3 positionOffset = deltas[vertexIdx]; if (positionOffset.IsZero) { continue; } rewrittenDeltas.Add(new MorphDelta(vertexIdx, positionOffset)); } return(new MorphRecipe { Channel = morphRecipe.Channel, Deltas = rewrittenDeltas.ToArray() }); }
public static MorphRecipe Merge(string channelName, FigureRecipeMerger.Reindexer reindexer, MorphRecipe parentMorph, MorphRecipe[] childMorphs, AutomorpherRecipe[] childAutomorphers) { List <MorphDelta> morphDeltas = new List <MorphDelta>(); Vector3[] flatPositionOffsets; if (parentMorph != null) { morphDeltas.AddRange(parentMorph.Deltas); flatPositionOffsets = new Vector3[reindexer.GetParentVertexCount()]; foreach (MorphDelta parentDelta in parentMorph.Deltas) { flatPositionOffsets[parentDelta.VertexIdx] = parentDelta.PositionOffset; } } else { flatPositionOffsets = null; } for (int childIdx = 0; childIdx < reindexer.ChildOffsets.Length; ++childIdx) { int childVertexOffset = reindexer.ChildOffsets[childIdx].Vertex; var childMorph = childMorphs[childIdx]; if (childMorph != null) { morphDeltas.AddRange(childMorph.Deltas .Select(delta => new MorphDelta(delta.VertexIdx + childVertexOffset, delta.PositionOffset))); } else { if (flatPositionOffsets == null) { continue; } //generate the deltas using the automorpher var automorpher = childAutomorphers[childIdx]; for (int childVertexIdx = 0; childVertexIdx < automorpher.BaseDeltaWeights.Count; ++childVertexIdx) { Vector3 childPositionOffset = Vector3.Zero; foreach (var baseDeltaWeight in automorpher.BaseDeltaWeights.GetElements(childVertexIdx)) { childPositionOffset += baseDeltaWeight.Weight * flatPositionOffsets[baseDeltaWeight.Index]; } if (!childPositionOffset.IsZero) { morphDeltas.Add(new MorphDelta(childVertexIdx + childVertexOffset, childPositionOffset)); } } } } return(new MorphRecipe { Channel = channelName, Deltas = morphDeltas.ToArray() }); }
private List <MorphRecipe> MergeMorphs(List <FormulaRecipe> mergedFormulas) { var parentMorphsByName = parent.Morphs.ToDictionary(morph => morph.Channel, morph => morph); var childMorphsByName = children.Select(child => child.Morphs.ToDictionary(morph => morph.Channel, morph => morph)).ToList(); var allMorphNames = parentMorphsByName.Keys.Concat(childMorphsByName.SelectMany(dict => dict.Keys)).Distinct().ToList(); var childAutomorphers = children.Select(child => child.Automorpher).ToArray(); List <MorphRecipe> mergedMorphs = new List <MorphRecipe>(); foreach (string morphName in allMorphNames) { parentMorphsByName.TryGetValue(morphName, out var parentMorph); var childMorphs = new MorphRecipe[childMorphsByName.Count]; for (int childIdx = 0; childIdx < childMorphsByName.Count; ++childIdx) { childMorphsByName[childIdx].TryGetValue(morphName, out childMorphs[childIdx]); } var mergedMorph = MorphRecipe.Merge(morphName, reindexer, parentMorph, childMorphs, childAutomorphers); mergedMorphs.Add(mergedMorph); } for (int childIdx = 0; childIdx < children.Length; ++childIdx) { var child = children[childIdx]; int childVertexOffset = reindexer.ChildOffsets[childIdx].Vertex; string childControlChannel = childControlChannels[childIdx].Name; //Add a morph that moves each child vertex from the parent surface to its active position mergedMorphs.Add(child.Automorpher.GenerateGraftControlMorph(childControlChannel, childVertexOffset, child.Geometry)); //Add a formula that disables each child morph when when the child control channel is 0 foreach (var childMorph in child.Morphs) { if (parentMorphsByName.ContainsKey(childMorph.Channel)) { /* * Skip child morphs that are following a parent morph. * * In principle, I should disable these to and replace them with automorpher-generated * morphs. But in practice, these morphs are already very similar to the automorphs so it's * OK to leave them as is. */ continue; } mergedFormulas.Add(new FormulaRecipe { Output = childMorph.Channel, Stage = FormulaRecipe.FormulaStage.Multiply, Operations = new List <OperationRecipe> { OperationRecipe.MakePushChannel(childControlChannel) } }); } } return(mergedMorphs); }