// First pass: calculate the transofmration matrix for each vertex // here we must associate a matrix with each bone (maybe with each vertex_id??) // then we multiply the current_bone matrix with the one we had before // (perhaps it was identity, perhaps it was already some matrix (if // the bone influences many vertices) ) // then we store this multiplied matrix. // in the render function we get a vertex_id, so we can find the matrix to apply // to the vertex, then we send the vertex to OpenGL public void RecursiveCalculateVertexTransform(Node nd, Matrix4x4 current) { Matrix4x4 current_node = current * nd.Transform; foreach (int mesh_id in nd.MeshIndices) { Mesh cur_mesh = _scene._inner.Meshes[mesh_id]; MeshDraw mesh_draw = _mesh_id2mesh_draw[mesh_id]; foreach (Bone bone in cur_mesh.Bones) { // a bone transform is more than by what we need to trasnform the model BoneNode armature_node = _scene.GetBoneNode(bone.Name); Matrix4x4 bone_global_mat = armature_node.GlobTrans; /// bind tells the original delta in global coord, so we can find current delta Matrix4x4 bind = bone.OffsetMatrix; Matrix4x4 delta_roto = bind * bone_global_mat; Matrix4x4 current_bone = delta_roto * current_node; foreach (var pair in bone.VertexWeights) { // Can apply bone weight here mesh_draw._vertex_id2matrix[pair.VertexID] = current_bone; } } } foreach (Node child in nd.Children) { RecursiveCalculateVertexTransform(child, current_node); } }
/// <summary> /// Function to blend from one keyframe to another. /// </summary> public void ChangeLocalFixedDataBlend(ActionState st) { Debug.Assert(0 <= st.KfBlend && st.KfBlend <= 1); foreach (NodeAnimationChannel channel in _action.NodeAnimationChannels) { BoneNode bone_nd = _scene.GetBoneNode(channel.NodeName); // now rotation tk.Quaternion target_roto = tk.Quaternion.Identity; if (channel.RotationKeyCount > st.TargetKeyframe) { target_roto = channel.RotationKeys[st.TargetKeyframe].Value.eToOpenTK(); } tk.Quaternion start_frame_roto = channel.RotationKeys[st.OriginKeyframe].Value.eToOpenTK(); tk.Quaternion result_roto = tk.Quaternion.Slerp(start_frame_roto, target_roto, (float)st.KfBlend); // now translation tk.Vector3 target_trans = tk.Vector3.Zero; if (channel.PositionKeyCount > st.TargetKeyframe) { target_trans = channel.PositionKeys[st.TargetKeyframe].Value.eToOpenTK(); } tk.Vector3 cur_trans = channel.PositionKeys[st.OriginKeyframe].Value.eToOpenTK(); tk.Vector3 result_trans = cur_trans + tk.Vector3.Multiply(target_trans - cur_trans, (float)st.KfBlend); // combine rotation and translation tk.Matrix4 result = tk.Matrix4.CreateFromQuaternion(result_roto); result.Row3.Xyz = result_trans; bone_nd.LocTrans = result.eToAssimp(); } }
public void UpdateBonePositions(BoneNode nd) { var triangle = _bone_id2triangle[nd._inner.Name]; Vector3 new_start = nd.GlobalTransform.ExtractTranslation(); if (nd.Children.Count > 0) { // this bone's end == the beginning of __any__ child bone Vector3 new_end = nd.Children[0].GlobalTransform.ExtractTranslation(); triangle._start = new_start; triangle._end = new_end; foreach (var child_nd in nd.Children) { UpdateBonePositions(child_nd); } } else { // this bone has no children, we don't know where it will end, so we guess. // strategy 1: just set a random sensible value for bone // strategy 2: get geometric center of the vertices that this bone acts on // we have to use the Y-unit vector instead of X because we defined Y_UP // in the collada.dae file, so all the matrices work such that direct unit vector is unit Y // strategy 3: choose the length of the smallest bone found var delta = Vector3.TransformVector(Vector3.UnitY, nd.GlobalTransform); Vector3 new_end = new_start + Vector3.Multiply(delta, (float)_average_bone_length); triangle._start = new_start; triangle._end = new_end; } }
public BoneNode BuildBoneNodes(string armature_root_name) { Node armature_root = InnerRecurFindNode(_inner.RootNode, armature_root_name); BoneNode root = InnerRecurBuildBones(armature_root); return(root); }
public void LoadScene(byte[] filedata) { MemoryStream sphere = new MemoryStream(filedata); var assimp_scene = BuildAssimpScene(sphere, "dae"); _cur_scene = new SceneWrapper(assimp_scene); _cur_scene.NameUnnamedMeshes(); _cur_scene.NodeNamesAreUnique(); // load other data _action_one = new NodeInterpolator(_cur_scene, _cur_scene._inner.Animations[0]); BoneNode armature = _cur_scene.BuildBoneNodes("Armature"); string mesh_default_name = "Cube"; Node mesh = _cur_scene.FindNode(mesh_default_name); if (mesh == null) { throw new Exception("Could not find node named " + mesh_default_name); } ActionState state = new ActionState(_cur_scene._inner.Animations[0]); _enttity_one = new Entity(_cur_scene, mesh, armature, state); state._owner = _enttity_one; _action_one.ApplyAnimation(_enttity_one._armature, _enttity_one._action); HasScene = true; }
// Updates global transforms by walking the hierarchy private void ReCalculateGlobalTransform(BoneNode nd) { nd.GlobalTransform = nd.LocalTransform * nd.Parent.GlobalTransform; foreach (var child in nd.Children) { ReCalculateGlobalTransform(child); } }
// make triangles to draw for each bone private void MakeBoundingTriangles(BoneNode nd) { _bone_id2triangle[nd._inner.Name] = new BoneBounds(); for (int i = 0; i < nd._inner.ChildCount; i++) { MakeBoundingTriangles(nd.Children[i]); } }
public double FindAverageBoneLength(BoneNode nd) { double len = 0; int qty = 0; InnerFindAverageLength(nd, ref len, ref qty); return(len / qty); }
/// Build geometry data for node (usually use only for one of the children of scene.RootNode) public Geometry(IList <Mesh> scene_meshes, Node nd, BoneNode armature) { MakeBoundingBoxes(scene_meshes, nd); MakeBoundingTriangles(armature); _average_bone_length = FindAverageBoneLength(armature); UpdateBonePositions(armature); EntityBox = new BoundingBoxGroup(_mesh_id2box.Values); }
// the only public constructor // TODO: change the "Node mesh". This should point to MeshDraw object which is unique to each entity. public Entity(SceneWrapper sc, Node mesh, BoneNode armature, ActionState state) { _scene = sc; _node = mesh; _extra_geometry = new Geometry(sc._inner.Meshes, mesh, armature); _armature = armature; _action = state; _transform = new TransformState(Matrix4.Identity, 10, 17); }
// Update this particular armature to this particular frame in action (to this particular keyframe) public void ApplyAnimation(BoneNode armature, ActionState st) { ChangeLocalFixedDataBlend(st); var root_node = armature; root_node.GlobalTransform = root_node.LocalTransform * st.GlobalTransform; foreach (var child in root_node.Children) { ReCalculateGlobalTransform(child); } }
private ArmatureTreeNode MakeArmatureTree(Entity ent, BoneNode nd) { var current = new ArmatureTreeNode(nd._inner.Name); current.DrawData = ent._extra_geometry._bone_id2triangle[nd._inner.Name]; foreach (var child_nd in nd.Children) { var treeview_child = MakeArmatureTree(ent, child_nd); current.Nodes.Add(treeview_child); } return(current); }
private BoneNode InnerRecurBuildBones(Node nd) { var current = new BoneNode(nd); current.GlobTrans = GetNodeGlobalTransform(nd); current.LocTrans = nd.Transform; // add to dict for faster lookup _name2bone_node[nd.Name] = current; foreach (var child in nd.Children) { BoneNode w_child = InnerRecurBuildBones(child); w_child.Parent = current; current.Children.Add(w_child); } return(current); }
public void InnerFindAverageLength(BoneNode nd, ref double total_length, ref int bones_count) { var triangle = _bone_id2triangle[nd._inner.Name]; Vector3 bone_start = nd.GlobalTransform.ExtractTranslation(); // dont analyse bones with no children if (nd.Children.Count > 0) { // this bone's end == the beginning of __any__ child bone Vector3 bone_end = nd.Children[0].GlobalTransform.ExtractTranslation(); double len = (bone_start - bone_end).Length; total_length += len; bones_count++; foreach (var child_nd in nd.Children) { InnerFindAverageLength(child_nd, ref total_length, ref bones_count); } } }
public ArmatureEntity(Scene sc, BoneNode arma) { _armature = arma; _scene = sc; }