I am trying to create a scene for my class where a group of objects (a model) has to be cloned and displayed in random positions. I created a function for this, but unfortunately, it only transfers the same object from one place to another. I need to add more objects instead of moving the same one, but sadly I cannot find any information about it. I tried to clone, but I failed :/ Later on, I will have to remove these models one by one, so if anybody could give advice on that too, I would appreciate it.
Here's my code:
this.addmodel = function() {
scene.add(model);
model.name = "model-" + scene.children.length;
model.position.x= -20 + Math.round((Math.random() * 40));
model.position.y= Math.round((Math.random() * 5));
model.position.z= -17.5 + Math.round((Math.random() * 35));
this.numOfObjects = scene.children.length;
}
Sample object:
function Model(){
this.mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1),new THREE.MeshBasicMaterial({color:"red"}));
this.addModel = function(){
var newModel = this.mesh.clone();
newModel.name = "model-" + scene.children.length;
newModel.position.x= -20 + Math.round((Math.random() * 40));
newModel.position.y= Math.round((Math.random() * 5));
newModel.position.z= -17.5 + Math.round((Math.random() * 35));
scene.add(newModel);
}
};
then create an instance and call its method:
var cubeModel = new Model();
for(var i = 0; i < 10; i++){
cubeModel.addModel();
}
jsfiddle example
Related
I am trying to make a function that randomizes the placement of some clouds I imported, but the problem is, it generates the amount of clouds, but in the same position! I randomized the position, but when the code runs I have like 10 clouds in the same position. What can I do? this is the code:
loader.load('/clouds/clouds1/scene.gltf', function (clouds1) {
var clouds1array = []
function addClouds1(){
for (var i = 1; i < 10; i++) {
const clouds1Mesh = clouds1.scene
const clouds1Position = clouds1Mesh.position
clouds1Position.x = Math.random() * 10
clouds1Position.y = Math.random() * 10
clouds1Position.z = (Math.random() - 0.5 ) * 300
clouds1Mesh.scale.setX(0.05)
clouds1Mesh.scale.setY(0.05)
clouds1Mesh.scale.setZ(0.05)
scene.add(clouds1Mesh)
clouds1array.push(clouds1Mesh)
}
}
addClouds1()
})
edit: clouds1.scene structure is this:
I don't know why it has this amount of children, I tried to solve with the answer, but it still does not work. The 3rd child in the end contains the mesh, and I tried using that for it to work, but it says that I cannot use it in the scene.add() function
edit: I solved the problem! I just had to put the for loop outside the load function
for(let i = 0; i < 30; i+= 3)
loader.load('/clouds/clouds1/scene.gltf', function (clouds1) {
const cloud = clouds1.scene
const child1 = clouds1.scene.children[0].children[0].children[0].children[2].children[0]
child1.material = new THREE.MeshStandardMaterial({ emissive: 'white', emissiveIntensity: 0.5})
cloud.scale.set(0.05, 0.05, 0.05)
cloud.position.x = (Math.random() - 0.5) * 500
cloud.position.y = (Math.random() + ((Math.random() + 20 ) + 70))
cloud.position.z = (Math.random() - 1) * 500
cloud.rotation.x = Math.random()
cloud.rotation.y = Math.random()
cloud.rotation.z = Math.random()
scene.add(cloud)
})
The GLTFLoader results, which you have as clouds1 is a generic object, from which you properly extract clouds1.scene. However, clouds1.scene is also a single Scene object. If you have 10 clouds in the GLTF model you loaded, then clouds1.scene will have 10 children, and you will need to loop through them like this:
loader.load('/clouds/clouds1/scene.gltf', function (clouds1) {
var clouds1array = []
const clouds1Children = clouds1.scene.children
for (var i = 1; i < 10; i++) {
const clouds1Mesh = clouds1Children[i]
const clouds1Position = clouds1Mesh.position
clouds1Position.x = Math.random() * 10
clouds1Position.y = Math.random() * 10
clouds1Position.z = (Math.random() - 0.5 ) * 300
clouds1Mesh.scale.setX(0.05)
clouds1Mesh.scale.setY(0.05)
clouds1Mesh.scale.setZ(0.05)
scene.add(clouds1Mesh)
clouds1array.push(clouds1Mesh)
}
})
it's my first question on this website. haha.
I was trying to make some dummy data which is from yesterday 12AM to now 15 minutes apart.
here is my code.(Javascript)
const toDay = new Date();
const fromDay = new Date(
toDay.getFullYear(),
toDay.getMonth(),
toDay.getDate() - 1,
0,
0
);
const duration = Math.floor((toDay - fromDay) / (1000 * 60 * 15));
console.log('duration', duration);
let arrayOfData = [];
let dataForX = fromDay;
let dataForY = 0;
for (let i = 0; i < duration; i++) {
arrayOfData.push({
date: dataForX,
value: dataForY,
});
// dataForX = new Date(dataForX.setMinutes(dataForX.getMinutes() + 15));
dataForX.setMinutes(dataForX.getMinutes() + 15);
dataForY = Math.random() * 100;
}
console.log('arrayOfData', arrayOfData);
I wanted some data like Pic1 to make:
But, it resulted in Pic2:
Fortunately, I solve this problem by coding like this,
dataForX = new Date(dataForX.setMinutes(dataForX.getMinutes() + 15));
instead of this.
dataForX.setMinutes(dataForX.getMinutes() + 15);
But, I still don't understand why it came this different result out.
Can you tell me the reason for this result?
When you write dataForX.setMinutes(dataForX.getMinutes() + 15); you are actually changing the reference of the same object, thus all dates end up being the same.
When you write dataForX = new Date(dataForX.setMinutes(dataForX.getMinutes() + 15));, you create a new object(reference) every time. The name of the variable is the same, but the reference changes. You store the old reference first, then create a new one.
Consider the below snippet. The principle is the same as with the dates, but I created an object to make it easier to understand.
When I change foo to be foo = {bar:10};, you might think that the value inside the array would be changed to 10, but it isn't. We are creating a new reference, so the old reference is not affected by our change.
const myArray = [];
let foo = {bar: 3};
myArray.push(foo);
console.log(myArray); // will print 3
foo.bar = 5;
console.log(myArray); // will print 5
foo = {bar:10}; // creating a new reference using the same variable name
console.log(myArray); // will still print 5
myArray.push(foo);
console.log(myArray); // will print 5 and 10
foo.bar = 12;
console.log(myArray); // will print 5 and 12
You need a copy of the date object:
arrayOfData.push({
date: new Date(dataForX.getTime()), // <-- Here
value: dataForY,
});
Example with the change:
const toDay = new Date();
const fromDay = new Date(
toDay.getFullYear(),
toDay.getMonth(),
toDay.getDate() - 1,
0,
0
);
const duration = Math.floor((toDay - fromDay) / (1000 * 60 * 15));
console.log('🚀 ~ file: script.js ~ line 10 ~ duration', duration);
let arrayOfData = [];
let dataForX = fromDay;
let dataForY = 0;
for (let i = 0; i < duration; i++) {
arrayOfData.push({
date: new Date(dataForX.getTime()),
value: dataForY,
});
// dataForX = new Date(dataForX.setMinutes(dataForX.getMinutes() + 15));
dataForX.setMinutes(dataForX.getMinutes() + 15);
dataForY = Math.random() * 100;
}
console.log('arrayOfData', arrayOfData);
.as-console-wrapper { top: 0; max-height: 100% !important; }
I have a rather broad question, but no idea how to tackle that. So forgive me.
I am trying to have several (like 200 and more) objects and, let's just say, a container to the side of the field, where I draw the objects. Now what I want is, that each object has some non visual attributes and when I click on that object, the attributes should appear in that container.
Now I could go about it?
I mean, I know I can ask for the name of the selected object and then do a key value query from some dictionary. Question is, whether there is an easier way to go about it.
For the click event I used a library called threex.domevents, check the GitHub page for more information, the code for the event it's self explanatory.
First domevents needs to be initialized in your scene like this:
var domEvents = new THREEx.DomEvents(camera, renderer.domElement);
Then I created a custom Mesh object:
// random id
function genRandomId()
{
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 5; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
// random int for position
var min = -50;
var max = 50;
function genRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// custom mesh --------------------------------------------
function MyMesh(geometry, material, destinationContainer) {
THREE.Mesh.call(this, geometry, material);
this.userData = {
foo1: genRandomId(),
foo2: genRandomId(),
foo3: genRandomId(),
};
this.position.x = genRandomInt(min, max);
this.position.y = genRandomInt(min, max);
this.position.z = genRandomInt(min, max);
var that = this;
// click event listener
domEvents.addEventListener(this, 'click', function(event) {
console.log('clicked object on position:');
console.log(that.position);
destinationContainer.userData = that.userData;
console.log('Now the conainer has:');
console.log(destinationContainer.userData);
destinationContainer.userData = that.userData;
}, false);
}
MyMesh.prototype = Object.create(THREE.Mesh.prototype);
MyMesh.prototype.constructor = MyMesh;
genRandomId and genRandomInt are random generators for the pourpose of illustrating this example, I took the code for the random ids from Generate random string/characters in JavaScript.
In your scene you can generate 200 (or more) MyMesh meshes and add them to the scene:
const color = 0x156289;
const emissive = 0x072534;
var planeGeometry = new THREE.PlaneGeometry(5, 5);
var planeMaterial = new THREE.MeshPhongMaterial({
color: color,
emissive: emissive,
side: THREE.DoubleSide,
shading: THREE.FlatShading
});
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(planeMesh);
var objGeometry = new THREE.BoxGeometry(1, 1, 1);
var objMaterial = new THREE.MeshPhongMaterial({
color: color,
emissive: emissive,
shading: THREE.FlatShading
});
var i = 0;
while (i < 200) {
scene.add(new MyMesh(objGeometry, objMaterial, planeMesh));
i++;
}
And finally render the scene:
var render = function() {
requestAnimationFrame(render);
planeMesh.rotation.x += 0.010;
planeMesh.rotation.y += 0.010;
renderer.render(scene, camera);
};
render();
This is a demo with the full source code: http://run.plnkr.co/plunks/W4x8XsXVroOaLUCSeXgO/
Open the browser console and click on a cube and you'll see the that planeMesh is switching its userData attributes with the ones of the clicked cube mesh.
Yes, that's fine. You can put your own custom keys directly on a Three.js object and it shouldn't bother it as long as you don't accidentally overwrite an important built-in Three.js key. For that reason I'd recommend that you put all of your custom keys in a "namespace" on the object so they're nice and neat and contained.
For example, if you had a Three.js object foo, you could put all your keys under foo.myCustomNamespace, so that your custom data like foo.myCustomNamespace.name, foo.myCustomNamespace.description, etc. are all together and won't interfere with THREE.js properties.
Edit: Three.js provides a built-in namespace for user data called, conveniently, userData. Access it on THREE.Object3D.userData.
https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L92
I am trying to generate a texture from an array in threeJS and it is not working as expected.
It appears that the way I generate the texture is not correct.
If I use the following texture, it works as expected.
http://www.html5canvastutorials.com/demos/assets/crate.jpg
crateTex = THREE.ImageUtils.loadTexture('data/crate.jpg');
If I generate a dummy texture and try to display it, it is all black...
var dummyRGBA = new Uint8Array(4 * 4 * 4);
for(var i=0; i< 4 * 4; i++){
// RGB from 0 to 255
dummyRGBA[4*i] = dummyRGBA[4*i + 1] = dummyRGBA[4*i + 2] = 255*i/(4*4);
// OPACITY
dummyRGBA[4*i + 3] = 255;
}
dummyDataTex = new THREE.DataTexture( dummyRGBA, 4, 4, THREE.RGBAFormat );
dummyDataTex.needsUpdate = true;
dummyTex = new THREE.Texture(dummyDataTex);
I think your mistake is in the fact that you make a texture of a texture.
When you do:
dummyDataTex = new THREE.DataTexture( dummyRGBA, 4, 4, THREE.RGBAFormat );
the object dummyDataTex that you create here is already of type THREE.Texture.
So your next step:
dummyTex = new THREE.Texture(dummyDataTex);
is not necessary. You should instead immediately use dummyDataTex.
So, I have a friendly neighborhood object constructor, like so;
function Clip(a, b)
{
this.track = a
this.slot = b
this.path = "live_set tracks " + a + " clip_slots " + b + " clip "
clip = new LiveAPI(this.patcher, this.path)
this.length = clip.get("length")
}
What I'd like to do is
Add an an arbitrary number of them to an array
When the length of the array hits 8, add that array to a new "super"array and start a new array.
In other words, the superarray should allow me to access the objects' properties and methods by, for instance, clip[0][0].length - clip[0][7].length, clip[1][0].length - clip[1][7].length, etc.
Is this what you're looking for? I simplified some of the code, but the general idea seems to fit.
http://jsfiddle.net/bryandowning/pH6bU/
var superArr = [];
function Clip(a) {
this.length = a;
}
/*
* num: number of clip collections to add to container
* max: number of clips per collection
* container: array to add collections to
*/
function addClips( num, max, container ){
while(num--){
// arr: a collection of clips
var arr = [];
for( var i = 0; i < max; i++ ){
arr.push(
// just did a random number for the length
new Clip( Math.floor( Math.random() * 10 ) )
);
}
container.push( arr );
}
}
addClips( 5, 8, superArr );
console.log( superArr );
console.log( superArr[0][0].length );
​