THREE.DragControls is not a constructor error - javascript

I am trying to use Three.DragControls constructor for my application in three.js.
I run npm install and npm install three-dragcontrols, receiving the message the dependency was added to the package.
When i am trying to use new Three.DragControls i receive the error:
"THREE.DragControls is not a constructor"
The code:
var scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf0f0f0 );
var camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
var objects = [];
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera.position.z = 1000;
var startColor;
function init() {
scene.add( new THREE.AmbientLight( 0x0f0f0f ) );
var light = new THREE.SpotLight( 0xffffff, 1.5 );
light.position.set( 0, 500, 2000 );
scene.add(light);
var geometry = new THREE.BoxGeometry( 40, 40, 40 );
var geometry = new THREE.SphereGeometry( 40, 40, 40 );
for (var i = 0; i < 100; i++) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
object.position.x = Math.random() * 1000 - 500;
object.position.y = Math.random() * 600 - 300;
object.position.z = Math.random() * 800 - 400;
object.castShadow = true;
object.receiveShadow = true;
scene.add( object );
objects.push( object );
}
var controls = new THREE.DragControls( objects, camera, renderer.domElement );
controls.addEventListener( 'dragstart', dragStartCallback );
controls.addEventListener( 'dragend', dragendCallback );
}
function dragStartCallback(event) {
startColor = event.object.material.color.getHex();
event.object.material.color.setHex(0x000000);
}
function dragendCallback(event) {
event.object.material.color.setHex(startColor);
}
function animate() {
requestAnimationFrame( animate );
renderer.render(scene, camera);
};
init();
animate();

Possible answer (can't post this as a comment because of SO's stupid rules):
Please share the part of your code in which you import/require the module. In many cases with these modules for Three.js you have to attach them to the THREE object.
For example:
// Doesn't work:
THREE.DragControls = require("three-drag-controls");
// Does work:
THREE.DragControls = require("three-drag-controls")(THREE);
This is because the module is in fact a function which returns the constructor for the controls. If you don't run the function first with THREE as an argument, you get "THREE.DragControls is not a constructor".

For those coming here while trying to follow the 'Three.js Drag and Drop Tutorial' and get stuck with the above error message, the solution is to install the Drag Controls module and follow the usage instructions on the repo (https://www.npmjs.com/package/drag-controls), namely changing the imports to:
import * as THREE from 'three';
import DragControls from 'drag-controls'
DragControls.install({THREE: THREE})
And then creating the DragControls instance this way:
var controls = new DragControls( objects, camera, renderer.domElement );
Things should work after that.
Hope this saved some of you some time!

Related

Three.js: Drawing points

For a web project, I would like to draw random points with Three.js.
This is my code so far:
<script type="module">
import * as THREE from 'https://threejs.org/build/three.module.js';
import { TrackballControls } from 'https://threejs.org/examples/jsm/controls/TrackballControls.js';
let camera, scene, renderer, controls;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xffffff );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 0, 500 );
controls = new TrackballControls( camera, renderer.domElement );
controls.minDistance = 200;
controls.maxDistance = 500;
scene.add( new THREE.AmbientLight( 0x222222 ) );
const light = new THREE.PointLight( 0xffffff );
light.position.copy( camera.position );
scene.add( light );
//
//
const randomPoints = [];
for ( let i = 0; i < 10; i ++ ) {
randomPoints.push( new THREE.Vector3( ( i - 4.5 ) * 50, THREE.MathUtils.randFloat( - 50, 50 ), THREE.MathUtils.randFloat( - 50, 50 ) ) );
}
const randomSpline = new THREE.CatmullRomCurve3( randomPoints );
//
const extrudeSettings2 = {
steps: 120,
bevelEnabled: false,
extrudePath: randomSpline
};
const pts2 = [], numPts = 5;
for ( let i = 0; i < numPts * 2; i ++ ) {
const l = i % 2 == 1 ? 10 : 10;
const a = i / numPts * Math.PI;
pts2.push( new THREE.Vector2( Math.cos( a ) * l, Math.sin( a ) * l ) );
}
const shape2 = new THREE.Shape( pts2 );
const geometry2 = new THREE.ExtrudeGeometry( shape2, extrudeSettings2 );
const material2 = new THREE.MeshLambertMaterial( { color: 0xff8000, wireframe: false } );
const mesh2 = new THREE.Mesh( geometry2, material2 );
scene.add( mesh2 );
//
const materials = [ material2 ];
const extrudeSettings3 = {
depth: 40,
steps: 1,
bevelEnabled: true,
bevelThickness: 2,
bevelSize: 4,
bevelSegments: 1
};
const geometry3 = new THREE.ExtrudeGeometry( shape2, extrudeSettings3 );
const mesh3 = new THREE.Mesh( geometry3 );
mesh3.position.set( 150, 100, 0 );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
}
</script>
Currently, everything is based on splines. The result should not be based on extrusion, but on random points. I don't really know how to get random points. Is there a special function for it? Or can another function be used for it?
I would be veeeery thankful for help! :)
I think a good place to start with is using ConvexGeometry. You give it an array of points / Vector3 ( which I see you have created under the variable randomPoints ) as a parameter and it will create a shape for you.
I see you used CatmullRomCurve3, this may be a good tool to create the curves between the points as you mentioned. We can combine both of these ideas to create a somewhat curvier model.
const divisions = 25; // The amount of divisions between points
const catmullPoints = new THREE.CatmullRomCurve3( randomPoints, true, "catmullrom", 0.5 ).getPoints(divisions);
const geometryConvex = new ConvexGeometry( randomPoints );
So now you have a geometry with a somewhat randomized shape. The thing about it is that it will look a bit more "geometrical" than the example shapes you provided. So what you can try to do is divide your randomPoints to chunks, i.e multiple sub-arrays, and do a similar approach as above, basically saving the created geometries to a separate array, let's call it geometries, and then you can use mergeBufferGeometries to create a single geometry out of these geometries, this will give a more abstract looking shape. The code:
const size = THREE.MathUtils.randInt(2,10); // the number of sub arrays can be another parameter to randomize
const pointsChunk = chunk([...randomPoints], size); // you can use lodash or other chunk algorithms found online
const geometries = [];
for ( let i = 0 ; i < pointsChunk.length ; i++) {
const divisions = 25;
const catmullPoints = new THREE.CatmullRomCurve3( pointsChunk[i], true, "catmullrom", 0.5 ).getPoints( divisions );
geometries.push(new ConvexGeometry( catmullPoints ));
}
const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries( geometries );
There may be more ways to go about it, but I believe ConvexGeometry is definitely a good place to start.
Here is a fiddle of my attempt : https://jsfiddle.net/9rc503tn/2

THREE.OrbitControls is not a constructor

I'm trying to add OrbitControls in my three.js project, but when I try to call the constructor it gives an uncaught type error "THREE.OrbitControls is not a constructor". I have searched for a solution, but the only thing I can find is explicitly importing the file. But when I do, it still gives the error.
This is my code:
<script src="JS/three.js"></script>
<scirpt src="JS/OrbitControls.js"></scirpt>
<script src="JS/Models/ModelLoader.js"></script>
<script>
var scene = new THREE.Scene( );
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 10000 );
var renderer = new THREE.WebGLRenderer( );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera.position.z = 3;
var controls = new THREE.OrbitControls(camera, renderer.domElement);//here is the problem
var pointLight = new THREE.PointLight(0,2);
pointLight.position.z = 2;
scene.add(pointLight);
// Game Logic
var update = function( ){
}
// Draw Scene
var render = function( ){
renderer.render( scene, camera );
}
var testLoad = function(){
var modelLoader = new ModelLoader();
var obj = modelLoader.loadObj("/Res/Models/Characters/char1.obj", "/Res/Models/Characters/char1.mtl");
scene.add(obj);
}
// Main Game Loop(update, render, repeat)
var gameLoop = function( ){
requestAnimationFrame( gameLoop );
update( );
render( );
}
gameLoop();
The ModelLoader is a custom class for loading obj files.
Anyone that knows what I've done wrong? Thanks in advance!
You have a typo:
<scirpt src="JS/OrbitControls.js"></scirpt>
should be
<script src="JS/OrbitControls.js"></script>

ReferenceError: THREE is not defined in a Rails Project with the threejs-rails gem

I am working in a ruby on rails project with three.js. I installed the corresponding gem and it seems to work just fine. But somehow the javascript still throws the following Error:
Uncaught ReferenceError: THREE is not defined
on the Line:
renderer = new THREE.WebGLRenderer();
The weird thing is that my program seems to be working. My object gets displayed.
my javscript file looks like this:
// set size to the size of the containing box
var box = document.getElementById('player');
if(box){
var boxSize = box.clientWidth;
}
var a = 0.05;
// set some camera attributes
var VIEW_ANGLE = 45,
ASPECT = 1,
NEAR = 0.1,
FAR = 10000;
var camera, scene, renderer;
var $player;
var char, materialChar ;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer();
camera = new THREE.PerspectiveCamera( 75, 1, 0.1, 10000 );
camera.position.y = 10;
camera.position.z = 20;
scene = new THREE.Scene();
// get the DOM element to attach to
// - assume we've got jQuery to hand
$player = $('#player');
// attach the render-supplied DOM element
$player.append(renderer.domElement);
// create the character
initChar();
// create a point light
addLight();
scene.add(camera);
// draw!
renderer.setSize(boxSize, boxSize);
renderer.render(scene, camera);
renderer.shadowMap.enabled = true;
document.getElementById("player").addEventListener("click", zoom);
}
function addLight(){
var dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(100, 100, 50);
scene.add(dirLight);
var ambLight = new THREE.AmbientLight(0x404040);
scene.add(ambLight);
var bluePoint = new THREE.PointLight(0x0033ff, 3, 150);
bluePoint.position.set( 70, 5, 70 );
scene.add(bluePoint);
scene.add(new THREE.PointLightHelper(bluePoint, 3));
var greenPoint = new THREE.PointLight(0x33ff00, 1, 150);
greenPoint.position.set( -70, 5, 70 );
scene.add(greenPoint);
scene.add(new THREE.PointLightHelper(greenPoint, 3));
}
function initChar(){
var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load('<%= asset_path 'grey.jpg' %>');
materialChar = new THREE.MeshBasicMaterial({
map: texture
});
var loader = new THREE.JSONLoader();
loader.load('<%= asset_path 'standard-male-figure2.json' %>', function(geometry){
char = new THREE.Mesh( geometry, materialChar );
scene.add(char);
});
}
function animate(){
requestAnimationFrame( animate );
render();
}
function render(){
//char.rotation.y += 0.05;
renderer.render( scene, camera );
}
function zoom(){
camera.position.z = camera.position.z - 10;
}
I have similar problem in Rails 4.2. I think the default jquery / Ajax gem is missing the following component. I have it linked in my erb which solved the problem. It can also be configured to precompile by using asset pipeline if you choose to.
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>

Loading PLY files without faces - Three.js

I'm working on a Web Service which main funcionality resolves around displaying binary .ply files. To open and visualize the point cloud I'm using PLYLoader from Three.js. I used it to present example files from the internet and it worked. But when I'm trying to attach my .ply file it doesn't. I'm using Three.js because I need my display to be optimal, my files are at least 70mb.
Example .ply file here:
https://onedrive.live.com/redir?resid=7DDB287362EDFD5A!761&authkey=!AOHxkZk-6etH--0&ithint=file%2cply
I noticed that the .ply files that i have are a bit different from .ply files i displayed before. In my files there are no faces only points.
So basically my question is "Is it possible to display point cloud using Three.js? or should I use other library? If this is possible can someone help me with configuring my code to work properly?
Here it is my script in which I'm supporting .ply files:
var cameraControls;
var container = document.getElementById("three");
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, container.offsetWidth / container.offsetHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( container.offsetWidth, container.offsetHeight );
document.getElementById("three").appendChild( renderer.domElement );
cameraControls = new THREE.OrbitControls( camera, renderer.domElement );
cameraControls.target.set( 0, 0, 0 );
cameraControls.addEventListener( 'change', render );
var geometry = new THREE.BoxGeometry( 3, 3, 3 );
var material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
var loader = new THREE.PLYLoader();
loader.load( 'resources/cube.ply', function ( geometry ) {
geometry.computeFaceNormals();
var material = new THREE.MeshStandardMaterial( { color: 0x0055ff } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.y = - 0.25;
mesh.rotation.x = - Math.PI / 2;
mesh.scale.multiplyScalar( 1 );
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add( mesh );
} );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
var light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );
directionalLight.position.set( 0, 1, 0 );
scene.add( directionalLight );
camera.position.z = 5;
function render() {
requestAnimationFrame( render );
renderer.render( scene, camera );
}
render();
Use THREE.MeshNormalMaterial. It loads the textures with MeshNormalMaterial for some odd reason
Use PointMaterials and Points
const loader = new PLYLoader();
loader.load('00000012.ply', function (geometry) {
var material = new THREE.PointsMaterial( { size: 0.005 } );
material.vertexColors = true //if has colors
var mesh = new THREE.Points(geometry, material)
scene.add(mesh)
} );

Trying to implement WebGL viewer using Three.js + ColladaLoader.js

I am trying to implement a WebGl viewer using three.js + colladaloader.js but i am having some problems when trying to import and view my own collada object. I can load the example correctly, but when adding my own model to it (without changing the code) i get a
Can not convert Transform of type lookat
WebGLRenderingContext: GL ERROR :GL_INVALID_OPERATION :
glDrawElements: attempt to access out of range vertices in attribute 0
Any ideas how to get my own model working
I should add that my ColladaLoader.js is a patched version hosted here https://raw.github.com/jihoonl/three.js/6e5a02427f2b9626a3fccc9c09d8654cc02d2109/examples/js/loaders/ColladaLoader.js
Here is the model i am trying to load: http://sketchup.google.com/3dwarehouse/details?mid=bad38a0b2a3d753c8857d6b1c783b210&ct=mdsa&prevstart=0
Here is my code:
<script>
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats;
var camera, scene, renderer, objects;
var particleLight, pointLight;
var dae, skin;
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load( '/site_media/models/model.dae', function ( collada ) {
dae = collada.scene;
skin = collada.skins[ 0 ];
dae.scale.x = dae.scale.y = dae.scale.z = 0.002;
dae.updateMatrix();
init();
animate();
} );
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set( 2, 2, 3 );
scene = new THREE.Scene();
// Grid
var size = 14, step = 1;
var geometry = new THREE.Geometry();
var material = new THREE.LineBasicMaterial( { color: 0x303030 } );
for ( var i = - size; i <= size; i += step ) {
geometry.vertices.push( new THREE.Vector3( - size, - 0.04, i ) );
geometry.vertices.push( new THREE.Vector3( size, - 0.04, i ) );
geometry.vertices.push( new THREE.Vector3( i, - 0.04, - size ) );
geometry.vertices.push( new THREE.Vector3( i, - 0.04, size ) );
}
var line = new THREE.Line( geometry, material, THREE.LinePieces );
scene.add( line );
// Add the COLLADA
scene.add( dae );
particleLight = new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
scene.add( particleLight );
// Lights
scene.add( new THREE.AmbientLight( 0xcccccc ) );
var directionalLight = new THREE.DirectionalLight(/*Math.random() * 0xffffff*/0xeeeeee );
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add( directionalLight );
pointLight = new THREE.PointLight( 0xffffff, 4 );
pointLight.position = particleLight.position;
scene.add( pointLight );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
var t = 0;
var clock = new THREE.Clock();
function animate() {
var delta = clock.getDelta();
requestAnimationFrame( animate );
if ( t > 1 ) t = 0;
if ( skin ) {
// guess this can be done smarter...
// (Indeed, there are way more frames than needed and interpolation is not used at all
// could be something like - one morph per each skinning pose keyframe, or even less,
// animation could be resampled, morphing interpolation handles sparse keyframes quite well.
// Simple animation cycles like this look ok with 10-15 frames instead of 100 ;)
for ( var i = 0; i < skin.morphTargetInfluences.length; i++ ) {
skin.morphTargetInfluences[ i ] = 0;
}
skin.morphTargetInfluences[ Math.floor( t * 30 ) ] = 1;
t += delta;
}
render();
stats.update();
}
function render() {
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos( timer ) * 10;
camera.position.y = 2;
camera.position.z = Math.sin( timer ) * 10;
camera.lookAt( scene.position );
particleLight.position.x = Math.sin( timer * 4 ) * 3009;
particleLight.position.y = Math.cos( timer * 5 ) * 4000;
particleLight.position.z = Math.cos( timer * 4 ) * 3009;
renderer.render( scene, camera );
}
</script>
fixed it by making my texture BIGGER (and placing it on the same folder as my model and renaming to the same name as my model)
It seems like ColladaLoader.js does not load small textures for some reason
Another thing there is a bug https://github.com/mrdoob/three.js/issues/3106 but this is the fix https://raw.github.com/jihoonl/three.js/6e5a02427f2b9626a3fccc9c09d8654cc02d2109/examples/js/loaders/ColladaLoader.js , and it seems that it doesnt work without this fix

Categories