Get he mesh from the DracoLoader of ThreeJS - three.js

I work on the Draco loader of ThreeJS but I encounter a problem with the loader. I actually need the mesh of the dracofile load but the mesh is not available after i load it with this code:
let mesh;
const loader = new DRACOLoader();
loader.setDecoderPath('./Three/examples/js/libs/draco/')
loader.load('PATHTO drc file', function(geometry) {
var material = new THREE.PointsMaterial({ size: 0.05 });
material.vertexColors = false
mesh = new THREE.Points(geometry, material);
mesh.position.set(0, 0, -1);
});
console.log(mesh) // print undifined
I need the mesh to perform some operations do you have some clues for this problem ?

The loading callback runs asynchronously, so the line immediately afterward runs first and the mesh is defined only later. There are many ways to manage asynchronous programming (I'd suggest https://eloquentjavascript.net/11_async.html as an introduction) but a simple solution in this case would be to put the later code into a callback:
let mesh;
...
loader.load('...', (geometry) => {
mesh = ...
start();
});
...
function start() {
console.log(mesh);
}

Related

Attempts to load a texture show no error but the texture does not display

I have a model, a background sky and a ground surface. Texturing the surface results in no surface.
I've tried the basic approach and come to the conclusion that it is probably that the scene is being rendered before the texture has finished loading. Having searched and found various possible solutions, I have tried several of them, without really understanding how they are supposed to work. None of them has worked. One problem is that it is an old problem and most of the suggestions involve outdated versions of the three.js library.
// Ground
// create a textured Ground based on an answer in Stackoverflow.
var loader = new THREE.TextureLoader();
loader.load('Textures/Ground128.jpg',
function (texture) {
var groundGeometry = new THREE.PlaneBufferGeometry(2000, 2000, 100, 100);
const groundMaterial = new THREE.MeshLambertMaterial({map: texture});
var ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.receiveShadow = true; //Illumination addition
ground.rotation.x = -0.5 * Math.PI; // rotate into the horizontal.
scene.add(ground);
}
);
// This variation does not work either
http://lhodges.users37.interdns.co.uk/me/downloads/Aphaia/Temple.htm
http://lhodges.users37.interdns.co.uk/me/downloads/Aphaia/Temple7jsV0.15b.htm
The first of the above is the complete page in which the ground is a plain billiard table green. The second is the page containing the above code.
There appear to be no error (Last time I tried.)
By the time your texture loads and you add the ground, your scene has already rendered (and there is no other render call).
You need to call renderer.render(scene, camera); after adding the ground to the scene.
// Ground
// create a textured Ground based on an answer in Stackoverflow.
var loader = new THREE.TextureLoader();
loader.load('Textures/Ground128.jpg',
function (texture) {
var groundGeometry = new THREE.PlaneBufferGeometry(2000, 2000, 100, 100);
const groundMaterial = new THREE.MeshLambertMaterial({map: texture});
var ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.receiveShadow = true; //Illumination addition
ground.rotation.x = -0.5 * Math.PI; // rotate into the horizontal.
scene.add(ground);
renderer.render(scene, camera); // <--- add this line
}
);

Using OutlinePass (THREE.js r102) with skinned mesh

/examples/js/postprocessing/OutlinePass.js from THREE.js r102 does not appear to work with skinned meshes. Specifically, the rendered outline always stays in the mesh's rest position.
Is there some way to get this working (that is, to update the outline to reflect the current pose of an animated mesh)? OutlinePass does not appear to be documented (mod the comments in the code itself).
Is there some other accepted method of outlining animated meshes? I'm in the process of migrating some code from r7x, where I ended up accomplishing this by manually creating a copy of the mesh and applying a shader material that scales along the normals. I can do that again, but if there's a simpler/better supported method to accomplish the same effect I'd rather use it instead of reproducing a method that breaks every new major release.
A simple jsfiddle illustrating the issue:
https://jsfiddle.net/L69pe5q2/3/
This is the code from the jsfiddle. The mesh I use is the SimpleSkinning.gltf example from the three.js distribution. In the jsfiddle I load it from a dataURI so it doesn't complain about XSS loading, and I've edited the base64-encoded data out (and replaced it with [FOO]) in the code below, purely for readability.
The OutlinePass is created and added to the composer in initComposer().
var camera, light, renderer, composer, mixer, loader, clock;
var scene, mesh, outlinePass;
var height = 480,
width = 640;
var clearColor = '#666666';
load();
function load() {
loader = new THREE.GLTFLoader();
clock = new THREE.Clock();
scene = new THREE.Scene();
loader.load('data:text/plain;base64,[FOO]', function(obj) {
scene.add(obj.scene);
mixer = new THREE.AnimationMixer(obj.scene);
var clip = THREE.AnimationClip.findByName(obj.animations,
'Take 01');
var a = mixer.clipAction(clip);
a.reset();
a.play();
mesh = obj.scene;
mesh.position.set(-7, 2.5, -7);
init();
animate();
});
}
function init() {
initCamera();
initScene();
initRenderer();
initComposer();
outlinePass.selectedObjects = [mesh];
}
function initCamera() {
camera = new THREE.PerspectiveCamera(30, width / height, 1, 10000);
camera.position.set(7, 0, 7);
camera.lookAt(0, 0, 0);
}
function initScene() {
light = new THREE.AmbientLight(0xffffff)
scene.add(light);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({
width: width,
height: height,
antialias: false,
});
renderer.setSize(width, height);
renderer.setClearColor(clearColor);
document.body.appendChild(renderer.domElement);
}
function initComposer() {
var renderPass, copyPass;
composer = new THREE.EffectComposer(renderer);
renderPass = new THREE.RenderPass(scene, camera);
composer.addPass(renderPass);
outlinePass = new THREE.OutlinePass(new THREE.Vector2(width, height),
scene, camera);
composer.addPass(outlinePass);
outlinePass.edgeStrength = 10;
outlinePass.edgeThickness = 4;
outlinePass.visibleEdgeColor.set('#ff0000');
copyPass = new THREE.ShaderPass(THREE.CopyShader);
copyPass.renderToScreen = true;
composer.addPass(copyPass);
}
function animate() {
var delta = clock.getDelta();
requestAnimationFrame(animate);
update(delta);
render(delta);
}
function update(delta) {
if (mixer) mixer.update(delta);
}
function render(delta) {
composer.render();
}
according to Mugen87 in Jan 2019 he said:
With this small patch, it's now possible to use the outline pass with animated meshes. The only thing users have to do at app level is to set morphTargets or skinning to true for OutlinePass.depthMaterial and OutlinePass.prepareMaskMaterial. That's of course still a manual effort but at least the more complicated shader enhancement is already done.
take this example:
https://jsfiddle.net/2ybks7rd/
reference link on github

Threejs DataTexture Not Updating

UPDATE: Issue was that texData object was recreated each time and thus reference for DataTexture was lost. Solution by WestLangley was to overwrite the data in texData instead of recreating texData object.
I have a simple threejs scene with a DataTexture in a ShaderMaterial. The data array passed to it once during initialization is updated on mouse events. However the DataTexture does not seem to update.
Did i assign uniforms or texture data wrongly? Or using the needsUpdate flags wrongly? It does work when deleting and recreating the texture, material, mesh and scene objects each time, but this shouldnt really be necessary as i have seen from many examples which i could however not reproduce.
Note that the data itself is updated nicely, just not the DataTexture.
// mouse event triggers request to server
// server then replies and this code here is called
// NOTE: this code **is** indeed called on every mouse update!
// this is the updated data from the msg received
// NOTE: texData **does** contain the correct updated data on each event
texData = new Float32Array(evt.data.slice(0, msgByteLength));
// init should happen only once
if (!drawContextInitialized) {
// init data texture
dataTexture = new THREE.DataTexture(texData, texWidth, texHeight, THREE.LuminanceFormat, THREE.FloatType);
dataTexture.needsUpdate = true;
// shader material
material = new THREE.ShaderMaterial({
vertexShader: document.querySelector('#vertexShader').textContent.trim(),
fragmentShader: document.querySelector('#fragmentShader').textContent.trim(),
uniforms: {
dataTexture: { value: dataTexture }
}
});
// mesh with quad geometry and material
geometry = new THREE.PlaneGeometry(width, height, 1, 1);
mesh = new THREE.Mesh(geometry, material);
// scene
scene = new THREE.Scene();
scene.add(mesh);
// camera + renderer setup
// [...]
drawContextInitialized = true;
}
// these lines seem to have no effect
dataTexture.needsUpdate = true;
material.needsUpdate = true;
mesh.needsUpdate = true;
scene.needsUpdate = true;
renderer.render(scene, camera);
When updating the DataTexture data, do not instantiate a new array. Instead, update the array elements like so:
texData.set( javascript_array );
Also, the only flag you need to set when you update the texture data is:
dataTexture.needsUpdate = true;
three.js r.83
I had a real hard time for some reason seeing any change to modifactions . In desperation i just made a new DataTexture . Its important to set needsUpdate to true
imgData.set(updatedData)
var newDataTex = new THREE.DataTexture( imgData,...
var newDataTex.needsUpdate = true
renderMesh.material.uniforms.texture.value = newDataTex

I'm new to threejs, how to create a sky dome

I'm pretty new to three.js and I tried for hours to create a skybox/skydome for a better visual feeling to my world (in this case space). Googled, checked tutorials, asked here on StackOverflow. And nothing worked, or I got a silly and dumb answer here on SO. Question is simple: how to make a skybox/dome?
This is how you do a skydome in threejs.
var skyGeo = new THREE.SphereGeometry(100000, 25, 25);
First the geometry. I wanted it big and made it big
var loader = new THREE.TextureLoader(),
texture = loader.load( "images/space.jpg" );
Loads the texture of your background space. One thing here is that you need it to run through a server to be able to load the texture. I use wamp or brackets preview.
Create the material for the skybox here
var material = new THREE.MeshPhongMaterial({
map: texture,
});
Set everything together and add it to the scene here.
var sky = new THREE.Mesh(skyGeo, material);
sky.material.side = THREE.BackSide;
scene.add(sky);
This might not be the best solution for this, but it´s easy specially for a beginner in threejs. Easy to understand and create.
This is how you can load image as texture and apply that on innerside of a sphere geometry to emulate skydome.
Complete solution with error callback for future reference
//SKY
var loader = new THREE.TextureLoader();
loader.load(
"./assets/universe.png",
this.onLoad,
this.onProgress,
this.onError
);
onLoad = texture => {
var objGeometry = new THREE.SphereBufferGeometry(30, 60, 60);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
objMaterial.side = THREE.BackSide;
this.earthMesh = new THREE.Mesh(objGeometry, objMaterial);
scene.add(this.earthMesh);
//start animation
this.start();
};
onProgress = xhr => {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
};
// Function called when download errors
onError = error => {
console.log("An error happened" + error);
};

How can I change the texture in a Blender model loaded into three.js, after it's been loaded, with ImageUtils.loadTexture?

I'm doing a student project at involves a gift box where users can change how it looks.
I started learning what to do by making a cube, importing a texture and setting a gui.dat control to allow the user to change the texture.
I'm now trying to replace the cube with a blender model of a gift box but I'm having trouble changing the texture.
EDIT: The full code is on github here:
https://github.com/GitKiwi/GiftBox/blob/master/Workspace/Proto%208c%20Changing%20textures%20on%20giftbox.html
The coding for the working cube model is:
`// add cube with texture
var cubeGeometry = new THREE.CubeGeometry(4,4,4);
var cubeMaterial = new THREE.MeshLambertMaterial({ map:
THREE.ImageUtils.loadTexture("birthday.jpg") });
var cube = new THREE.Mesh(cubeGeometry,cubeMaterial);
cube.position.set (0,0,0);
cube.rotation.set (0,-1.2,0);
cube.receiveShadow = true;
// add the cube to scene
scene.add(cube); `
//gui texture change
`var controls = new function()
{ this.changeTexture = "birthday";
this.changeTexture = function (e){
var texture = THREE.ImageUtils.loadTexture
("../assets/textures/general/" + e + ".jpg");
cube.material.map = texture; }`
//gui control
var gui = new dat.GUI();
gui.add(controls, "changeTexture", ['christmas', 'valentine', 'birthday']).onChange(controls.changeTexture);
I'm loading the gift box in four parts and I'm just trying to get the first part, the box, to change texture. I load it with:
var box;
var loaderOne = new THREE.JSONLoader();
loaderOne.load('../assets/models/box.js', function (geometry)
{
var material = new THREE.MeshLambertMaterial({color: 0xffff00});
box = new THREE.Mesh(geometry, material);
box.position.set (5,0,5);
box.scale.set (1,1,1);
//box.name = "mybox";
scene.add(box);
});
I can't get it to change texture with the gui control. I've tried changing the "cube" to "box" in the gui texture change code and I've tried naming the box and calling it(commented out in the code above) but those didn't work. I've searched for answers to this in a number of places but I'm just really stuck. I feel I'm perhaps missing something obvious?
Any help to would really be appreciated.
The code wasn't working because there were no texture maps for the model I was importing.
What I did was go back to Blender and create a model with two textures that could each be applied to the whole model. The exported JSON file then had the model geometry and the two textures (with their texture maps).
In three.js I loaded it:
// load in geometry and textures
var loader = new THREE.JSONLoader();
loader.load('../models/two textures on cube.js', function (geometry, material)
{
matOne = new THREE.MeshLambertMaterial(material[1]);
matTwo = new THREE.MeshLambertMaterial(material[2]);
box = new THREE.Mesh(geometry, matOne);
//position, scale
box.position.set (0,0,0);
box.rotation.set (0,-1.2,0);
box.scale.set (2,2,2);
box.receiveShadow = true;
scene.add(box);
}, '../models');
and then used this code to switch the textures:
//gui control panel
var controls = new function()
{
//changing the texture
this.changeTexture = function (e)
{
switch (e)
{
case "birthday":
box.material = matOne;
break;
case "christmas":
box.material = matTwo;
break;
}
}
}
with this code for the gui.dat controls:
//gui control panel
var gui = new dat.GUI();
gui.add(controls, "changeTexture", ['birthday', 'christmas']).onChange(controls.changeTexture);

Resources