如何在Doom3场景中运行多个MD5骨骼动画序列
2. 实现Doom3SceneWithMD5SkeletionApplication类:
import { mat4, vec3 } from "../common/math/TSM";
import { HttpRequest } from "../common/utils/HttpRequest";
import { MD5SkinedMesh } from "../lib/MD5SkinedMesh";
import { GLMeshBuilder, EVertexLayout } from "../webgl/WebGLMesh";
import { GLTextureCache } from "../webgl/WebGLTextureCache";
import { GLAttribState } from "../webgl/WebGLAttribState";
import { Doom3Application } from "./Doom3Application";
import { CanvasKeyBoardEvent } from "../common/Application";
/* 继承关系:
Application
WebGLApplication
CameraApplication
Doom3Application(第九章实现的Demo)
Doom3SceneWithMD5SkeletionApplication
*/
export class Doom3SceneWithMD5SkeletionApplication extends Doom3Application{
public model:MD5SkinedMesh; // 要运行动画和显示的骨骼动画
public currAnimId:number = 0; // 一个骨骼动画可以有多个动画序列,本Demo中包括【0:跑,1:出拳】
public currFrame:number = 0; // 每个动画序列可以有n幅动画帧组成
public texBuilder:GLMeshBuilder; // 使用纹理绘制器
public constructor(canvas: HTMLCanvasElement){
super( canvas); // 调用基类Doom3Application类的构造函数
// 初始化本类的成员变量
this.model = new MD5SkinedMesh();
this.texBuilder = new GLMeshBuilder( this.gl, GLAttribState.POSITION_BIT | GLAttribState.TEXCOORD_BIT, this.program, GLTextureCache.instance.getMust("default"), EVertexLayout.INTERLEAVED );
}
覆写(override)基类渲染资源加载的async run虚方法
// 本书在Application这个最顶层的类中实现了一套异步/同步加载的框架流程
// 覆写基类的async run虚方法,该方法返回Promise<void>类型
// 因此可以使用await进行等待同步
public async run (): Promise<void>
{
// 1. 使用http从服务器请求MD5的Mesh数据,注意:使用await!!!!
let response: string = await HttpRequest.loadTextFileAsync( MD5SkinedMesh.path + "suit.md5mesh" );
this.model.parse(response); // 一旦获得MD5的Mesh数据后,进行词法解析并在内存中生成对应数据结构
// 2. 然后再从服务器请求获得纹理数据,注意:也使用了await!!!!
await this.model.loadTextures(this.gl);
// 3. 再向服务器请求跑的动画序列数据,并且进行解析,生成对应的数据结构
// 注意:使用await!!!!
response = await HttpRequest.loadTextFileAsync( MD5SkinedMesh.path + "suit_walk.md5anim");
this.model.parseAnim(response);
// 4. 接着再向服务器请求出拳的动画序列,并且进行解析,生成对应的数据结构
// 注意:使用await!!!!
response = await HttpRequest.loadTextFileAsync( MD5SkinedMesh.path + "suit_punch.md5anim");
this.model.parseAnim(response);
// 记住一定要调用基类的run方法
// 用来加载Doom3 Proc场景数据
super.run();
}
覆写(override)基类的update和onKeyUp虚方法
// 覆写基类的update虚方法
public update ( elapsedMsec: number, intervalSec: number ): void
{
super.update( elapsedMsec, intervalSec ); // 先调用基类的update方法
// 然后更新当前序列的帧号
this.currFrame++;
// 周而复始连续播放算法
this.currFrame %= this.model.anims[this.currAnimId].frames.length;
// 计算出当前动画序列的当前帧的姿态
this.model.playAnim(this.currAnimId,this.currFrame);
}
// 覆写基类的onKeyUp事件处理虚方法
// 当按1键时,运行出拳动画序列
// 当按其他任意键时,播放跑的动画序列
protected onKeyUp ( evt: CanvasKeyBoardEvent ): void
{
if(evt.key==="1"){
this.currAnimId = 1;
this.currFrame = 0;
}else{
this.currFrame = 0;
this.currAnimId = 0;
}
// 调用基类的键盘事件方法
super.onKeyUp(evt);
}
// 一旦更新好后,就需要绘制
// 实现一个受保护的方法,用来渲染MD5骨骼动画
protected renderDoom3MD5Skeleton():void{
this.matStack.loadIdentity();
this.matStack.rotate(-90,vec3.right); // 将id Doom3坐标系变换为WebGL坐标系
this.matStack.translate(new vec3([0,0,4]))
this.matStack.rotate(this.angle,vec3.forward);
mat4.product(this.camera.viewProjectionMatrix,this.matStack.modelViewMatrix,mat4.m0);
this.model.drawBindPose(this.texBuilder,mat4.m0); // 绘制bindpose
this.matStack.pushMatrix();
this.matStack.translate(new vec3([1.0,0,0]));
mat4.product(this.camera.viewProjectionMatrix,this.matStack.modelViewMatrix,mat4.m0);
this.model.drawAnimPose(this.texBuilder,mat4.m0); // 绘制当前序列的某帧姿态
this.matStack.popMatrix();
}
// 覆写基类的render虚方法
public render (): void
{
// 先调用基类同名方法,这样就会绘制出整个Doom3场景
super.render();
// 然后在Doom3场景中显示运行MD5骨骼动画
this.renderDoom3MD5Skeleton();
}
} // 类定义结速