internal void Update() { foreach (TSOPair p in rel) { for (int i = 0; i < 4; i++) { if (skin_weights[i].weight < WeightEpsilon) { p.a.skin_weights[i] = new SkinWeight(0, 0.0f); } else { TSONode bone = skin_weights[i].bone; int bone_idx = p.sub_mesh.bones.IndexOf(bone); if (bone_idx == -1) { bone_idx = p.sub_mesh.AddBone(bone); } if (bone_idx != -1) { p.a.skin_weights[i] = new SkinWeight(bone_idx, skin_weights[i].weight); } } } p.a.FillSkinWeights(); p.a.GenerateBoneIndices(); } }
bool HiddenNode(TSONode node) { Vector3 position = node.GetWorldPosition(); Vector3 view_position = Vector3.TransformCoordinate(position, Transform_View); return(view_position.Z > 0.0f); }
Vector3 GetNodePositionOnScreen(TSONode node) { Vector3 p1 = node.GetWorldPosition(); Vector3 p2 = WorldToScreen(p1); return(p2); }
/// node操作を生成します。 public NodeCommand(Figure fig, TSONode node) { this.fig = fig; this.node = node; this.old_attr.rotation = node.Rotation; this.old_attr.translation = node.Translation; }
/// 頂点操作を生成します。 public VertexCommand(TSOFile tso, TSONode selected_node, MqoVert vertex, float weight, WeightOperation weight_op) { this.tso = tso; this.selected_node = selected_node; this.vertex = vertex; this.weight = weight; this.weight_op = weight_op; }
/// メッシュ操作を生成します。 public MeshCommand(TSOFile tso, TSOMesh mesh, TSONode selected_node, float weight, WeightOperation weight_op) { this.tso = tso; this.mesh = mesh; this.selected_node = selected_node; this.weight = weight; this.weight_op = weight_op; }
/// 統合メッシュ操作を生成します。 public SmoothMqoMeshCommand(TSOFile tso, MqoMesh mqo_mesh, TSONode selected_node) { this.tso = tso; this.mqo_mesh = mqo_mesh; this.selected_node = selected_node; weits = new float[mqo_mesh.vertices.Length]; new_weits = new float[mqo_mesh.vertices.Length]; }
void ComputePosingMatrix(TSONode node, ref Matrix m) { node.posing_matrix = node.TransformationMatrix * m; foreach (TSONode child in node.children) { ComputePosingMatrix(child, ref node.posing_matrix); } }
/// <summary> /// 指定nodeを追加します。 /// </summary> /// <param name="node">node</param> /// <returns>ボーン参照配列の添字</returns> public int AddBone(TSONode node) { int end = AddBoneIndex(node.ID); if (end != -1) { bones.Add(node); } return(end); }
/// <summary> /// ボーン選択の配列を得ます。 /// </summary> /// <param name="sub_mesh">サブメッシュ</param> /// <param name="selected_node">選択ボーン</param> /// <returns>ボーン選択の配列</returns> static int[] ClipBoneSelections(TSOSubMesh sub_mesh, TSONode selected_node) { int[] clipped_boneSelections = new int[sub_mesh.maxPalettes]; for (int numPalettes = 0; numPalettes < sub_mesh.maxPalettes; numPalettes++) { TSONode tso_node = sub_mesh.GetBone(numPalettes); clipped_boneSelections[numPalettes] = (selected_node == tso_node) ? 1 : 0; } return(clipped_boneSelections); }
/// <summary> /// 指定nodeに対するオフセット行列を計算します。 /// </summary> /// <param name="node">node</param> public static Matrix GetOffsetMatrix(TSONode node) { Matrix m = Matrix.Identity; while (node != null) { m.Multiply(node.TransformationMatrix); node = node.parent; } return(Matrix.Invert(m)); }
/// <summary> /// ワールド座標系での位置を得ます。 /// </summary> /// <returns></returns> public Vector3 GetWorldPosition() { TSONode node = this; Vector3 v = Vector3.Empty; while (node != null) { v = Vector3.TransformCoordinate(v, node.TransformationMatrix); node = node.parent; } return(v); }
/// <summary> /// ワールド座標系での位置と向きを得ます。 /// </summary> /// <returns></returns> public Matrix GetWorldCoordinate() { TSONode node = this; Matrix m = Matrix.Identity; while (node != null) { m.Multiply(node.TransformationMatrix); node = node.parent; } return(m); }
/// 選択nodeを指定軸中心に回転します。 public void RotateAxisOnScreen(int dx, int dy, Vector3 axis) { TSONode node = SelectedNode; if (node == null) { return; } float angle = dx * 0.005f; node.Rotation = Quaternion.RotationAxis(axis, angle) * node.Rotation; }
/// nodeを選択します。 /// returns: nodeを見つけたかどうか public bool SelectNode() { bool found = false; Figure fig; if (TryGetFigure(out fig)) { if (SelectedTSOFile != null) { //スクリーン座標からnodeを見つけます。 //衝突する頂点の中で最も近い位置にあるnodeを返します。 float x = lastScreenPoint.X; float y = lastScreenPoint.Y; int width = 5;//頂点ハンドルの幅 float min_z = 1e12f; TSONode found_node = null; foreach (TSONode node in SelectedTSOFile.nodes) { TMONode bone; if (fig.nodemap.TryGetValue(node, out bone)) { Vector3 p2 = GetNodePositionOnScreen(bone); if (p2.X - width <= x && x <= p2.X + width && p2.Y - width <= y && y <= p2.Y + width) { if (p2.Z < min_z) { min_z = p2.Z; found = true; found_node = node; } } } } if (found) { selected_node = found_node; if (SelectedNodeChanged != null) { SelectedNodeChanged(this, EventArgs.Empty); } } } } return(found); }
/// node操作を開始します。 public void BeginNodeCommand() { if (SelectedNode == null) { return; } Figure fig; if (TryGetFigure(out fig)) { TSONode node = SelectedNode; node_command = new NodeCommand(fig, node); } }
/// 選択nodeを指定軸方向に移動します。 public void TranslateAxisOnScreen(int dx, int dy, Vector3 axis) { TSONode node = SelectedNode; if (node == null) { return; } axis = Vector3.TransformNormal(axis, node.RotationMatrix); float len = dx * 0.005f; node.Translation = new Vector3(axis.X * len, axis.Y * len, axis.Z * len) + node.Translation; }
/// <summary> /// スキン変形行列の配列を得ます。 /// </summary> /// <param name="tso">tso</param> /// <returns>スキン変形行列の配列</returns> public Matrix[] ClipBoneMatrices(TSOFile tso) { Matrix[] clipped_boneMatrices = new Matrix[tso.nodes.Length]; for (int numPalettes = 0; numPalettes < tso.nodes.Length; numPalettes++) { TSONode tso_node = tso.nodes[numPalettes]; TMONode tmo_node; if (nodemap.TryGetValue(tso_node, out tmo_node)) { clipped_boneMatrices[numPalettes] = tso_node.offset_matrix * tmo_node.combined_matrix; } } return(clipped_boneMatrices); }
/// <summary> /// スキン変形行列の配列を得ます。 /// </summary> /// <param name="sub_mesh">サブメッシュ</param> /// <returns>スキン変形行列の配列</returns> public Matrix[] ClipBoneMatrices(TSOSubMesh sub_mesh) { Matrix[] clipped_boneMatrices = new Matrix[sub_mesh.maxPalettes]; for (int numPalettes = 0; numPalettes < sub_mesh.maxPalettes; numPalettes++) { TSONode tso_node = sub_mesh.GetBone(numPalettes); TMONode tmo_node; if (nodemap.TryGetValue(tso_node, out tmo_node)) { clipped_boneMatrices[numPalettes] = tso_node.offset_matrix * tmo_node.combined_matrix; } } return(clipped_boneMatrices); }
void DrawNodeAxis(TSONode node) { Vector3 p1 = GetNodePositionOnScreen(node); p1.Z = 0.0f; Vector3 position = node.GetWorldPosition(); Matrix m = node.GetWorldCoordinate(); Vector3 dirx = GetMatrixDirX(ref m); Vector3 diry = GetMatrixDirY(ref m); Vector3 dirz = GetMatrixDirZ(ref m); Vector3 view_position_dirx = Vector3.TransformCoordinate(position + dirx, Transform_View); Vector3 view_position_diry = Vector3.TransformCoordinate(position + diry, Transform_View); Vector3 view_position_dirz = Vector3.TransformCoordinate(position + dirz, Transform_View); bool hidden_dirx = view_position_dirx.Z > 0.0f; bool hidden_diry = view_position_diry.Z > 0.0f; bool hidden_dirz = view_position_dirz.Z > 0.0f; Line line = new Line(device); line.Width = 3; Vector2[] vertices = new Vector2[2]; vertices[0] = new Vector2(p1.X, p1.Y); if (!hidden_dirx) { Vector3 px = WorldToScreen(position + dirx); vertices[1] = new Vector2(px.X, px.Y); line.Draw(vertices, Color.FromArgb(255, 0, 0));//R } if (!hidden_diry) { Vector3 py = WorldToScreen(position + diry); vertices[1] = new Vector2(py.X, py.Y); line.Draw(vertices, Color.FromArgb(0, 255, 0));//G } if (!hidden_dirz) { Vector3 pz = WorldToScreen(position + dirz); vertices[1] = new Vector2(pz.X, pz.Y); line.Draw(vertices, Color.FromArgb(0, 0, 255));//B } line.Dispose(); line = null; }
/// nodeを選択します。 /// returns: nodeを見つけたかどうか public bool SelectNode() { bool found = false; if (SelectedTSOFile != null) { //スクリーン座標からnodeを見つけます。 //衝突する頂点の中で最も近い位置にあるnodeを返します。 float x = lastScreenPoint.X; float y = lastScreenPoint.Y; int width = 5;//頂点ハンドルの幅 float min_z = 1e12f; TSONode found_node = null; foreach (TSONode node in SelectedTSOFile.nodes) { Vector3 p2 = GetNodePositionOnScreen(node); if (p2.X - width <= x && x <= p2.X + width && p2.Y - width <= y && y <= p2.Y + width) { if (p2.Z < min_z) { min_z = p2.Z; found = true; found_node = node; } } } if (found) { SelectedNode = found_node; } } return(found); }
/// メッシュ操作を生成します。 public SmoothMeshCommand(TSOFile tso, TSOMesh mesh, TSONode selected_node) { this.tso = tso; this.mesh = mesh; this.selected_node = selected_node; }
/// <summary> /// ボーン参照リストを生成します。 /// </summary> public void LinkBones(TSONode[] nodes) { this.bones = new List<TSONode>(); foreach (int bone_index in bone_indices) this.bones.Add(nodes[bone_index]); }
/// <summary> /// オフセット行列を計算します。 /// </summary> public void ComputeOffsetMatrix() { offset_matrix = TSONode.GetOffsetMatrix(this); }
/// <summary> /// ボーン参照リストを生成します。 /// </summary> public void LinkBones(TSONode[] nodes) { foreach (TSOSubMesh sub_mesh in sub_meshes) sub_mesh.LinkBones(nodes); }
/// <summary> /// 指定nodeに対するオフセット行列を計算します。 /// </summary> /// <param name="node">node</param> public static Matrix GetOffsetMatrix(TSONode node) { Matrix m = Matrix.Identity; while (node != null) { m *= node.TransformationMatrix; node = node.parent; } return Matrix.Invert(m); }
/// <summary> /// 指定ストリームから読み込みます。 /// </summary> /// <param name="source_stream">ストリーム</param> public void Load(Stream source_stream) { reader = new BinaryReader(source_stream, System.Text.Encoding.Default); byte[] magic = reader.ReadBytes(4); if (magic[0] != (byte)'T' || magic[1] != (byte)'S' || magic[2] != (byte)'O' || magic[3] != (byte)'1') { throw new Exception("File is not TSO"); } int node_count = reader.ReadInt32(); nodes = new TSONode[node_count]; for (int i = 0; i < node_count; i++) { nodes[i] = new TSONode(i); nodes[i].Read(reader); } GenerateNodemapAndTree(); int node_matrix_count = reader.ReadInt32(); Matrix m = Matrix.Identity; for (int i = 0; i < node_matrix_count; i++) { reader.ReadMatrix(ref m); nodes[i].TransformationMatrix = m; } for (int i = 0; i < node_matrix_count; i++) { nodes[i].ComputeOffsetMatrix(); } UInt32 texture_count = reader.ReadUInt32(); textures = new TSOTex[texture_count]; for (int i = 0; i < texture_count; i++) { textures[i] = new TSOTex(); textures[i].Read(reader); } UInt32 script_count = reader.ReadUInt32(); scripts = new TSOScript[script_count]; for (int i = 0; i < script_count; i++) { scripts[i] = new TSOScript(); scripts[i].Read(reader); } UInt32 sub_script_count = reader.ReadUInt32(); sub_scripts = new TSOSubScript[sub_script_count]; for (int i = 0; i < sub_script_count; i++) { sub_scripts[i] = new TSOSubScript(); sub_scripts[i].Read(reader); sub_scripts[i].GenerateShader(); } UInt32 mesh_count = reader.ReadUInt32(); meshes = new TSOMesh[mesh_count]; for (int i = 0; i < mesh_count; i++) { meshes[i] = new TSOMesh(); meshes[i].Read(reader); meshes[i].LinkBones(nodes); //Console.WriteLine("mesh name {0} len {1}", mesh.name, mesh.sub_meshes.Length); } }
/// <summary> /// 指定ストリームから読み込みます。 /// </summary> /// <param name="source_stream">ストリーム</param> public void Load(Stream source_stream) { BinaryReader reader = new BinaryReader(source_stream, System.Text.Encoding.Default); byte[] magic = reader.ReadBytes(4); if (magic[0] != (byte)'T' || magic[1] != (byte)'S' || magic[2] != (byte)'O' || magic[3] != (byte)'1') throw new Exception("File is not TSO"); int node_count = reader.ReadInt32(); nodes = new TSONode[node_count]; for (int i = 0; i < node_count; i++) { nodes[i] = new TSONode(i); nodes[i].Read(reader); } GenerateNodemapAndTree(); int node_matrix_count = reader.ReadInt32(); Matrix m = Matrix.Identity; for (int i = 0; i < node_matrix_count; i++) { reader.ReadMatrix(ref m); nodes[i].TransformationMatrix = m; } for (int i = 0; i < node_matrix_count; i++) { nodes[i].ComputeOffsetMatrix(); } UInt32 texture_count = reader.ReadUInt32(); textures = new TSOTex[texture_count]; for (int i = 0; i < texture_count; i++) { textures[i] = new TSOTex(); textures[i].Read(reader); } UInt32 script_count = reader.ReadUInt32(); scripts = new TSOScript[script_count]; for (int i = 0; i < script_count; i++) { scripts[i] = new TSOScript(); scripts[i].Read(reader); } UInt32 sub_script_count = reader.ReadUInt32(); sub_scripts = new TSOSubScript[sub_script_count]; for (int i = 0; i < sub_script_count; i++) { sub_scripts[i] = new TSOSubScript(); sub_scripts[i].Read(reader); sub_scripts[i].GenerateShader(); } UInt32 mesh_count = reader.ReadUInt32(); meshes = new TSOMesh[mesh_count]; for (int i = 0; i < mesh_count; i++) { meshes[i] = new TSOMesh(); meshes[i].Read(reader); meshes[i].LinkBones(nodes); //Console.WriteLine("mesh name {0} len {1}", mesh.name, mesh.sub_meshes.Length); } }
/// 選択ボーンに対応するウェイトを加算する。 /// returns: ウェイトを変更したか public bool Execute() { foreach (MqoSkinWeight skin_weight in vertex.skin_weights) { SkinWeightCommand skin_weight_command = new SkinWeightCommand(skin_weight); this.skin_weight_commands.Add(skin_weight_command); } //処理前の値を記憶する。 { int nskin_weight = 0; foreach (MqoSkinWeight skin_weight in vertex.skin_weights) { this.skin_weight_commands[nskin_weight].old_attr.bone = skin_weight.bone; this.skin_weight_commands[nskin_weight].old_attr.weight = skin_weight.weight; nskin_weight++; } } bool updated = false; //選択ボーンに対応するウェイトを検索する。 MqoSkinWeight selected_skin_weight = null; foreach (MqoSkinWeight skin_weight in vertex.skin_weights) { if (skin_weight.bone == selected_node) { selected_skin_weight = skin_weight; break; } } bool prepare_bone = false; switch (weight_op) { case WeightOperation.Gain: prepare_bone = weight > 0; break; case WeightOperation.Assign: prepare_bone = weight > 0; break; } //選択ボーンに対応するウェイトがなければ、最小値を持つウェイトを置き換える。 if (selected_skin_weight == null && prepare_bone) { TSONode bone = selected_node; { selected_skin_weight = vertex.skin_weights[3]; //前提: vertex.skin_weights の要素数は 4 かつ並び順はウェイト値の降順 selected_skin_weight.bone = bone; selected_skin_weight.weight = 0.0f; } } //選択ボーンに対応するウェイトを加算する。 if (selected_skin_weight != null) { updated = true; float w0 = selected_skin_weight.weight; //変更前の対象ウェイト値 float m0 = 1.0f - w0; //変更前の残りウェイト値 float w1; //変更後の対象ウェイト値 switch (weight_op) { case WeightOperation.Gain: w1 = w0 + weight; break; case WeightOperation.Reduce: w1 = w0 - weight; break; case WeightOperation.Assign: w1 = weight; break; default: w1 = w0; break; } //clamp 0.0f .. 1.0f if (w1 > 1.0f) { w1 = 1.0f; } if (w1 < 0.0f) { w1 = 0.0f; } float d1 = w1 - w0; //実際の加算値 float m1 = 0.0f; //減算後の残りウェイト値 if (m0 != 0) { //残りウェイトを減算する。 foreach (MqoSkinWeight skin_weight in vertex.skin_weights) { if (skin_weight == selected_skin_weight) { continue; } float w2 = skin_weight.weight - skin_weight.weight * d1 / m0; if (w2 < 0.001f) { w2 = 0.0f;//微小ウェイトは捨てる。 } skin_weight.weight = w2; m1 += w2; } } selected_skin_weight.weight = 1.0f - m1; Array.Sort(vertex.skin_weights); } #if false { int nskin_weight = 0; foreach (MqoSkinWeight skin_weight in vertex.skin_weights) { Console.WriteLine("i:{0} bone:{1} w:{2}", nskin_weight, skin_weight.bone.Name, skin_weight.weight); nskin_weight++; } Console.WriteLine(); } #endif //処理後の値を記憶する。 { int nskin_weight = 0; foreach (MqoSkinWeight skin_weight in vertex.skin_weights) { this.skin_weight_commands[nskin_weight].new_attr.bone = skin_weight.bone; this.skin_weight_commands[nskin_weight].new_attr.weight = skin_weight.weight; nskin_weight++; } } return(updated); }
/// <summary> /// フィギュアに含まれるnode treeを描画する。 /// </summary> void DrawNodeTree() { TSOFile tso = selected_tso_file; if (tso == null) { return; } Line line = new Line(device); foreach (TSONode node in tso.nodes) { if (HiddenNode(node)) { continue; } Vector3 p0 = GetNodePositionOnScreen(node); p0.Z = 0.0f; TSONode parent_node = node.parent; if (parent_node != null) { if (HiddenNode(parent_node)) { continue; } Vector3 p1 = GetNodePositionOnScreen(parent_node); p1.Z = 0.0f; Vector3 pd = p0 - p1; float len = Vector3.Length(pd); float scale = 4.0f / len; Vector2 p3 = new Vector2(p1.X + pd.Y * scale, p1.Y - pd.X * scale); Vector2 p4 = new Vector2(p1.X - pd.Y * scale, p1.Y + pd.X * scale); Vector2[] vertices = new Vector2[3]; vertices[0] = new Vector2(p3.X, p3.Y); vertices[1] = new Vector2(p0.X, p0.Y); vertices[2] = new Vector2(p4.X, p4.Y); line.Draw(vertices, TSONodeLineColor); } } line.Dispose(); line = null; Rectangle rect = new Rectangle(0, 16, 15, 15); //node circle Vector3 rect_center = new Vector3(7, 7, 0); sprite.Begin(SpriteFlags.None); foreach (TSONode node in tso.nodes) { if (HiddenNode(node)) { continue; } Vector3 p0 = GetNodePositionOnScreen(node); p0.Z = 0.0f; sprite.Draw(dot_texture, rect, rect_center, p0, Color.White); } sprite.End(); }
/// <summary> /// 指定nodeを追加します。 /// </summary> /// <param name="node">node</param> /// <returns>ボーン参照配列の添字</returns> public int AddBone(TSONode node) { int end = AddBoneIndex(node.ID); if (end != -1) bones.Add(node); return end; }
/// <summary> /// 指定ライタにnodeを書き出します。 /// </summary> /// <param name="bw">ライタ</param> /// <param name="item">node</param> public static void Write(BinaryWriter bw, TSONode item) { Write(bw, item.Name); }
public MqoSkinWeight(TSONode bone, float weight) { this.bone = bone; this.weight = weight; }