简体   繁体   中英

How to use .obj files in expo three.js without AR module? [React Native Expo]

My problem is about how can I use .mtl and .obj files with expo three.js, but I don't want to use AR, I only want to use a simple canvas/View with the object rotating. This code is the thing I want but with my obj file, not to create a cube.

 import { View as GraphicsView } from 'expo-graphics';
 import ExpoTHREE, { THREE } from 'expo-three';
 import React from 'react';
 import Assets from './Assets.js';
 import ThreeStage from './ThreeStage.js';
 export default class App extends React.Component {
   componentWillMount() {
     THREE.suppressExpoWarnings();
   }

   render() {
     return (
      <GraphicsView
        onContextCreate={this.onContextCreate}
         onRender={this.onRender}
       />
     );
   }

   async setupModels() {
     await super.setupModels();

     const model = Assets.models.obj.ninja;

     const SCALE = 2.436143; // from original model
     const BIAS = -0.428408; // from original model


     const object = await ExpoTHREE.loadObjAsync({
       asset: require('ninja.obj'),
     });

     const materialStandard = new THREE.MeshStandardMaterial({
       color: 0xffffff,
       metalness: 0.5,
       roughness: 0.6,
       displacementScale: SCALE,
       displacementBias: BIAS,
      normalScale: new THREE.Vector2(1, -1),
       //flatShading: true,
       side: THREE.DoubleSide,
     });

     const geometry = object.children[0].geometry;
    geometry.attributes.uv2 = geometry.attributes.uv;
     geometry.center();
     const mesh = new THREE.Mesh(geometry, materialStandard);
     mesh.scale.multiplyScalar(0.25);

    ExpoTHREE.utils.scaleLongestSideToSize(mesh, 1);
     ExpoTHREE.utils.alignMesh(mesh, { y: 1 });
     this.scene.add(mesh);
     this.mesh = mesh;
   }

   onRender(delta) {
     super.onRender(delta);
     this.mesh.rotation.y += 0.5 * delta;
   }

 }

My assets.js file which contains the path to my 3D modal in .obj

export default {

    obj: {


            "museu.obj": require('../Conteudos_AV/museu1.obj'),

    }
};

And my threeStage.js file which contains in import of 3DModal.js

import ExpoTHREE, { THREE } from 'expo-three';

class ThreeStage {
  constructor() {
    this.onRender = this.onRender.bind(this);
    this.setupControls = this.setupControls.bind(this);
    this.onResize = this.onResize.bind(this);
    this.setupCamera = this.setupCamera.bind(this);
    this.setupScene = this.setupScene.bind(this);
  }
  onContextCreate = async ({
    gl,
    canvas,
    width,
    height,
    scale: pixelRatio,
  }) => {
    this.gl = gl;
    this.canvas = canvas;
    this.width = width;
    this.height = height;
    this.pixelRatio = pixelRatio;
    await this.setupAsync();
  };

  setupAsync = async () => {
    const { gl, canvas, width, height, pixelRatio } = this;
    await this.setupRenderer({ gl, canvas, width, height, pixelRatio });
    await this.setupScene();
    await this.setupCamera({ width, height });
    await this.setupLights();
    await this.setupModels();
    await this.setupControls();
  };

  setupControls() {
    new THREE.OrbitControls(this.camera);
  }

  setupRenderer = props => {
    this.renderer = new ExpoTHREE.Renderer(props);
    this.renderer.capabilities.maxVertexUniforms = 52502;
  };

  setupCamera({ width, height }) {
    this.camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 10000);
    this.camera.position.set(0, 6, 12);
    this.camera.lookAt(0, 0, 0);
  }

  setupScene() {
    this.scene = new THREE.Scene();

    this.scene.background = new THREE.Color(0x999999);
    this.scene.fog = new THREE.FogExp2(0xcccccc, 0.002);

    this.scene.add(new THREE.GridHelper(50, 50, 0xffffff, 0x555555));
  }

  setupLights = () => {
    const directionalLightA = new THREE.DirectionalLight(0xffffff);
    directionalLightA.position.set(1, 1, 1);
    this.scene.add(directionalLightA);

    const directionalLightB = new THREE.DirectionalLight(0xffeedd);
    directionalLightB.position.set(-1, -1, -1);
    this.scene.add(directionalLightB);

    const ambientLight = new THREE.AmbientLight(0x222222);
    this.scene.add(ambientLight);
  };

  async setupModels() {}

  onResize({ width, height, scale }) {
    this.camera.aspect = width / height;
    this.camera.updateProjectionMatrix();
    this.renderer.setPixelRatio(scale);
    this.renderer.setSize(width, height);
    this.width = width;
    this.height = height;
    this.pixelRatio = scale;
  }

  onRender(delta) {
    this.renderer.render(this.scene, this.camera);
  }
}

export default ThreeStage;

It looks like the provided code creates a ThreeStage class that is imported, but never used by the class containing the Expo GraphicsView.

The examples provided in the repo for expo-three use a bit of an esoteric structure since they're each meant to be served through a react-navigation app with a centralized asset library and abstracted components. This is a lot of extra for a simple application that just attempts to display a model on the screen.

import React from 'react';
import ExpoTHREE, { THREE } from 'expo-three';
import { GraphicsView } from 'expo-graphics';

export default class App extends React.Component {

  componentDidMount() {
    THREE.suppressExpoWarnings();
  }

  render() {
    return (
      <GraphicsView
        onContextCreate={this.onContextCreate}
        onRender={this.onRender}
        onResize={this.onResize}
      />
    );
  }

  // When our context is built we can start coding 3D things.
  onContextCreate = async ({ gl, pixelRatio, width, height }) => {

    // Create a 3D renderer
    this.renderer = new ExpoTHREE.Renderer({
      gl,
      pixelRatio,
      width,
      height,
    });

    // We will add all of our meshes to this scene.
    this.scene = new THREE.Scene();

    this.scene.background = new THREE.Color(0xbebebe)

    this.camera = new THREE.PerspectiveCamera(45, width/height, 1, 1000)

    this.camera.position.set(3, 3, 3);

    this.camera.lookAt(0, 0, 0);

    this.scene.add(new THREE.AmbientLight(0xffffff));

    await this.loadModel();
  };

  loadModel = async () => {
    const obj = {
      "museu.obj": require('../Conteudos_AV/museu1.obj')
    }

    const model = await ExpoTHREE.loadAsync(
      obj['museu.obj'],
      null,
      obj
    );

    // this ensures the model will be small enough to be viewed properly
    ExpoTHREE.utils.scaleLongestSideToSize(model, 1);

    this.scene.add(model)

  };


  // When the phone rotates, or the view changes size, this method will be called.
  onResize = ({ x, y, scale, width, height }) => {
    // Let's stop the function if we haven't setup our scene yet
    if (!this.renderer) {
      return;
    }
    this.camera.aspect = width / height;
    this.camera.updateProjectionMatrix();
    this.renderer.setPixelRatio(scale);
    this.renderer.setSize(width, height);
  };

  // Called every frame.
  onRender = delta => {


    // Finally render the scene with the Camera
    this.renderer.render(this.scene, this.camera);
  };
}

I adapted this code from one of Evan's expo snack examples, which are a little easier to follow since they don't have a lot of the overhead of the whole example app. You can find more on his expo snack page here: https://expo.io/snacks/@bacon .

This code should render your object file, but may run into issues if your .obj relies on additional material or texture files. If that is the case, you'll need to add them to the loadModel function like this:

    const obj = {
      "museu.obj": require('../Conteudos_AV/museu1.obj'),
      "museu.mtl": require('../Conteudos_AV/museu1.mtl'),
      "museu.png": require('../Conteudos_AV/museu1.png'),
    }

    const model = await ExpoTHREE.loadAsync(
      [obj['museu.obj'], obj['museu.mtl']],
      null,
      obj
    );

I'd recommend taking a look at expo snacks that use expo-three rather than the example app when you're getting started, as it can be a bit of a quagmire to work through all the intricacies of the example.

I don't have a device handy at the moment to test, but let me know if you have any issues with the above code and I can troubleshoot when I'm back beside a phone and laptop together.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM