Can't resolve 'three' in three-gltf-loader - vue.js

This is the first time that I cannot find an answer and have to write here. I am trying a three.js project with Vue.js. I have this error:
Failed to compile.
./node_modules/three-gltf-loader/index.js
Module not found: Error: Can't resolve 'three' in 'C:\Users\Skunk\Documents\dolfin\dolfin\node_modules\three-gltf-loader'
My code:
import * as THREE from 'three-js';
import GLTFLoader from 'three-gltf-loader';
export default {
name: 'HelloWorld',
props: {
msg: String
},
mounted(){
let scene = new THREE.Scene( );
let camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerWidth / window.innerHeight, 0.1, 1000);
let renderer = new THREE.WebGLRenderer( );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', function( )
{
let width = window.innerWidth;
let height = window.innerHeight;
renderer.setSize( width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix( );
} );
const loader = new GLTFLoader();
// Load a glTF resource
loader.load(
// resource URL
'models/dol.gltf',
// called when the resource is loaded
function ( gltf ) {
scene.add( gltf.scene );
gltf.animations; // Array<THREE.AnimationClip>
gltf.scene; // THREE.Group
gltf.scenes; // Array<THREE.Group>
gltf.cameras; // Array<THREE.Camera>
gltf.asset; // Object
},
// called while loading is progressing
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
}
);
camera.position.z = 3;
//let ambientLight = new THREE.AmbientLight( 0xFFFFFF, 0.8);
// scene.add( ambientLight );
// game logic
let update = function ( )
{};
// draw scene
let render = function( )
{
renderer.render( scene, camera );
};
// run game loop
let GameLoop = function( )
{
requestAnimationFrame( GameLoop );
update( );
render( );
};
GameLoop( );
}
}
Am I using pieces of code that are not compatible?

For starters, you should not be using the "three-js" node module. This is a really outdated version of Three that got stuck on r79 and hasn't been updated in 4 years. Instead, you should be using the official "three" node module, which is the legitimate library, and is currently on r124.
Second, just import the GLTF loader from the "three/examples" folder as demonstrated in the GLTF examples, instead of installing a whole new module.
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
const loader = new GLTFLoader();

Related

Loading an .obj file with Expo-three?

I am trying to insert a .obj file into a React Native app built using Expo.
From the examples I've found that are successfully working, most of these seem to rely on building spheres or cubes within the rendering. I haven't found a good example with a successful rendering of a local file, specifically .obj.
I'm using the expo-three documentation which describes rendering with obj files, but no working examples.
This is what I have so far, which is not producing any rendered object. But want to know if I am on the right track with this, and what I am missing to get the object to render.
Below is the current file code.
import { Renderer, TextureLoader } from 'expo-three';
import * as React from 'react';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import {
AmbientLight,
Fog,
GridHelper,
PerspectiveCamera,
PointLight,
Scene,
SpotLight,
} from 'three';
import { Asset } from 'expo-asset';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
export default function ThreeDPhytosaur() {
return (
<GLView
style={{ flex: 1 }}
onContextCreate={async (gl) => {
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
const sceneColor = 0x6ad6f0;
const renderer = new Renderer({ gl });
renderer.setSize(width, height);
renderer.setClearColor(sceneColor);
const camera = new PerspectiveCamera(70, width / height, 0.01, 1000);
camera.position.set(2, 5, 5);
const scene = new Scene();
scene.fog = new Fog(sceneColor, 1, 10000);
scene.add(new GridHelper(10, 10));
const ambientLight = new AmbientLight(0x101010);
scene.add(ambientLight);
const pointLight = new PointLight(0xffffff, 2, 1000, 1);
pointLight.position.set(0, 200, 200);
scene.add(pointLight);
const spotLight = new SpotLight(0xffffff, 0.5);
spotLight.position.set(0, 500, 100);
spotLight.lookAt(scene.position);
scene.add(spotLight);
const asset = Asset.fromModule(model['phytosaur']);
await asset.downloadAsync();
const objectLoader = new OBJLoader();
const object = await objectLoader.loadAsync(asset.uri);
object.scale.set(0.025, 0.025, 0.025);
scene.add(object);
camera.lookAt(object.position);
const render = () => {
timeout = requestAnimationFrame(render);
renderer.render(scene, camera);
gl.endFrameEXP();
};
render();
}}
/>
);
}
const model = {
'phytosaur': require('../assets/phytosaur.obj'),
};
Thanks very much!
This is the code that I got to render the obj file. Changed the structure of the original file based on some other examples found.
But this might help someone else!
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
import { Asset } from 'expo-asset';
import { Renderer} from 'expo-three';
import * as React from 'react';
import {
AmbientLight,
Fog,
PerspectiveCamera,
PointLight,
Scene,
SpotLight,
} from 'three';
export default function ThreeDTwo() {
let timeout;
React.useEffect(() => {
// Clear the animation loop when the component unmounts
return () => clearTimeout(timeout);
}, []);
return (
<GLView
style={{ flex: 1 }}
onContextCreate={async (gl) => {
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
const sceneColor = 668096;
// Create a WebGLRenderer without a DOM element
const renderer = new Renderer({ gl });
renderer.setSize(width, height);
renderer.setClearColor(0x668096);
const camera = new PerspectiveCamera(70, width / height, 0.01, 1000);
camera.position.set(2, 5, 5);
const scene = new Scene();
scene.fog = new Fog(sceneColor, 1, 10000);
const ambientLight = new AmbientLight(0x101010);
scene.add(ambientLight);
const pointLight = new PointLight(0xffffff, 2, 1000, 1);
pointLight.position.set(0, 200, 200);
scene.add(pointLight);
const spotLight = new SpotLight(0xffffff, 0.5);
spotLight.position.set(0, 500, 100);
spotLight.lookAt(scene.position);
scene.add(spotLight);
const asset = Asset.fromModule(require("../assets/phytosaur_without_mtl.obj"));
await asset.downloadAsync();
// instantiate a loader
const loader = new OBJLoader();
// load a resource
loader.load(
// resource URL
asset.localUri,
// called when resource is loaded
function ( object ) {
object.scale.set(0.065, 0.065, 0.065)
scene.add( object );
camera.lookAt(object.position)
//rotate my obj file
function rotateObject(object, degreeX=0, degreeY=0, degreeZ=0) {
object.rotateX(THREE.Math.degToRad(degreeX));
object.rotateY(THREE.Math.degToRad(degreeY));
object.rotateZ(THREE.Math.degToRad(degreeZ));
}
// usage:
rotateObject(object, 0, 0, 70);
//animate rotation
function update() {
object.rotation.x += 0.015
}
const render = () => {
timeout = requestAnimationFrame(render);
update();
renderer.render(scene, camera);
gl.endFrameEXP();
};
render();
},
// called when loading is in progresses
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( error );
}
);
}}
/>
);
}

How to set the spatial reference of a ESRI Javascript API map (3.x)?

Is it possible to change the spatial references of a map without providing a basemap? I would like to use a custom spatial reference so I can use custom lods.
In the sample below, if I uncomment the "streets" basemap the 6 features are rendered on the map.
But with it commented out I only see the vertical features because the map remains in 4326 and the 3857 points are not visible.
Assigning a value to the map.spatialReference has no effect.
Is it possible to impose a default spatial reference on the map without using a basemap? In the code below I tried using center, extent and direct assignment to the spatialReference but the only thing that worked was using the "streets" basemap, which I do not want to use.
import Map from "esri/map";
import SpatialReference from "esri/SpatialReference";
import Scalebar from "esri/dijit/Scalebar";
import GraphicsLayer from "esri/layers/GraphicsLayer";
import Graphic from "esri/graphic";
import Point from "esri/geometry/Point";
import Extent from "esri/geometry/Extent";
import SimpleMarkerSymbol from "esri/symbols/SimpleMarkerSymbol";
import SimpleLineSymbol from "esri/symbols/SimpleLineSymbol";
import Color from "esri/Color";
import * as projection from "esri/geometry/projection";
import "dojo/domReady!";
const range = (n: number) => {
return new Array(n).fill(n).map((_, i) => i);
};
const createMarker = (color: number) =>
new SimpleMarkerSymbol(
"solid",
8,
new SimpleLineSymbol(),
new Color([color, color, color, 1])
);
const baselineResolution = 156543.03408771486;
const baselineScale = 591657527.591555;
const lods = range(30).map((i) => {
const level = i;
const resolution = baselineResolution / Math.pow(2, level);
const scale = baselineScale / Math.pow(2, level);
return { level, resolution, scale, levelValue: "level" };
});
export async function run() {
await projection.load();
const srs3857 = new SpatialReference({ wkid: 3857 });
const srs4326 = new SpatialReference({ wkid: 4326 });
console.log({ lods });
const center = new Point(-9462156, 4300621, srs3857);
const extent = new Extent(
center.x - 1000,
center.y - 1000,
center.x + 1000,
center.y + 1000,
srs3857
);
// zoom levels are broken without specifying a basemap
const map = new Map("map", {
//basemap: "streets",
center, // does not set the map srs
extent, // does not set the map srs
lods: lods,
zoom: 14,
});
console.log(map.spatialReference); // undefined
map.spatialReference = srs3857; // has no affect?
new Scalebar({ map: map, scalebarUnit: "dual" });
const graphicsLayer = new GraphicsLayer();
map.addLayer(graphicsLayer);
// these do not render
[-85, -85.01, -84.99]
.map(
(x) => projection.project(new Point(x, 36.0, srs4326), srs3857) as Point
)
.forEach((p, i) =>
graphicsLayer.add(new Graphic(p, createMarker(i * 10), {}))
);
// these do render
[36, 36.01, 35.99]
.map(
(y) => projection.project(new Point(-85, y, srs4326), srs4326) as Point
)
.forEach((p, i) =>
graphicsLayer.add(new Graphic(p, createMarker(i * 10), {}))
);
}
I did find this one trick that sets the map up correctly but I would prefer a real solution:
// trick to get the map to work properly
map.on("load", () =>
setTimeout(() => map.removeLayer(map.getLayer(map.basemapLayerIds[0])), 0)
);

OrbitControls are not working react-native

I am trying to implement orbitControls in my react application. I have loaded 3d model GraphicsView
of expo-graphics. Model loaded perfectly, now i need to rotate the 3D model with screen drag. For
this i have added orbitControls, which is not working properly. Model did'nt rotate with screen
drag.Please help what i need to do to do to rotate 3D model in my react app.
Here is my model class.
```
import React from 'react';
import ExpoTHREE, { THREE } from 'expo-three';
import { GraphicsView } from 'expo-graphics';
import OrbitControls from 'three-orbitcontrols';
class Model extends React.Component {
componentDidMount() {
THREE.suppressExpoWarnings();
}
// 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(0x3d392f);
//this.scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );
this.camera = new THREE.PerspectiveCamera(12, width/height, 1, 1000);
this.camera.position.set(3, 3, 3);
this.camera.lookAt(0, 0, 0);
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.rotateSpeed = 2.0;
this.controls.zoomSpeed = 1.2;
this.controls.panSpeed = 0.8;
this.controls.noPan = false;
this.controls.staticMoving = false;
this.controls.dynamicDampingFactor = 0.3;
this.controls.keys = [ 65, 83, 68 ];
this.controls.addEventListener( 'change', this.onRender );
//clock = new THREE.Clock();
//this.scene.add(new THREE.AmbientLight(0xffffff));
var hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
hemiLight.position.set( 0, 20, 0 );
this.scene.add( hemiLight );
var dirLight = new THREE.DirectionalLight( 0xffffff );
dirLight.position.set( - 3, 10, - 10 );
dirLight.castShadow = true;
dirLight.shadow.camera.top = 2;
dirLight.shadow.camera.bottom = - 2;
dirLight.shadow.camera.left = - 2;
dirLight.shadow.camera.right = 2;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 40;
this.scene.add( dirLight );
await this.loadModel();
};
loadModel = async () => {
const obj = {
"f.obj": require('../assets/models/f.obj')
}
const model = await ExpoTHREE.loadAsync(
obj['f.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);
};
render() {
return (
<GraphicsView
onContextCreate={this.onContextCreate}
onRender={this.onRender}
onResize={this.onResize}
/>
);
}
}
export default Model;
```

How to use ThreeJS with VueJS

I want to use threeJS, so i install it with npm i three --save. I followed basic tutorial in threeJS documentation but i got an error.
mounted () {
this.initThree();
},
methods: {
initThree() {
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
this.renderer = new THREE.WebGLRenderer( { canvas: document.getElementById( "background" ), alpha: true, antialias: true } );
this.renderer.setSize( window.innerWidth, window.innerHeight );
let geometry = new THREE.BoxGeometry( 1, 1, 1 );
let material = new THREE.MeshBasicMaterial( { color: 0x0000ff } );
this.cube = new THREE.Mesh( geometry, material );
this.scene.add( this.cube );
this.camera.position.z = 5;
this.animate()
},
animate() {
this.cube.rotation.x += 0.01;
this.renderer.render( this.scene, this.camera );
requestAnimationFrame( this.animate() );
}
}
}
Your problem is not related to VueJS /ThreeJs, you should simply fix this line :
requestAnimationFrame( this.animate() );
It should be :
requestAnimationFrame(this.animate)
Please read more about callback : https://developer.mozilla.org/en-US/docs/Glossary/Callback_function
EDIT: You can try TroisJS, easy ThreeJS integration with VueJS : https://github.com/troisjs/trois

Dividing react-native Animated.Values

I want to produce an Animated.Value that is the result of dividing a constant by another Animated.Value.
const originalSize = 255;
const currentSize = this.props.someAnimatedValue;
// This method doesn't exist, but it's what I want:
const animatedScaleFactor = Animated.divide(originalSize, currentSize);
```
Since there is no Animated.divide() method, I only have at my disposal someAnimatedValue.interpolate(), which I can use to map an input range to an output range with a custom easing curve, Animated.multiply(), and Animated.modulus().
Any ideas?
UPDATE - use Animated.Divide instead.
Old answer:
Figured out how to get what I want f(x) = 1/x using an interpolator and custom easing function:
import React, {
Animated,
Component,
View
} from 'react-native';
export default class EasingTest extends Component {
state = {
input: new Animated.Value(0)
};
componentDidMount() {
const max = 937;
const output = this.state.input.interpolate({
inputRange: [0,max],
outputRange: [0,1/max],
easing: t => 1 / t
});
setInterval(() => {
const input = this.state.input.__getValue();
const expected = 1 / input;
const actual = output.__getValue();
const error = Math.abs(expected - actual) / max;
console.info(`f(${input}) = ${actual} (expected ${expected}, error ${Math.round(error * 100)}%`);
}, 200);
Animated.timing(this.state.input, {
toValue: max,
duration: 1000
}).start();
}
render() {
return <View />
}
}