I am using Angular 5.2.0 and three JS 0.91.0. I am trying to load a Collada file.
But I always get an error saying "THREE.ColladaLoader is not a constructor"
Please help.
Below is my TS code snippet:
import { Component, OnInit, ViewChild } from '#angular/core';
import * as THREE from 'three';
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
#ViewChild('collada') container;
renderer = new THREE.WebGLRenderer();
scene = null;
camera = null;
mesh = null;
clock = null;
self = null;
devicesData = [{
name: "Device One",
secure: true,
x_axis: '-24',
y_axis: '-67.00',
z_axis: '1111.40',
counter: '4.00',
device_time: '2017-11-16 13.05.53.988'
}, {
name: "Device Two",
secure: true,
x_axis: '12345.67',
y_axis: '1111.0',
z_axis: '1212.387',
counter: '4.00',
device_time: '2017-11-15 13.05.53.988'
}, {
name: "Device Three",
secure: false,
x_axis: '444.56',
y_axis: '22.00',
z_axis: '111.90',
counter: '5.00',
device_time: '2017-11-17 13.05.53.988'
}, {
name: "Device Four",
secure: true,
x_axis: '12345.67',
y_axis: '1111.0',
z_axis: '1212.387',
counter: '4.00',
device_time: '2017-11-18 13.05.53.988'
}]
constructor() { }
ngOnInit() {
}
loadColladaFile() {
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
this.camera.position.set(8, 10, 8);
this.camera.lookAt(new THREE.Vector3(0, 3, 0));
this.scene = new THREE.Scene();
this.clock = new THREE.Clock();
// loading manager
var loadingManager = new THREE.LoadingManager(function () {
this.scene.add(self);
});
// collada
var loader = new THREE.ColladaLoader();
loader.load('../assets/sphere.dae', function (collada) {
this.self = collada.scene;
});
var ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
this.scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(1, 1, 0).normalize();
this.scene.add(directionalLight);
//
this.renderer = new THREE.WebGLRenderer();
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(this.container.nativeElement.offsetWidth, 120);
this.mesh = new THREE.Mesh();
this.scene.add(this.mesh);
this.renderer.setSize(this.container.nativeElement.offsetWidth, 120);
this.renderer.domElement.style.display = "block";
this.renderer.domElement.style.margin = "auto";
this.container.nativeElement.appendChild(this.renderer.domElement);
this.animate();
}
ngAfterViewInit() {
this.loadColladaFile();
}
animate() {
window.requestAnimationFrame(() => this.animate());
this.mesh.rotation.x += 0.01;
this.mesh.rotation.y += 0.02;
this.renderer.render(this.scene, this.camera);
}
}
I have also tried adding three-collada-loader Dependency but the error is same.
I get the error here var loader = new THREE.ColladaLoader();
Thanx in Advance.
ColladaLoader is not part of the core library. You can find it in /examples/js/loaders/ColladaLoader.js.
I don't see a typings file for it in the three.js package. I did find one here, but I can't say whether it's up-to-date or will work for you.
EDIT:
The repository for three.js typings (linked above) has been deprecated. The last version to provide these typings was 0.93.31.
Related
I am unable to get the animation clips from the .glb file to play. The .glb model loads and is displayed on the screen but the animation does not play.
The animations play fine when viewing the object in the three.js editor.
The .glb has 1 animation clip associated with it.
Any Help would be much appreciated!!!
import {
Mesh,
AmbientLight,
Clock,
AnimationMixer,
PerspectiveCamera,
Scene,
WebGLRenderer,
MeshBasicMaterial,
RingGeometry,
sRGBEncoding
} from 'three';
import {ARButton} from 'three/examples/jsm/webxr/ARButton';
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
class App {
constructor() {
this.clock = new Clock();
this.camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
this.camera.position.set(0, 1.6, 3);
this.scene = new Scene();
this.renderer = new WebGLRenderer({
antialias: true,
alpha: true
});
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.outputEncoding = sRGBEncoding;
document.body.appendChild(this.renderer.domElement);
this.initXR();
this.initScene();
window.addEventListener('resize', this.onWindowResize.bind(this), false);
this.renderer.setAnimationLoop(this.render.bind(this));
}
initXR() {
this.renderer.xr.enabled = true;
document.body.appendChild(ARButton.createButton(this.renderer, {requiredFeatures: ['hit-test']}));
this.hitTestSourceRequested = false;
this.hitTestSource = null;
this.controller = this.renderer.xr.getController(0);
this.controller.addEventListener('select', this.onSelect.bind(this));
}
initScene() {
let geometry = new RingGeometry(0.08, 0.10, 32).rotateX(-Math.PI / 2);
let material = new MeshBasicMaterial();
this.reticle = new Mesh(geometry, material);
this.reticle.matrixAutoUpdate = false;
this.reticle.visible = false;
this.scene.add(this.reticle);
const loader = new GLTFLoader();
loader.load('./models/stylized_character.glb', gltf => {
this.myObj = gltf;
gltf.scene.scale.set(.25, .25, .25);
gltf.scene.visible = true;
this.scene.add(gltf.scene);
//todo: this doesnt seem to work
this.mixer = new AnimationMixer(gltf);
var action = this.mixer.clipAction(gltf.animations[0]);
action.play();
});
var aLight = new AmbientLight(0xffffff, 1);
this.scene.add(aLight)
}
render(_, frame) {
const delta = this.clock.getDelta();
if (this.mixer) {
this.mixer.update(delta);
}
if (frame) {
if (this.hitTestSourceRequested === false) {
this.requestHitTestSource();
}
if (this.hitTestSource) {
this.getHitTestResults(frame);
}
}
this.renderer.render(this.scene, this.camera);
}
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.render(this.scene, this.camera);
}
onSelect() {
if (this.reticle.visible) {
this.myObj.scene.position.setFromMatrixPosition(this.reticle.matrix);
this.myObj.scene.visible = true;
}
}
async requestHitTestSource() {
const session = this.renderer.xr.getSession();
session.addEventListener('end', () => {
this.hitTestSourceRequested = false;
this.hitTestSource = null;
});
const referenceSpace = await session.requestReferenceSpace('viewer');
this.hitTestSource = await session.requestHitTestSource({space: referenceSpace, entityTypes: ['plane']});
this.hitTestSourceRequested = true;
}
getHitTestResults(frame) {
const hitTestResults = frame.getHitTestResults(this.hitTestSource);
if (hitTestResults.length) {
const hit = hitTestResults[0];
const pose = hit.getPose(this.renderer.xr.getReferenceSpace());
this.reticle.visible = true;
this.reticle.matrix.fromArray(pose.transform.matrix);
} else {
this.reticle.visible = false;
}
}
}
window.addEventListener('DOMContentLoaded', () => {
new App();
});
When importing a .gltf file into threejs (in expo), I get the following error:
Error: Event {
"isTrusted": false,
}
I am using the GLTFLoader from three(/examples/jsm/loaders/GLTFLoader.js) in an expo react native project with the following code:
loader.load(
"../body.gltf",
(gltf) => {
console.log("Object: ", gltf);
scene.add(gltf.scene);
},
(progress) => console.log("Progress: ", progress),
(err) => console.log("Error: ", err)
);
Is there something in my permissions I am not aware of, is it something in expo or something else?
Loading assets:
useEffect(() => {
(async () => {
const assets = [
Asset.fromModule(require("./assets/abductors.gltf")),
Asset.fromModule(require("./assets/abs.gltf")),
Asset.fromModule(require("./assets/adductors.gltf")),
Asset.fromModule(require("./assets/biceps.gltf")),
Asset.fromModule(require("./assets/bracheoradialis.gltf")),
Asset.fromModule(require("./assets/calves.gltf")),
Asset.fromModule(require("./assets/chest.gltf")),
Asset.fromModule(require("./assets/feet.gltf")),
Asset.fromModule(require("./assets/flexors.gltf")),
Asset.fromModule(require("./assets/forearms.gltf")),
Asset.fromModule(require("./assets/glutes.gltf")),
Asset.fromModule(require("./assets/hamstrings.gltf")),
Asset.fromModule(require("./assets/hands.gltf")),
Asset.fromModule(require("./assets/head.gltf")),
Asset.fromModule(require("./assets/lats.gltf")),
Asset.fromModule(require("./assets/obliques.gltf")),
Asset.fromModule(require("./assets/pelvic.gltf")),
Asset.fromModule(require("./assets/quads.gltf")),
Asset.fromModule(require("./assets/rotators.gltf")),
Asset.fromModule(require("./assets/serratus.gltf")),
Asset.fromModule(require("./assets/shoulders.gltf")),
Asset.fromModule(require("./assets/tibalis.gltf")),
Asset.fromModule(require("./assets/transverse.gltf")),
Asset.fromModule(require("./assets/traps.gltf")),
Asset.fromModule(require("./assets/triceps.gltf"))
];
let body = new THREE.Scene();
for (let i = 0; i < assets.length; i++) {
await assets[i].downloadAsync();
const loader = new GLTFLoader();
loader.load(
assets[i].uri || "",
(gltf) => {
assets[i] = gltf.scene;
body.add(gltf.scene);
},
(xhr) => {
console.log(`${(xhr.loaded / xhr.total) * 100}% loaded`);
},
(error) => {
console.error("An error happened", error);
}
);
}
setBody(body);
})();
}, []);
Showing them:
const _onContextCreate = async (gl) => {
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
let renderer = new ExpoTHREE.Renderer({ gl });
renderer.setSize(width, height);
renderer.setClearColor(0xffffff);
let camera = new THREE.PerspectiveCamera(80, width / height, 0.01, 1000);
camera.position.z = 5;
let scene = new THREE.Scene();
const light = new THREE.PointLight(0xffffff, 1, 500);
light.position.set(5, 10, 10);
scene.add(light);
scene.add(body);
body.position.set(0, -2, -5);
body.scale.set(1.2, 1.2, 1.2);
body.rotation.set(0, 0, 0);
[...]
};
return (
<Model
_onContextCreate={_onContextCreate}
body={body}
setActiveMuscles={setActiveMuscles}
onChangeStroke={onChangeStroke}
/>
);
I'm using SVGloader to load an SVG so I can map it on my OBJ file. But when gave it url to the svg file it generates an error
TypeError:Cannot set property 'getStrokeStyle' of undefined
I'm using Angular 8 and rendering a Obj file using THREE.js. I want to load an svg and map it on the obj file to add texture to that file, but as I told above it is generating an error and I don't know how to solve it.
Here is code file.
import { Component, AfterViewInit, ViewChild, Input, ElementRef } from '#angular/core';
import * as THREE from 'three';
import { OrbitControls } from '#avatsaev/three-orbitcontrols-ts';
import {OBJLoader} from 'three-obj-mtl-loader';
import {SVGLoader} from 'three-svg-loader';
#Component({
selector: 'app-scene',
templateUrl: './scene.component.html',
styleUrls: ['./scene.component.css']
})
export class SceneComponent implements AfterViewInit {
#Input() name: string;
#ViewChild('canvas', {static:true}) canvasRef: ElementRef;
renderer = new THREE.WebGLRenderer;
scene = null;
camera = null;
controls = null;
mesh = null;
light = null;
loader;
svgLoader;
private calculateAspectRatio(): number {
const height = this.canvas.clientHeight;
if (height === 0) {
return 0;
}
return this.canvas.clientWidth / this.canvas.clientHeight;
}
private get canvas(): HTMLCanvasElement {
return this.canvasRef.nativeElement;
}
constructor() {
// this.loader = new OBJLoader();
this.scene = new THREE.Scene();
this.loader = new OBJLoader();
this.svgLoader = new SVGLoader();
this.camera = new THREE.PerspectiveCamera(15, window.innerWidth / window.innerHeight, 0.1, 1000)
}
ngAfterViewInit() {
this.configScene();
this.configCamera();
this.configRenderer();
this.configControls();
this.createLight();
this.createMesh();
this.animate();
}
configScene() {
// this.scene.background = new THREE.Color( 0xdddddd );
}
configCamera() {
this.camera.aspect = this.calculateAspectRatio();
this.camera.updateProjectionMatrix();
this.camera.position.set( 0, 0, 3 );
this.camera.lookAt( this.scene.position );
}
configRenderer() {
this.renderer = new THREE.WebGLRenderer({
canvas: this.canvas,
antialias: true,
alpha: true
});
this.renderer.setPixelRatio(devicePixelRatio);
// setClearColor for transparent background
// i.e. scene or canvas background shows through
this.renderer.setClearColor( 0x000000, 0 );
this.renderer.setSize((window.innerWidth/2), (window.innerHeight/2));
window.addEventListener('resize', ()=>{
this.renderer.setSize((window.innerWidth/2), (window.innerHeight)/2);
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
})
console.log('clientWidth', this.canvas.clientWidth);
console.log('clientHeight', this.canvas.clientHeight);
}
configControls() {
this.controls = new OrbitControls(this.camera);
this.controls.autoRotate = false;
this.controls.enableZoom = false;
// this.controls.maxDistance = 5;
// this.controls.minDistance = 10;
this.controls.enablePan = false;
this.controls.update();
}
createLight() {
this.light = new THREE.PointLight( 0xffffff );
this.light.position.set( -10, 10, 10 );
this.scene.add( this.light );
}
createMesh() {
this.svgLoader.load('../../../../assets/abc.svg')
console.log("SVG Loader", this.svgLoader)
this.loader.load('../../../../assets/nonunified.obj', (object)=>{
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.geometry.center();
}
} );
this.scene.add(object)
},
// 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( 'An error happened' );
}
)}
animate() {
window.requestAnimationFrame(() => this.animate());
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
}
The SVGLoader is not for loading svg-files to be used as textures but for loading them as Geometry: https://threejs.org/docs/#examples/en/loaders/SVGLoader
If you want to use an svg-file as a texture, you should be able to use the TextureLoader like this:
obj.material.map = new TextureLoader().load('../../../../assets/abc.svg');
I'm not sure if you actually need to rasterize it to a canvas first, if the above doesn't work, try what is described here: How do you load and display SVG graphics in three.js?
I want to develop a web app to entering measurements of a man and displaying a 3d model with these measurements. I have chosen three.js to start it. And I downloaded a 3d model named standard-male-figure from clara.io. Here is my code to display human model.
import React, { Component } from "react";
import PropTypes from "prop-types";
import withStyles from "#material-ui/core/styles/withStyles";
import * as THREE from "three-full";
const styles = (/*theme*/) => ({
});
class ThreeDView extends Component {
constructor(props){
super(props);
this.start = this.start.bind(this);
this.stop = this.stop.bind(this);
this.renderScene - this.renderScene.bind(this);
this.animate = this.animate.bind(this);
}
componentDidMount() {
const width = this.mount.clientWidth;
const height = this.mount.clientHeight;
//ADD SCENE
this.scene = new THREE.Scene();
//ADD CAMERA
this.camera = new THREE.PerspectiveCamera(100,1);
this.camera.position.z = 12;
this.camera.position.y = 0;
this.camera.position.x = 0;
//ADD RENDERER
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setClearColor("#f0f0f0");
this.renderer.setSize(width, height);
this.mount.appendChild(this.renderer.domElement);
// MOUSE ROTATION
this.orbit = new THREE.OrbitControls(this.camera,this.renderer.domElement);
this.orbit.update();
//ADD LIGHTS
this.light = new THREE.PointLight(0xffffff,1.3);
this.light.position.z = 10;
this.light.position.y=20;
this.scene.add(this.light);
// ADD MAN FIGURE
const loader = new THREE.ColladaLoader();
loader.load("/models/standard-male-figure.dae",(manFigure)=>{
this.man = manFigure;
this.man.name = "man-figure";
this.man.scene.position.y = -10;
this.scene.add(this.man.scene);
},undefined,()=>alert("Loading failed"));
this.start();
}
componentWillUnmount() {
this.stop();
this.mount.removeChild(this.renderer.domElement);
}
start() {
if (!this.frameId) {
this.frameId = requestAnimationFrame(this.animate);
}
}
stop () {
cancelAnimationFrame(this.frameId);
}
animate () {
this.renderScene();
this.frameId = window.requestAnimationFrame(this.animate);
}
renderScene () {
this.orbit.update();
this.light.position.z = this.camera.position.z;
this.light.position.y=this.camera.position.y+20;
this.light.position.x=this.camera.position.x;
this.renderer.render(this.scene, this.camera);
}
render() {
return (
<div style={{ height: "640px" }} ref={(mount) => { this.mount = mount; }} >
</div>
);
}
}
ThreeDView.propTypes = {
values: PropTypes.object
};
/*
all values in inches
values = {
heightOfHand:10,
, etc..
}
*/
export default withStyles(styles)(ThreeDView);
values is measurements that user is entering. I have no idea about how to start updating 3d model with these measurements. Please give me a starting point or any advise to complete this. Thank You!.
Firstly you can get the current size and scale of your man
const box = new THREE.Box3().setFromObject(this.man)
const currentObjectSize = box.getSize()
const currentObjectScale = this.man.scale
and when an user update the size value (newSize), you can calculate a new scale for you man
const newScale = new THREE.Vector3(
currentObjectScale.x * newSize.x/currentObjectSize.x,
currentObjectScale.y * newSize.y/currentObjectSize.y,
currentObjectScale.z * newSize.z/currentObjectSize.z
)
and update your man with this scale
this.man.scale.set(newScale.x, newScale.y, newScale.z)
I used the react-vr init App to spin up a sample project and had no problem generating spheres throughout my scene using:
{
data.map(node => return <Sphere key={`node-${node.id}`} radius={1} widthSegments={8} heightSegments={8} lit={true}
style={{color: "red", transform: [{translate: [node.x, node.y, node.z]}]
}}/>
}
But now I am looking to connect the Spheres via a simple Line geometry. I tried doing the below to create the line, but I don't know how to add to the scene. In plain three.js code I would simply scene.add(), but I'm not sure how that works in react-vr.
import {
LineBasicMaterial,
Line,
Geometry,
Vector3
} from 'three';
const lineMaterial = new LineBasicMaterial({color: 0xf0f0f0, transparent: true});
lineMaterial.opacity = .75;
const line = new Line(new Geometry(), lineMaterial);
line.geometry.vertices = [new Vector3(0, 0, 0), new Vector3(0, 0, 0)];
line.renderOrder = 10;
H/T to #cidicles and the react-vr docs! I create a new SceneGroup module in which it takes a three.js Group as an initializing variable and then when I'm ready in ./index.vr.js I call SceneGroupModule.drawLine(); to actually draw the Line and add to the scene.
./client.js
import {VRInstance} from 'react-vr-web';
import {
Scene,
Group
} from 'three';
function init(bundle, parent, options) {
const scene = new Scene();
const sceneGroupModule = new SceneGroupModule();
const vr = new VRInstance(bundle, 'App', parent, {
nativeModules: [sceneGroupModule],
scene: scene,
});
const group = new Group();
scene.add(group);
sceneGroupModule.init(group);
vr.render = () => {};
// Begin the animation loop
vr.start();
return vr;
}
window.ReactVR = {init};
export default class SceneGroupModule extends Module {
constructor() {
super('SceneGroupModule');
}
// Called directly after the module is created.
init(sceneGroup) {
this.sceneGroup = sceneGroup;
}
drawLine() {
const lineMaterial = new LineBasicMaterial({color: 0xf0f0f0, transparent: true});
lineMaterial.opacity = .75;
const line = new Line(new Geometry(), lineMaterial);
line.geometry.vertices = [new Vector3(0, 0, 0), new Vector3(0, 0, 0)];
line.geometry.vertices = [
new Vector3(10, 2, 5),
new Vector3(11, 3, 4)
];
line.geometry.verticesNeedUpdate = true;
this.sceneGroup.add(line);
}
}
./index.vr.js
import React from 'react';
import {
AppRegistry,
View,
NativeModules
} from 'react-vr';
// Native Module defined in vr/client.js
const SceneGroupModule = NativeModules.SceneGroupModule;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
SceneGroupModule.drawLine();
}
render() {
return (
<View
style={{
transform: [{translate: [0, 0, -3]}],
layoutOrigin: [0.5, 0, 0],
alignItems: 'center',
}}
>
</View>
);
}
}
AppRegistry.registerComponent('App', () => App);