#reactjs #react-native #three.js #expo #wavefront
#reactjs #react-native #three.js #expo #волновой фронт
Вопрос:
Моя проблема заключается в том, как я могу использовать файлы .mtl и .obj с expo three.js но я не хочу использовать AR, я хочу использовать только простой холст / представление с вращающимся объектом. Этот код — то, что я хочу, но с моим obj-файлом, а не для создания куба.
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;
}
}
Мой assets.js файл, содержащий путь к моему 3D-модальному в .obj
export default {
obj: {
"museu.obj": require('../Conteudos_AV/museu1.obj'),
}
};
И мой threeStage.js файл, который содержит при импорте 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;
Ответ №1:
Похоже, что предоставленный код создает класс ThreeStage, который импортируется, но никогда не используется классом, содержащим Expo GraphicsView.
Примеры, представленные в репозитории для expo-three, используют немного эзотерическую структуру, поскольку каждый из них предназначен для обслуживания через приложение для навигации по реакции с централизованной библиотекой ресурсов и абстрактными компонентами. Это слишком много для простого приложения, которое просто пытается отобразить модель на экране.
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);
};
}
Я адаптировал этот код из одного из примеров Evan’s expo snack, которым немного легче следовать, поскольку они не требуют больших затрат на весь пример приложения. Вы можете найти больше на его странице закусок expo здесь:https://expo.io/snacks/@bacon.
Этот код должен отображать ваш объектный файл, но может возникнуть проблема, если ваш .obj использует дополнительные материалы или файлы текстур. Если это так, вам нужно будет добавить их в функцию loadModel следующим образом:
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
);
Я бы порекомендовал взглянуть на expo snacks, в которых используется expo-three, а не на приложение example, когда вы начинаете, поскольку проработка всех тонкостей примера может оказаться непростой задачей.
В данный момент у меня нет удобного устройства для тестирования, но дайте мне знать, если у вас возникнут какие-либо проблемы с приведенным выше кодом, и я смогу устранить неполадки, когда вернусь к телефону и ноутбуку вместе.