主界面
视频演示
部分代码
初始设置
void ofApp::setup(){
ofSetLogLevel(OF_LOG_VERBOSE); //设置日志级别:冗长
ofBackground(50, 0);//亮度50 透明度0
ofDisableArbTex(); //我们的模型坐标需要 GL_TEXTURE_2D。we need GL_TEXTURE_2D for our models coords.
bAnimate = true;//动画
bAnimateMouse = false;//禁用鼠标动画
animationPosition = 0;//动画位置
loadModel("Fox/Fox_05.fbx"); //默认加载 狐狸.fbx
bHelpText = true;//显示帮助文本
bUseCamera = true;//使用相机
#if defined(TARGET_EMSCRIPTEN)//目标脚本
bEnableAutoSwitch = true;//启用自动切换
#endif
}
加载模型
void ofApp::loadModel(int aindex){
//四个模型:狐狸,飞行头盔,德鲁伊,天文男孩走路
vector<string> modelPaths = {
"Fox/Fox_05.fbx",
"FlightHelmet/FlightHelmet.gltf",
"Druid/druid.gltf",
"Astroboy/astroBoy_walk.dae"
};
#if !defined(TARGET_OPENGLES) //定义了 opengl es
//模型在 emscripten 中看起来很奇怪 model looks strange in emscripten
modelPaths.push_back("Payphone/korean_public_payphone_01_1k.gltf");
#endif
//夹在最小值和最大值之间的值。将值限制在由值 min 和 max 定义的指定范围内。如果值为 min <= value <= max,则返回值。
// 如果值大于max,返回max;如果该值小于 min,则返回 min。
modelIndex = ofClamp(aindex, 0, (int)modelPaths.size()-1 );
loadModel( modelPaths[modelIndex] );//加载模型
}
void ofApp::loadModel(string filename){
if( model.load(filename, ofxAssimpModelLoader::OPTIMIZE_DEFAULT) ){
if( model.hasAnimations() ){//模型带有动画
animationIndex = 0;//动画索引初始为0
model.setLoopStateForAllAnimations(OF_LOOP_NORMAL);//一遍又一遍地重复视频。
model.getAnimation(animationIndex).play();//播放第 animationIndex+1 个动画
}
}else{
ofLogError() << " can't load model: " << filename << endl;
}
mTimeModelLoaded = ofGetElapsedTimef(); //获取当前逝去时间 记录模型初次加载时间
}
更新
void ofApp::update(){
float sceneHeight = fabs((model.getSceneMaxModelSpace()-model.getSceneMinModelSpace()).y);//获取模型场景高度方向的范围
//使用相机
if(bUseCamera){
model.setScale(1, -1, 1);//镜像模型的y值
model.setRotation(0, 180, 0, 1, 0);//绕y轴旋转模型180
model.setPosition(0, -sceneHeight * 0.5, 0); //设置模型位置,使模型body中心在原点 y=-模型场景高度/2
light.setPosition(model.getPosition() + glm::vec3(-300, 300, 1200));//设置光源位置
}else{//不适用相机:不镜像y值
model.setScale(1, 1, 1); //
model.setRotation(0, 180, 0, 1, 0);
model.setPosition(ofGetWidth()/2, ofGetHeight()/2 + sceneHeight * 0.5, 0);//设置模型位置
light.setPosition(model.getPosition() + glm::vec3(-300, -300, 1200));
}
model.update();//更新模型显示
//模型由动画
if( model.hasAnimations() ){
if(bAnimateMouse) { //鼠标触发动画
//setPositionForAllAnimations()是一个ofAnimation的方法,它用于设置所有添加到该ofAnimation对象上的动画的起始位置。
model.setPositionForAllAnimations(animationPosition);//
}
mesh = model.getCurrentAnimatedMesh(0);//getCurrentAnimatedMesh()是ofAnimation的另一个方法,它用于获取当前正在播放的动画所对应的ofMesh对象。
}
//自动切换模型 10s 切换
if( bEnableAutoSwitch ) {
float ctime = ofGetElapsedTimef();//获取当前逝去时间
if( ctime - mTimeModelLoaded > 10.0f ) {//判断时间间隔大于10s
modelIndex++;//切换模型
if( modelIndex >= 5 ) {
modelIndex = 0;
}
loadModel(modelIndex);//加载模型
}
} else {
mTimeModelLoaded = ofGetElapsedTimef(); //更新初次加载模型时间
}
}
绘制
void ofApp::draw(){
ofSetColor(255); //灰度值255
ofEnableDepthTest();//启用深度测试
if(bUseCamera)cam.begin(); //使用相机:鼠标移动旋转相机。不使用相机:鼠标移动旋转模型。
ofEnableLighting();//启用光照
light.enable();//启用光源light
ofEnableSeparateSpecularLight();//启用 单独的镜面光
ofPushMatrix();
if( !bUseCamera ) {//不使用相机 :模型在原地
ofTranslate(model.getPosition().x, model.getPosition().y, 0); //模型平移
ofRotateDeg(mouseX+30, 0, 1, 0);//绕y轴旋转
ofTranslate(-model.getPosition().x, -model.getPosition().y, 0);// 模型返回平移
}
model.drawFaces();//绘制模型面片
ofPopMatrix();
light.disable();
ofDisableLighting();
ofDisableSeparateSpecularLight();//禁用单独的镜面光
if( bUseCamera ){//使用相机
ofDrawSphere(light.getPosition(), 10);
}
if(bUseCamera)cam.end();//结束相机渲染
ofDisableDepthTest();//禁用深度测试
//帮助文本
if(bHelpText){
ofSetColor(255, 255, 255 );
string str;
str += "FPS: " + ofToString(ofGetFrameRate(),0) + "\n\n";
str +="(keys 1-5):加载模型 load models\n";
str += "该模型中的动画数量num of animations in this model: " + ofToString(model.getAnimationCount()) + " <- -> to change\n";
str +="(Spacebar): 切换动画toggle animation\n";
str +="(鼠标左键在 y 轴上拖动LEFT MOUSE BUTTON DRAG in y-axis): 控制动画control animation.\n";
str += "(c): 切换相机toggle camera: " + (bUseCamera ? string(" using ofEasyCam with (0,0) as screen center. \n") : string(" default view (0,0) is top left \n"));
str += "(h): toggle help.\n";
str += "(a): 自动切换auto switch: " + (bEnableAutoSwitch ? string("enabled") + " "+ofToString( ofClamp(10.f-(ofGetElapsedTimef()-mTimeModelLoaded), 0.f, 10.f), 1) : string("disabled"))+"\n";
str += "(f): toggle fullscreen: " + ofToString(ofGetWidth(),0)+" x "+ofToString(ofGetHeight(),0)+"\n";
ofDrawBitmapString(str, 20, 20);//显示文本
}
}
按键操作
void ofApp::keyPressed(int key){
switch (key) {
case '1': //按键1-5切换模型,禁用自动切换
bEnableAutoSwitch = false;
loadModel(0);
break;
case '2':
bEnableAutoSwitch = false;
loadModel(1);
break;
case '3':
bEnableAutoSwitch = false;
loadModel(2);
break;
case '4':
bEnableAutoSwitch = false;
loadModel(3);
break;
case '5':
bEnableAutoSwitch = false;
loadModel(4);
break;
case 'c': //使用相机
bUseCamera = !bUseCamera;
break;
case ' ':
bAnimate = !bAnimate;//切换禁用动画
if( model.hasAnimations() ){
if( bAnimate ){
model.getAnimation(animationIndex).play();//播放动画
}else{
model.stopAllAnimations();
}
}
break;
case 'h':
bHelpText = !bHelpText;
break;
case OF_KEY_RIGHT://->切换动画播放
if( model.hasAnimations() ){
model.stopAllAnimations();
animationIndex++;
animationIndex %= model.getAnimationCount();
model.getAnimation(animationIndex).play();
}
break;
case 'a'://切换 是否自动切换
bEnableAutoSwitch = !bEnableAutoSwitch;
break;
case 'f'://全屏
ofToggleFullscreen();
break;
default:
break;
}
}
改变动画播放的位置
//鼠标拖拽, 沿着y方向---------------
void ofApp::mouseDragged(int x, int y, int button){
// //改变动画播放的位置
animationPosition = y / (float)ofGetHeight();
}
鼠标按下:暂停所有动画
void ofApp::mousePressed(int x, int y, int button){
// pause all animations, so we can scrub through them manually.
model.setPausedForAllAnimations(true);
animationPosition = y / (float)ofGetHeight();//更新动画播放位置
bAnimateMouse = true;//鼠标调整动画位置启用
}
释放鼠标
void ofApp::mouseReleased(int x, int y, int button){
// unpause animations when finished scrubbing.
if(bAnimate) {//开启动画
model.setPausedForAllAnimations(false);//结束暂停动画
}
bAnimateMouse = false;//禁用鼠标调整动画位置
}
附 Claude提供的帮助
setPositionForAllAnimations 用法
getCurrentAnimatedMesh 用法
The End