I am running a three.js example code, its three.js version is 0.124.
createObj() {
const geometry = new THREE.InstancedBufferGeometry();
const baseGeometry = new THREE.BoxGeometry(2, 2, 20, 2, 2, 6);
// Copy attributes of the base Geometry to the instancing Geometry
geometry.copy(baseGeometry);
}
when the code is running in 'geometry.copy(baseGeometry);', an error is popping up as showed as
TypeError: Cannot read properties of undefined (reading 'clone')
at InstancedBufferGeometry.copy (three.module.js?cf64:10935)
at InstancedBufferGeometry.copy (three.module.js?cf64:40628)
at createObj (Clouds.js?d048:24)
InstancedBufferGeometry.copy() only accepts another InstancedBufferGeometry object as the argument. You're trying to pass a legacy Geometry object (THREE.Geometry was part of the core up until r125, and you're using r124), so it breaks when performing that method when it notices that it's missing all the instancing properties.
See here in the docs for using the copy() method
Related
I have a question about updating geometry in three.js using dat.gui.
I expected I could update the geometry by updating the value tied into the geometry by object as below.
gui.add(parameter, 'width')
.min(1)
.max(10)
.step(0.1)
.name('cubeWidth')
.onChange((value) => {
mesh.geometry.dispose();
geometry.parameters.width = parameter.width
console.log(mesh)
})
However, it updates the geometry only when I redefine the geometry like below.
mesh.geometry = new THREE.BoxGeometry(parameter.width, 1, 1);
It's bit weird to me because even when I log the geometry data both shows the updated width value like below.
Why does only the first approach work while the other one not work?
geometry.parameters.width = parameter.width
This has no effect. Parameters are only processed when a geometry is created. The parameters property is in fact read-only.
The solution for your issue is indeed to recreate the geometry with the new set of parameters.
Outlines are not being created. I attempted a BloomFilter as well and it looks the same as if I didn't add any filters.
What could be the issue here?
// in global scope
const clock = new THREE.Clock();
// in initialization
this.composer = new EffectComposer(this.renderer);
this.composer.addPass(new RenderPass(this.scene, this.camera.camera_object));
this.composer.addPass(new EffectPass(this.camera.camera_object, new OutlineEffect(this.renderer, this.camera.camera_object, {
defaultThickness: 0.01,
defaultColor: [0, 0, 0],
defaultAlpha: 0.8,
defaultKeepAlive: true
})));
// in render loop
this.composer.render(clock.getDelta());
It is hard to see the error without a full sample of the code. PostProcessing effects require the following:
Load the postprocessing lib. In the example below I provided the full URL to for effects.
Create the effect pass. A new THREE.OutlinePass or new THREE.UnrealBloomPass or similar.
Add effect with composer.addPass([your_pass]).
Here is a minimal CodePen sample using both a OutlinePass and UnrealBloomPass:
https://codepen.io/adelriosantiago/pen/qBaLmZK?editors=1010
Note that OutlinePass requires selectedObjects to be an array with the objects that you want to affect.
I am working on a POC in which I have to customize a watch in 3d model.
I am using thee.js for doing webgl stuff. I was able to change strap color , dial color and load texture images on strap. But I am stuck when I try to add dynamic text on the the watch , I have to update text on watch's back dial as user types in Engrave Text text box . For this I tried https://github.com/jeromeetienne/threex.dynamictexture library , which works on a new geometry but I am not able to add it watch's existing model.
I have pushed all code in github : https://github.com/bhupendra1011/watch-3d-engrave , as I was not able to load external 3d models from my fiddle account {CORS issue}.
Demo of the POC can be seen here : https://bhupendra1011.github.io/watch-3d-engrave/index.html
Below is the code to add text on watch dial; For loading text click on use textures textbox in index.html
function engraveTextOnWatch(val = "IWC") {
dynamicTexture = new THREEx.DynamicTexture(512, 512);
dynamicTexture.context.font = "bolder 90px Verdana";
// watch back dial geometry & material where text needs to be engraved
var backDialGeometry = object3d.children[1].children[0].children[0].geometry;
var backDialMaterial = object3d.children[1].children[0].children[0].material;
// geometry to add dynamic text
var geometry = new THREE.CubeGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
map: dynamicTexture.texture
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh); // adding mesh to scene , but this needs to be attache to watch's back dial geomtry
/* tried adding dynamicTexture to watch back dial material, but this is not working , text not appearing */
//backDialMaterial.map = dynamicTexture.texture;
dynamicTexture.texture.needsUpdate = true;
dynamicTexture.drawText(val, 2, 256, "orange");
}
Kindly let me know how can I add dynamic text on existing model.
Thanks.
I was able to reproduce your problem in Blender and it comes from your model's UV map.
I downloaded your model, opened it in Three.js editor and converted it to .obj to open it in blender to reproduve your problem
As you can see on the image, applying a texture is just as messy. You need to rework the model's uvmap to achieve the desired effect like so (I used a random image from your folder)
And while you're at it, you might want to consider optimizing your model, which is pretty heavy and could be simplified for faster load without losing visual quality (but it's entirely up to you, I'm just suggesting).
Here's the current model
Here's a quick possible replacement. Cylinder that's open on one end and beveled on the edge loop
I'm creating a scene where objects are brought in from JSON files (exported from blender) and added to the scene. Since this is going to be a Cordova app, I can't use JSONLoader (or any XHR resource).
I'm able to get the JSON file using the FileSystem API, but I can't figure out how to create a THREE.Geometry object from the JSON, since the Geometry constructor does not take any arguments. (normally, the finished Geometry object is provided automatically by the JSONLoader callback function). I can't help but feel there's something obvious I'm missing. Any suggestions?
Option 1:
You can build your own THREE.Geometry object by manually adding its attributes. The docs give you a quick example of how to add vertices and faces:
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3( -10, 10, 0 ),
new THREE.Vector3( -10, -10, 0 ),
new THREE.Vector3( 10, -10, 0 )
);
geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
geometry.computeBoundingSphere();
With this example, you could write your own iterators, and create your Geometry with the data in your JSON file.
Option 2:
The above process is probably going to get too verbose if you need normals, uvs, colors, etc. So instead of re-writing it manually, just use the JSONLoader's parsing attributes, without using it to load the data. This is possible via the THREE.JSONLoader.parse() method:
// Use whatever method you're using to fetch the data
var JSONData = getJSONSomehow();
// Now we only use JSONLoader for parsing
var JSONParser = new THREE.JSONLoader();
var myObject3D = JSONParser.parse(JSONData);
scene.add(myObject3D);
I'm using three.js and OrbitControls.js in combination, in a 3D app. Sometimes WebGLRenderer.render gets called with an undefined camera. This happens when I use the mouse to navigate the 3D model, using the controls delivered by OrbitConrols.
The undefined camera argument is weird, as my animate function (see code below) always calls WebGLRenderer.render with a well defined camera. Also, when instantiating OrbitControls, I give it a well defined camera. So the question is - how can it be that WebGLRenderer.render is at some point called with an undefined camera?
NOTE: The source code for the WebGLRenderer.render function can be seen in line 20426 in the three.js source code.
I attempted to locate all the potential call sites by searching for the text string render(. There are 14 such matches on the string render(, but none of these gives an undefined camera as argument. Thus, the trail was cold.
I tried stack tracing from the callee (the function body of the WebGLRenderer.renderfunction), but this merely lead back to some mix-it-all event hub. But it gave me a hint that the caller might be Javascript itself, calling from its DOM event system. That would explain why I couldn't find the call site in the three.js source code.
Thus perhaps the problem is associated with the point at which OrbitControls interacts with the Javascript event system. When initialising my app, I register an event on my OrbitControls instance. See code below. Could this be causing trouble? No camera is given as argument when this happens though :/
var myControls = require('../../instances/three/myControls');
var myRenderer = require('../../instances/three/myRenderer');
myControls.damping = 0.2;
myControls.domElement.addEventListener( 'change', myRenderer.render );
EDIT:
I'm using these versions:
three.js 0.81.0
three-orbit-controls 72.0.0
So, indeed the wrong-doing call to render was coming from some event system (the Javascript DOM event system). This module shows where the render function is given as callback to an event listener:
var myControls = require('../../instances/three/myControls');
var myRenderer = require('../../instances/three/myRenderer');
myControls.damping = 0.2;
myControls.addEventListener( 'change', myRenderer.render );
module.exports = {};
( where the myControls module looks like this:
var THREE = require('three');
var orbitControls = require('three-orbit-controls');
var myRenderer = require('../../instances/three/myRenderer');
var myCamera = require('./myCamera');
require('../../sideeffects/three/addCamera');
// add orbitControls to THREE:
var OrbitControls = orbitControls( THREE );
// make controls:
var controls = new OrbitControls( myCamera, myRenderer.domElement );
module.exports = controls;
)
However, it seems that OrbitControls.js has been changed in such a way that instances of the OrbitControls constructor are no longer DOM-elements. Thus, one CANNOT register events on them, as I was doing with the line:
myControls.addEventListener( 'change', myRenderer.render );
Instead, instances of the OrbitControls constructor has a new property: domElement which, as you might have guessed, is indeed a DOM element. Thus, I can fix the problem by changing the target of my event attachment to myControls.domElement. Thus the above line comes to look like this:
myControls.domElement.addEventListener( 'change', myRenderer.render );
and that solved the problem :)
EDIT:
It turns out that this line can actually be omitted completely! It is only necessary if there is no animation loop (a perpetual loop that calls requestAnimationFrame on each loop step)
Also, dampening can be enabled by doing this:
myControls.enableDamping = true;
and then adding myControls.update(); somewhere in your animation loop.