I converted an FBX model to JSON using convert-to-threejs.py, but I can't get three.js (r58) to load it. It says "Uncaught TypeError: Cannot read property 'length' of undefined". The error is at line 9930 in three.js which line is for ( i = 0; i < json.uvs.length; i++ ) ...
Here's the converted JSON: http://pastebin.com/86ZGvKnW. You'll see that it does not, in fact, have a uvs property.
Is convert-to-threejs.py outdated? If so, what's the alternative? If not, how can I get this model to load?
Here's my code:
window.loader = new THREE.JSONLoader();
loader.load('jet.json', function(geometry) {
// Never gets to here; error is thrown first
console.log(geometry);
});
Oh, I see. The actual model is embedded in that JSON file. I want embeds["Embed_31_jet 3"].
FBX files converted to JSON seem to describe complete scenes. So, instead of THREE.JSONLoader, use THREE.SceneLoader.
For example
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(...);
var loader = new THREE.SceneLoader();
loader.load('jet.json', function(res) {
scene.add(res.scene);
renderer.render(res.scene, camera);
});
Related
I try to add a custom sphereMesh to a offline viewer. I saw this can be done with the online version of the viewer. But I get an uncaught TypeError: Cannot read property 'Add' of undefined if I try this in my viewer.
<div id="MyViewerDiv"></div>
<script>
var myViewerDiv = document.getElementById('MyViewerDiv');
var viewer = new Autodesk.Viewing.Private.GuiViewer3D(myViewerDiv);
var options = {
'env' : 'Local',
'document' : './_3D_ 197440/_3D_.svf'
};
Autodesk.Viewing.Initializer(options, function() {
viewer.start(options.document, options);
});
var geom = new THREE.SphereGeometry(1000, 8, 8);
var material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var sphereMesh = new THREE.Mesh(geom, material);
sphereMesh.position.set(0, 0, 0);
viewer.impl.scene.add(sphereMesh);
</script>
There is no difference between online and offline viewer, when it comes to ways of adding custom geometry.
In your case you have a situation similar to "race condition", where you try to use a resource while it is initializing: sometimes the init process finishes first => success, sometimes it finishes later => get errors.
To solve the problem, there are 2 approaches:
Add your code into an extension and load extension as mentioned in documentation (the recommended one). Example: https://apprentice3d.github.io/SD226781-Samples/02.html
Move your custom mesh code into the body of the document loading callback function. Example: Check the source of https://s3.amazonaws.com/sample-collection/GiroWatch_Latest.html
I am trying to display a textured plane with Three.js. I'm working with Forge RCDB.
At first, I managed to display the plane, but instead of being textured, it was completely black... I made some changes and now nothing is displayed anymore...
Here is my code :
render () {
var viewer=NOP_VIEWER;
var scene=viewer.impl.scene;
var camera = viewer.autocamCamera;
var renderer = viewer.impl.renderer();
renderer.render( scene, camera );
}
and in the function supposed to display the textured plane :
new THREE.TextureLoader(texture).load(texture, this.render);
tex.wrapS = THREE.RepeatWrapping //ClampToEdgeWrapping //MirroredRepeatWrapping
tex.wrapT = THREE.RepeatWrapping //ClampToEdgeWrapping //MirroredRepeatWrapping
tex.mapping = THREE.UVMapping
At the beginning I used loadTexture(). I managed to display my plane, but it was all black, and no texture was applied on it.
Then, I use THREE.TextureLoader().load(), in this case, I believe it is trying to find the image on localhost. The image is downloaded, I can see it on the console.
But now I get these errors :
Uncaught TypeError: scope.manager.itemStart is not a function
and :
Uncaught TypeError: renderer.render is not a function
Now the object is not displayed, even in black.
So I think this may be linked to render, but I don't understand how...
I found this, and it answers my question partially.
Finally, I decided to keep THREE.ImageUtils.loadTexture(), and I replaced MeshLambertMaterial by MeshBasicMaterial.
No need for render.
I am using Three.js, version 71. I'm using Blender, version 2.73.
I created a textured collada object (.dae file) using Blender, and now I want to load it into my three.js scene. So far, I can only load models that get exported from blender that have no textures on them.
Here is how I create the textured collada object:
In blender, I simply use the default cube. Using the settings on the right, I add a texture to the cube. Here is the texture I am putting onto the cube (NOTE: it is 2048 X 2048, so it's a power of 2):
Here is an image of the cube in render mode to prove that the texture is on it:
Here are the export settings I used when I exported the cube as a collada from Blender:
Here is some code I used to try to load the textured collada:
var loader = new THREE.ColladaLoader();
var localObject;
loader.options.convertUpAxis = true;
loader.load( './models/test_texture.dae', function ( collada ) {
localObject = collada.scene;
localObject.scale.x = localObject.scale.y = localObject.scale.z = 32;
localObject.updateMatrix();
game.scene.add(localObject);
} );
Here is the error I got:
[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 2
I then googled that error message and someone said the I need to compute tangents. Here are my attempts at that and the errors I got:
var loader = new THREE.ColladaLoader();
var localObject;
loader.options.convertUpAxis = true;
loader.load( './models/test_texture.dae', function ( collada ) {
localObject = collada.scene;
localObject.scale.x = localObject.scale.y = localObject.scale.z = 32;
localObject.updateMatrix();
for (var i = collada.scene.children.length - 1; i >= 0; i--) {
var child = collada.scene.children[i];
// child.children[0] will give us the THREE.Mesh of the collada
if ( child.colladaId == "Cube" ) {
// ATTEMPT 1: Just tried computing tangets based on answer from neoRiley here: http://stackoverflow.com/questions/21200386/webgl-gl-error-gl-invalid-operation-gldrawelements-attempt-to-access-out-of
// child.children[0].geometry.computeTangents();
// ATTEMPT 2: Got this suggestion from Popov here: http://stackoverflow.com/questions/15717468/three-lod-and-normalmap-shader-fail
// child.children[0].geometry[ 0 ][ 0 ].computeTangents();
// child.children[0].geometry[ 1 ][ 0 ].computeTangents();
// ATTEMPT 3: Tried setting some update flags based on answer from Sayris here: http://stackoverflow.com/questions/13988615/webglrenderingcontext-error-loading-texture-maps
// child.children[0].geometry.buffersNeedUpdate = true;
// child.children[0].geometry.uvsNeedUpdate = true;
// child.children[0].material.needsUpdate = true;
// child.children[0].geometry.computeTangents();
}
};
game.scene.add(localObject);
} );
ATTEMPT 1 ERROR:
Uncaught TypeError: Cannot read property '0' of undefined
// Stack trace
three.js:9935 handleTriangle
three.js:9974 THREE.Geometry.computeTangents
myCode.js:116 (anonymous function)
ColladaLoader.js:204 parse
ColladaLoader.js:84 request.onreadystatechange
ATTEMPT 2 ERROR:
Uncaught TypeError: Cannot read property '0' of undefined
This came from own code. I didn't think geometry of THREE.Mesh is two dimensional, but I tried it anyway.
ATTEMPT 3 ERROR: (same as ATTEMPT 1 ERROR)
Uncaught TypeError: Cannot read property '0' of undefined
// Stack trace
three.js:9935 handleTriangle
three.js:9974 THREE.Geometry.computeTangents
myCode.js:116 (anonymous function)
ColladaLoader.js:204 parse
ColladaLoader.js:84 request.onreadystatechange
I decided to use the JSON loader instead because I couldn't get the collada one to work. The first thing I did was install the JSON exporter addon into Blender. I got the addon from the .zip file from my three.js download. It's in
three.js-r71/utils/exporters/blender/addons and it's called io_three. You just need to copy that folder and paste it in your Blender installation directory in Blender Foundation/Blender/2.73/scripts/addons.
You then have to enable it in Blender. To do that:
Click to File->User Preferences...
Click Add-ons
Type three in the search field
All the way to the right, click the check box to enable it
At the bottom left, click Save User Settings so you don't need to do this again. You'll know it's working if you see Three.js (.json) when you click File->Export.
I followed most of the instructions from this site to help me create and export a model: http://graphic-sim.com/B_basic_export.html
Here are the steps I used to create and export the model (I tweaked them a little bit from the site)
Start up Blender.
Look at the Properties editor (on the right).
Press the World context button. In the World panel click Ambient Color and change it from black to middle gray.
Press the Material context button. On the Diffuse panel change Intensity to 1.0. Do the same on the Specular panel. In the Shading panel put a check in the Shadeless box.
Press the Textures context button. Near the top in the Type drop down box, select Image or Movie. In the Image panel, browse to your image (make sure the image's dimensions are in a power of 2).
Choose the UV Editing screen layout (drop-down box to right of help menu at top).
With mouse cursor in 3D editor, go into edit mode (Tab key).
Unwrap (Press the U key). Choose Smart UV Project. Click Ok to accept defaults.
In the UV Editing screen, select your image using the menu at the bottom left (see screenshot)
Select Image->Save As Image. This image will need to be next to your JSON file that you will export.
Click File->Export->Three.js (.json).
To the left, select a few more export options (see screenshot for the ones i used, which I found by trial and error). I think I only added Face Materials, Materials, and Textures. You can then also click Save Settings to save these settings.
Put your JSON file and your image file that you saved earlier in your project folder.
Use the following code to load it:
var object;
var loader = new THREE.JSONLoader();
loader.load( "./models/test_texture.json", function(geometry, materials) {
object = new THREE.Mesh(geometry, materials[0]);
object.scale.set(32, 32, 32);
game.scene.add(object);
});
When you export your model from Blender to Three.js you end up with a file with JSON data inside. I know two ways of loading this model from that file:
var loader = new THREE.JSONLoader()
var material = new THREE.MeshPhongMaterial({color: '#8080a0'})
1.
loader.load('tower.json', function (geometry) {
var mesh = new THREE.Mesh(geometry, material)
})
2. Edit tower.js file and add
var tower =
in first line. Then you can load it with:
var towerModel = loader.parse(tower)
var mesh = new THREE.Mesh(towerModel.geometry, material)
I like second solution more because using loader.load() function every time you want to create a mesh when you have thousands of meshes based on the same model is very slow and can easly kill your browser.
So my question is - can I get JSON data from tower.json file into a variable without editing the file manually? The best solution would be to get JSON data into a variable without making changes to the file.
I would go for option 1.
It sounds very strange to add a loader to a source file.
To keep things clean you should keep loaders and data sources (your model files) separated if you ask me.
Why would you reload the same model anyway. You should just store it in a variable and reuse it. It is a waste of CPU and memory to load the same geometry twice.
You can do easily something like this to optimize things. It is just a concept but I hope you get the idea:
// Storage for model files
myFiles = {
tower: 'tower.json',
car: 'car.json'
};
// Storage for loaded geometries
myModels = {
tower: null,
car: null
};
// Loader, loads only first time from file then from storage
var load = function( modelName ){
if( myModels[modelName] === null ){
loader.load(myFiles[modelName], function (geometry) {
myModels[modelName] = geometry;
});
}
var model = new THREE.Mesh(myModels[modelName], material)
scene.add( model );
}
You can rework it how you think is best.
I am building a small music visualizer with WebGL and Three.js, using the ThreeAudio.js library to convert the audio into a texture that is passed into the shader. Though everything is currently functioning, I am getting the following error that I'd like to track down:
"Uncaught Type Error: Type error"
Which then traces back from my animate function, to my render function, to the three.js render function, to something called "l", to renderBuffer, to "z".
My animate function is as follows:
function animate() {
requestAnimationFrame( animate );
audioTextures.update();
stats.update();
render();
}
And my render function is as follows:
function render(){
renderer.render( scene, camera );
}
I believe it's an issue with the mesh I am creating, because when I comment out the code to add it to the scene the error goes away.
The code for the animated sphere I have is as follows:
audioSource = (new ThreeAudio.Source()).load('https://api.soundcloud.com/tracks/125098652/stream?client_id=MYCLIENTID').play();
audioTextures = new ThreeAudio.Textures(renderer, audioSource);
audioMaterial = new ThreeAudio.Material(audioTextures, vertexShader, fragmentShader);
audioMesh = new THREE.Mesh(geometry, audioMaterial);
scene.add(audioMesh);
The ThreeAudio github can be found here: https://github.com/unconed/ThreeAudio.js
Please let me know if it would be helpful to post my shaders as well.
Does anyone know how I should begin to solve this error? Has anyone seen it present in this way? Please let me know.
Ok, I ended up answering this question myself. In case anyone stumbles upon this looking for the answer to a similar problem, this is how I fixed it:
I realized that the animate function was being called before the audio source was loaded, which meant the audio textures did not have any data in them. It seemed like the library (ThreeAudio) that I was using handled the exception, in my limited experience issues with data getting to the shader tends to blow everything up.
The solution was to move the animate() call into a callback function once the audio source loaded. My code looks like this:
audioSource = (new ThreeAudio.Source()).load('https://api.soundcloud.com/tracks/125098652/stream?client_id=MYCLIENTID', function(){
clock = new THREE.Clock();
audioSource.play();
audioTextures = new ThreeAudio.Textures(renderer, audioSource);
audioMaterial = new ThreeAudio.Material(audioTextures, vertexShader, fragmentShader);
audioMaterial.uniforms['volume'] = {type: 'f', value: 0};
audioMaterial.uniforms['volume2'] = {type: 'f', value: 0};
audioMaterial.uniforms['volume3'] = {type: 'f', value: 0};
// My custom cube geometry
var geometry = CubeGeometry(audioTextures, 500, 500, 500, 500, 500);
audioMesh = new THREE.Mesh(geometry, audioMaterial);
audioMesh.position.y = -200;
scene.add(audioMesh);
animate();
});