【openframeworks】3D示例-加载fbx 、gltf 、dae 3D模型,播放动画(附 Claude提供的帮助)...

3ec8b1f7b38aee513020899b3dda07f0.png

主界面

视频演示


部分代码

初始设置

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 用法

66c5d4e24e88e0dff2ff80b63b4948a1.png

77c1e12dae4c656002a230a47ddc457b.png

getCurrentAnimatedMesh 用法

a955de178b8cb95056b788467c92f4ad.png

793a73335037c88a2d8efe380b35b030.png


The End

猜你喜欢

转载自blog.csdn.net/cxyhjl/article/details/130445636
今日推荐