前面我们分析了静态模型OBJ格式,桢动画模型MD2,这篇主要分析骨骼动画MD5的一些概念并且实现。

  混合桢动画有计算简单,容易实现等优点,但是在需要比较细致的效果时,则需要更多的关键桢,每桢都添加相同的顶点,如果模型再细分一些,则比较恐怖了。在这基础上,则发展出了骨骼动画模型,原理说起来很简单,比如我们人类,做的各种动作具体都是由几个关节点来控制,比如你抬腿,你只把你大腿的骨骼调动起来,而大腿的肌肉跟着骨骼向上。由些我们只需要保存每桢的骨骼变动,然后再上面蒙上表皮。因此大量简单了顶点存储,并且,我们能方便的对骨骼实时改动就能添加不同的动画,但是因为骨骼的改变都是针对父骨骼来的,而蒙皮操作又是针对骷髅节点来做的,这些操作需要大量的运算。

  下面我们来解析MD5骨骼模型中,一些基本的概念与实现,在MD5,除去纹理图片,有二个比较主要的文件,一个是后缀为md5mesh的文件,一个是后缀为md5anim的文件,二个文件如他们的后缀名所表达的意思一样,前者和OBJ模型里的描述比较类似,主要包含每部分的顶点,面,纹理组成,不同于OBJ模型的的,这些元素是变化的,因此在OBJ模型有一些新的元素,如顶点不是单独的顶点,而是由一个或多个权重点构成,每个权重点关联着对应着骨骼节点,这样骨骼节点的改变能引起权重点的改变,而权重点的改变又引起了顶点的改变,至于为什么要用到权重点来连接骨骼和顶点,而不是直接用骷髅和顶点关联,首先拿我们来说,我们身上有些位置并不是只和一个骨骼节点有关,更多是和多个节点有关,这样能让动画更真实,也避免在关节点产生重合和断裂的现象。

  首先我们来解析md5mesh文件里的信息,在这个文件里,主要有二大元素,一个是骨骼节点信息,一个是多个部位蒙皮信息,下面我简化了一个md5mesh,实际肯定不可能这样,主要是用来说明各节点用的。

  MD5骨骼动画模型加载(一)

  在文件中我都加了注释,简单来说,第一行是版本信息,下面写的解析也是针对这个10的版本,然后是命令行,骨骼节点数,蒙皮组件数,然后是骨骼节点的具体信息,在这里包含每个骨骼的父索引,顶点位置,四元数(包含旋转信息).在这里特别说下,这个骨骼节点的顺序暗含他们自己的索引,还有特别一点,在md5mesh文件中,骨骼的顶点位置与旋转信息是针对模型空间的,后面我们会看到在md5anim也有骨骼节点下的顶点位置与旋转信息,但是那是针对父骨骼节点坐标来的。然后就是蒙皮各个部分的详细信息,包含纹理坐标位置,顶点数,面的信息,权重信息,如前面所说,一个面包含3个顶点,每个顶点包含多个权重,每个权重关联一个或多个骨骼信息。下面根据上面各个部位来定义我们代码里各个类:

 1 type ArrayList<'T> = System.Collections.Generic.List<'T>
 2 let filtLine (line:string) = not (String.IsNullOrEmpty(line)) && line <> "(" && line <> ")" 
 3 let getLineData (line:string) = line.Split(' ','\t','\"') |> Array.filter filtLine
 4 let getFloat str = snd (System.Single.TryParse(str))
 5 let getInt str = snd (System.Int32.TryParse(str))
 6 let getw x y z = 1.0f - x*x - y*y - z*z |> fun p -> if p < 0.f then 0.f else float32 (-Math.Sqrt(float p))
 7 
 8 //--------mesh里用的结构
 9 type Md5Joint() = 
10     member val Name = "" with get,set
11     member val Index = -2 with get,set
12     //位移
13     member val Position = Vector3.Zero with get,set
14     //旋转
15     member val Quat = Quaternion.Identity  with get,set       
16     member val ParentIndex = -2 with get,set    
17     member this.SetValue(line:string,ind:int) =
18         let ls = getLineData line// line.Split(' ','\t') |> Array.filter (fun p -> not (String.IsNullOrEmpty(p)))//&& p <> @"\t" && p.Length > 0)
19         let pos,quat =
20             let gf str = snd (System.Single.TryParse(str))
21             let x,y,z,a,b,c = (gf ls.[2]),(gf ls.[3]),(gf ls.[4]),(gf ls.[5]),(gf ls.[6]),(gf ls.[7])
22             let d = getw a b c
23             Vector3(x,y,z),Quaternion(a,b,c,d)
24         this.Name <- ls.[0]
25         this.ParentIndex <- snd (System.Int32.TryParse(ls.[1]))
26         this.Position <- pos
27         this.Quat <- quat
28         this
29 
30 //Vert包含纹理坐标,以及关联的权重,根据权重求顶点
31 type Md5Vert() =
32     member val Index = -1 with get,set
33     member val Texcoord = Vector2.Zero with get,set
34     member val WeightStart = 0 with get,set
35     member val WeightCount = 0 with get,set
36     //经过权重求顶点实际位置
37     member val Position = Vector3.Zero with get,set 
38     member val Normal = Vector3.Zero with get,set       
39     member this.SetValue(line:string) =
40         let ls =getLineData line// line.Split(' ','\t') |> Array.filter (fun p -> not (String.IsNullOrEmpty(p)))
41         let gf str = snd (System.Single.TryParse(str))
42         this.Index <- snd (System.Int32.TryParse(ls.[1]))
43         this.Texcoord <- Vector2(gf ls.[2],gf ls.[3])
44         this.WeightStart <- snd (System.Int32.TryParse(ls.[4]))
45         this.WeightCount <- snd (System.Int32.TryParse(ls.[5]))
46         this
47     member this.DataArray with get() =[| this.Texcoord.X;this.Texcoord.Y;this.Position.X;this.Position.Y;this.Position.Z|]
48 
49 //三角形(包含Vert的索引)
50 type Md5Tri() =
51     member val Index = -1 with get,set
52     member val VertorIndexs = Array.create 3 0 with get,set
53     member this.SetValue(line:string) =
54         let ls = getLineData line // line.Split(' ','\t') |> Array.filter (fun p -> not (String.IsNullOrEmpty(p)))
55         let gi str = snd (System.Int32.TryParse(str))
56         this.Index <- gi ls.[1]
57         this.VertorIndexs <- [|gi ls.[2];gi ls.[3];gi ls.[4]|]
58         this
59 
60 //权重,(权重顶点用于计算Md5Vert,权重的JointIndex用于得到对应joint的四元数)
61 type Md5Weight() =
62     member val Index = -1 with get,set
63     member val JointIndex = -1 with get,set
64     member val Bias = 0.f with get,set
65     member val Position = Vector3.Zero with get,set
66     member this.SetValue(line:string) =
67         let ls = line.Split(' ','\t') |> Array.filter (fun p -> not (String.IsNullOrEmpty(p)))
68         let gf str = snd (System.Single.TryParse(str))
69         this.Index <- snd (System.Int32.TryParse(ls.[1]))
70         this.JointIndex <- snd (System.Int32.TryParse(ls.[2]))
71         this.Bias <- gf ls.[3]
72         this.Position <- Vector3(gf ls.[5],gf ls.[6],gf ls.[7])
73         this
74 
75 type Md5Mesh() =
76     let mutable vbo,ebo = 0,0
77     member val TexID = 0 with get,set
78     member val ShaderPath = "" with get,set
79     member val Verts = ArrayList<Md5Vert>() with get,set
80     member val Faces = ArrayList<Md5Tri>() with get,set
81     member val Weights = ArrayList<Md5Weight>() with get,set
82     member this.ElementCount with get() = this.Faces.Count * 3
蒙皮节点描述类

相关文章:

  • 2021-09-08
  • 2021-04-20
  • 2022-02-14
  • 2021-10-16
  • 2022-12-23
  • 2022-01-01
猜你喜欢
  • 2021-06-05
  • 2022-12-23
  • 2021-08-20
相关资源
相似解决方案