Javascript/Three.js one million iterations loop performance - javascript

I have this javascript code that i need to run once for generating a grid of quadrants from a 3d object imported from blender (it is a three.js project).
The problem is that it runs a loop of about 1 million iterations and happens that the script takes a long time and then crashes.
To be precise, there are 10,000 iterations in which, each time, a mesh is generated and then a further loop of 121 raycaster.intersectObjects is executed.
What I would like to know if this script is really so demanding for an Intel i7-7800x CPU or if the problem depends on something else, such as browser memory or setting (I tested with all the browsers).
var scene= new THREE.Scene();
renderer= new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor (0xf9f9f9, 1);
renderer.gammaOutput= true;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled= true;
renderer.shadowMap.type= THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
var light= new THREE.DirectionalLight(0xffffff, 1.4, 100);
light.position.set(3, 2, 0);
light.castShadow= true;
scene.add(light);
light.shadow.mapSize.width= 512;
light.shadow.mapSize.height= 512;
light.shadow.camera.near= 0.5;
light.shadow.camera.far= 500;
var camera= new THREE.PerspectiveCamera(75,
window.innerWidth/window.innerHeight, 0.1, 100000);
camera.position.set(0, 70, 70);
camera.lookAt(0, 0, 0);
var controls= new THREE.OrbitControls(camera);
var loader= new THREE.GLTFLoader();
loader.load(
'http://localhost/planegeometryeditor/meshes/map/map_def.gltf',
function(gltf) {
gltf.scene.receiveShadow= true;
scene.add(gltf.scene);
scene.children[1].rotation.y= -(Math.PI/2);
var sceneobjs= scene.children[1].children;
for(var iobj in sceneobjs) {
var obj= sceneobjs[iobj];
obj.updateMatrixWorld();
if(obj.name== 'terrain') {
var terrain= obj;
for(var tmindex in terrain.children[0].children) {
terrain.children[0].children[tmindex].material.side=
THREE.DoubleSide;
}
}
if(obj.name.search('tree')!= -1) { obj.name= 'tree' }
}
var terrain_bbox = new THREE.Box3().setFromObject(terrain);
var terrain_position= terrain.position;
//------------ LOOP -----------------------
//-----------------------------------------
var idquad= 0;
for(var i= -49.5; i<=49.5; i= i+1) {
for(var ii= -49.5; ii<=49.5; ii= ii+1) {
var idquad= idquad+1;
var quadgeo= new THREE.PlaneGeometry(1,1);
var quadmat= new
THREE.MeshBasicMaterial({color:0x00ff00, side:
THREE.DoubleSide});
var quad= new THREE.Mesh(quadgeo, quadmat);
quad.name= 'quad-'+idquad;
scene.add(quad);
quad.rotation.x= -(Math.PI/2);
quad.position.x= i;
quad.position.y= -5;
quad.position.z= ii;
for(var a= (i-0.5); a<=(i+0.5); a=
Number((a+0.1).toFixed(2))) {
for(var aa= (ii-0.5); aa<=(ii+0.5); aa=
Number((aa+0.1).toFixed(2))) {
var raycaster= new THREE.Raycaster();
var rorigin= new THREE.Vector3(a, -5, aa);
var rdirection= new THREE.Vector3(0, 1, 0);
raycaster.set(rorigin, rdirection);
var intersects=
raycaster.intersectObjects(scene.children,
true);
}
}
}
}
//------------ LOOP -----------------------
//-----------------------------------------
}, // success
undefined,
function (error) { console.error(error); }
);
render();

Related

How to save decal position with Three.js?

I'm looking for a way to store and reload the decals that produced in This example ( source-code), so when a user "paints" a new splatter - he could save it and later on - reloads it.
I've tried many ways but unfortunately none of them seems to work.
My approach was to save the position, rotation and scale and then build them like this :
function loadLeePerrySmith(callback) {
var loader = new THREE.JSONLoader();
loader.load('assets/LeePerrySmith.js', function (geometry) {
geometry.verticesNeedUpdate = true;
geometry.elementsNeedUpdate = true;
geometry.morphTargetsNeedUpdate = true;
geometry.uvsNeedUpdate = true;
geometry.normalsNeedUpdate = true;
geometry.colorsNeedUpdate = true;
geometry.tangentsNeedUpdate = true;
var material = new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture('assets/Map-COL.jpg'),
specularMap: THREE.ImageUtils.loadTexture('assets/Map-SPEC.jpg'),
normalMap: THREE.ImageUtils.loadTexture('assets/Map-NOR.jpg'),
shininess: 10
});
mesh = new THREE.Mesh(geometry, material);
// ALL FOLLOWING VECTORS SHOULD COME FROM D.B
var pos = new THREE.Vector3(0.18564199509178245, 23.11243036463454, 21.79273328636014);
var rot = new THREE.Vector3(-0.24357513937426453, -0.07708039421506024, 4.358263365975027);
var some = new THREE.Vector3(20.694949486021706, 20.694949486021706, 20.694949486021706);
var check = new THREE.Vector3(1, 1, 1);
var m = new THREE.Mesh(new THREE.DecalGeometry(mesh, pos, rot, some, check), decalMaterial);
scene.add(mesh);
decals.push(m);
scene.add(m);
mesh.scale.set(10, 10, 10);
});
}
Adding a simple timeOut did the trick
setTimeout(function(){
let m = new THREE.Mesh(new DecalGeometry(mesh, pos, rot,
size), decalMateria2);
decals.push(m);
scene.add(m);
},100)

Three.js animated bezier curves

EDITED. SOLUTION FOUND
I need to know how to implement animation of the points in a curve to simulate string movement in 3D with performance in mind.
Multiple strings between two points for example.
Fiddle provided. (code updated)
So I have curveObject and I'm trying to change position of a point1. (code updated)
var camera, scene, renderer;
var angle1 = angle2 = 0;
var curve1, point1, curveObject, geometryCurve, materialCurve;
var params1 = {P0x: 0, P0y: 0,
P1x: 2, P1y: 2,
P2x: -2, P2y: 1,
P3x: 0, P3y: 3,
steps: 30};
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 10;
scene.add(camera);
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setClearColor( 0x16112b, 1 );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
createBezierCurveNEW = function (cpList, steps) {
var N = Math.round(steps)+1 || 20;
var geometry = new THREE.Geometry();
var curve = new THREE.CubicBezierCurve3();
var cp = cpList[0];
curve.v0 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[1];
curve.v1 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[2];
curve.v2 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[3];
curve.v3 = new THREE.Vector3(cp[0], cp[1], cp[2]);
var j, stepSize = 1/(N-1);
for (j = 0; j < N; j++) {
geometry.vertices.push( curve.getPoint(j * stepSize) );
}
return geometry;
};
function CreateCurve(){
scene.remove(curve1);
var controlPoints1 = [
[params1.P0x, params1.P0y, 0],
[params1.P1x, params1.P1y, 0],
[params1.P2x, params1.P2y, 0],
[params1.P3x, params1.P3y, 0] ];
var curveGeom1 = createBezierCurveNEW(controlPoints1, params1.steps);
var mat = new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 5 } );
curve1 = new THREE.Line( curveGeom1, mat );
scene.add(curve1);
};
CreateCurve();
function animate() {
CreateCurve();
render();
angle1 -= .007;
angle2 += .003;
params1.P1x = Math.cos(angle1)+2;
params1.P1y = Math.sin(angle1)+3;
params1.P2x = -Math.cos(angle2)-2;
params1.P2y = Math.cos(angle2)+1;
requestAnimationFrame(animate);
};
animate();
function render() {
renderer.render(scene, camera);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>
I see value increment in console,
but no actual visual feedback. My guess - I need to update curve somehow.
Final goal is to smoothly animate slow sine-like movement of the curve.
like control points of bezier curve are being moved in Photoshop.
(The goal was reached. Sadly not by my own. I've stumbled upon some helper code lib at http://cs.wellesley.edu/~cs307/lectures/15.shtml so BIG thanks to these guys.)
There is little info regarding curve animation in threejs.
Maybe someone already got going something similar.
(The goal was reached. Sadly not by my own. I've stumbled upon some helper code lib at http://cs.wellesley.edu/~cs307/lectures/15.shtml so BIG thanks to these guys.)

THREE.js not drawing class objects

When my code runs, I don't see anything drawn on the scene. There are no errors, but I don't see the three ArrowHelper members drawn.
However, if I create 3 local ArrowHelper variables in my draw() function and add those to the scene, it works.
The ArrowHelper's are drawn each time render is called with the above code. The reason I don't want to use that version is because I think it is adding new meshes to the scene at each render call. I would prefer to only add one mesh and then change it.
I have a common function for Frame that returns a new instance of Frame. I use that common function to instantiate the variable A.
Can anyone explain what's going wrong with my first method? (Any other info, advice, etc. is welcome)
My source code is:
main.js:
var init=false;
var scene, camera, controls, renderer;
var A;
function initScene()
{
// Create scene and camera
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z = 5;
// Create Renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// Create controls object for mouse control of the scene
controls = new THREE.OrbitControls( camera, renderer.domElement );
A = Frame.common.unit();
A.draw();
// Set init flag
init = true;
// Call render
render();
}
function render()
{
if(!init)
{
console.log("Calling initScene");
initScene();
}
// Request frame and call controls.update()
requestAnimationFrame(render);
controls.update();
// Render
renderer.render( scene, camera );
}
frame.js:
var Frame = function(p, i, j, k)
{
console.log("Frame instance created");
this.p = p;
this.i = i;
this.j = j;
this.k = k;
this.i_arrow = new THREE.ArrowHelper( this.i, this.p, 1, 0x0000ff );
this.j_arrow = new THREE.ArrowHelper( this.j, this.p, 1, 0x00ff00 );
this.k_arrow = new THREE.ArrowHelper( this.k, this.p, 1, 0xff0000 );
};
Frame.common =
{
unit: function()
{
console.log("\"Unit\" Frame instance being created");
var p = new vec3.fromValues(0, 0, 0);
var i = new vec3.fromValues(1, 0, 0);
var j = new vec3.fromValues(0, 1, 0);
var k = new vec3.fromValues(0, 0, 1);
var result = new Frame(p, i, j, k,);
return result;
},
rand: function()
{
console.log("Random Frame instance being created");
var p = {};
vec3.random(p);
var i_hat = {};
vec3.random(i_hat);
var j_hat = {};
vec3.random(j_hat);
var k_hat = {};
vec3.random(k_hat);
var result = new Frame(p, i_hat, j_hat, k_hat);
return result;
}
} // End common
Frame.prototype.draw = function(scene)
{
scene.add(this.i_arrow, this.j_arrow, this.k_arrow);
/*var i = new THREE.Vector3(this.i[0], this.i[1], this.i[2]);
var j = new THREE.Vector3(this.j[0], this.j[1], this.j[2]);
var k = new THREE.Vector3(this.k[0], this.k[1], this.k[2]);
var p = new THREE.Vector3(this.p[0], this.p[1], this.p[2]);
var i_arrow = new THREE.ArrowHelper( i, p, 1, 0x0000ff );
var j_arrow = new THREE.ArrowHelper( j, p, 1, 0x00ff00 );
var k_arrow = new THREE.ArrowHelper( k, p, 1, 0xff0000 );
scene.add(i_arrow);
scene.add(j_arrow);
scene.add(k_arrow);*/
}
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'/>
<title>HTM Tutorial</title>
<style>
body {margin: 0;}
canvas {width: 100%; height: 100%}
</style>
</head>
<body onload='initScene()'>
<script type="text/javascript" src="node_modules/three/three.min.js"></script>
<script type="text/javascript" src="node_modules/gl-matrix/dist/gl-matrix-min.js"></script>
<script type="text/javascript" src="js/lib/OrbitControls.js"></script>
<script type="text/javascript" src="js/frame.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Animated mesh does not load in Babylon.js

I created a mesh in Blender and called it "Walking.babylon". I set it up in my code just how it states in the babylon tutorial:
///<reference path="/ref script/babylon.1.14-debug.js"/>
"use strict"
var canvas;
var engine;
var scene;
document.addEventListener("DOMContentLoaded", startBabylonJS, false);
function startBabylonJS() {
if (BABYLON.Engine.isSupported()) {
canvas = document.getElementById("renderCanvas");
engine = new BABYLON.Engine(canvas, true);
scene = new BABYLON.Scene(engine);
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
light.position = new BABYLON.Vector3(20, 150, 70);
//create the camera that will view our scene
var cam = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
cam.setTarget(new BABYLON.Vector3.Zero());
cam.attachControl(canvas, false);
scene.ambientColor = new BABYLON.Color3(0.3, 0.3, 0.3);
// Ground
var ground = BABYLON.Mesh.CreateGround("ground", 1000, 1000, 1, scene, false);
var groundMaterial = new BABYLON.StandardMaterial("ground", scene);
groundMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.2, 0.2);
groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
ground.material = groundMaterial;
ground.receiveShadows = true;
// Shadows
var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
// Dude
BABYLON.SceneLoader.ImportMesh("man", "Scenes/Models/Animation/", "Walking.babylon", scene, function (newMeshes, particleSystems, skeletons) {
var dude = newMeshes[0];
for (var index = 0; index < newMeshes.length; index++) {
shadowGenerator.getShadowMap().renderList.push(newMeshes[index]);
}
dude.rotation.y = Math.PI;
dude.position = new BABYLON.Vector3(0, 0, -80);
scene.beginAnimation(skeletons[0], 0, 100, true, 1.0);
});
//Once the scene is loaded, just register a render loop to render it
engine.runRenderLoop(function () {
scene.render();
});
//Resize
window.addEventListener("resize", function () {
engine.resize();
});
}
}
But When I load my scene and hit F12 I get this message in the console: Failed to load resource: the server responded with a status of 404 (Not Found) and right next to it is this link: http://localhost:50207/Scenes/Models/Animation/Walking.babylon.manifest?1419869394361
So my question is: what am I doing wrong that is causing my animated mesh not to show?
Open notepad and put this:
{
"version" : 1,
"enableSceneOffline" : true,
"enableTexturesOffline" : true
}
Then rename it: Walking.babylon.manifest
And put it where "Walking.babylon" is.

Three.js update scale object-size via view

i'm new in three.js and have a problem by scaling a object. I already looked at the documentation (https://github.com/mrdoob/three.js/wiki/Updates), but i don't really understand it at all.
My Problem: By changing a HTML-select-element the CubeGeometry should be scaled in the x-direction. That is already working BUT the "old" Cube do not disappear. So i have 2 Cubes. But i want only one Cube with the current size. I hope you can understand my problem ;-)
So here is my Code from the View:
$(document).on('change',".configurator > form select",function(event){
// update 3D-object - is that a nice way???
$.getScript("/javascripts/3D-animation.js.erb", function(){
// here comes the update
OBJECT3D.updateObject();
});
})
And here is my 3D-animation.js.erb:
var drawing_area;
var renderer;
var camera;
var obj;
var scene;
var OBJECT3D = {};
$(function() {
// get drawing_area
drawing_area = document.getElementById("canvas_wrapper");
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(drawing_area.clientWidth, drawing_area.clientHeight);
renderer.setClearColor( 0xffffff, 1);
// add renderer to drawing_area
drawing_area.appendChild(renderer.domElement);
// initialize camera
camera = new THREE.PerspectiveCamera(45, drawing_area.clientWidth/drawing_area.clientHeight, 1, 100000);
camera.position.z = 1000;
camera.position.y = 100;
camera.position.x = 300;//-0.78;
// create texture
var texture = THREE.ImageUtils.loadTexture( "/images/materials/texture_1.jpg" );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 1, 1 );
// create object
var obj_form = new THREE.CubeGeometry(250,250,250);
var obj_material = new THREE.MeshLambertMaterial( { map: texture,ambient: 0x999999 } );
OBJECT3D.obj = new THREE.Mesh(obj_form, obj_material);
// so what do i need here?
OBJECT3D.obj.geometry.dynamic = true;
// OBJECT3D.obj.geometry.__dirtyVertices = true;
// OBJECT3D.obj.geometry.__dirtyNormals = true;
// OBJECT3D.obj.geometry.verticesNeedUpdate = true;
// create scene
scene = new THREE.Scene();
scene.add(camera);
scene.add(OBJECT3D.obj);
// create lights
pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 400;
pointLight.position.y = 200;
pointLight.position.z = 1300;
scene.add(pointLight);
ambientLight = new THREE.AmbientLight( 0xffffff);
scene.add( ambientLight );
requestAnimationFrame(render);
function render(){
requestAnimationFrame(render);
OBJECT3D.obj.rotation.y += 0.005;
OBJECT3D.obj.rotation.z += 0.005;
renderer.render(scene, camera);
};
// update object
OBJECT3D.updateObject = function () {
console.log("in update");
OBJECT3D.obj.scale.x = 2.5; // SCALE
OBJECT3D.obj.geometry.needsUpdate = true;
//render();
}
});
Sorry, if the Code is not the best one, but i'm really new in this stuff! :) hope you can help me!
Thanks!
I think it is because you are using $.getScript method. This will load your script each time again and create a new instance of OBJECT3D.
I propose that you make sure that your code in 3D-animation.js.erb will be included and called only once upon the load of a page (include it just like any other regular javascript file) and then call your update directly like this:
$(document).on('change',".configurator > form select", function(event) {
OBJECT3D.updateObject();
});
also, I believe you can drop following lines of code:
OBJECT3D.obj.geometry.needsUpdate = true;
and
OBJECT3D.obj.geometry.dynamic = true;

Categories