Three.js | Not able to add Dynamic Text Geometry to 3d mesh - three.js

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

Related

Problem in Mesh Rotation around another Mesh of imported FBX model in Three.js

For the learning purposes, I downloaded a layalty-free FBX model from a website, which happens to be a helicopter. I want to emulate the rotation of the helicopter blades programmatically in Three.js. I imported the moded successfully by means of FBXLoader, without any problem. I checked its meshes in Blender, and it has more than fifty meshes. I pinpointed the blades' meshes and wrote this in the load() function:
pivotPoint = new THREE.Object3D();
const loader = new THREE.FBXLoader();
group = new THREE.Object3D();
loader.load(
'Apache.fbx',
object => {
scene.add(object);
const twentyFive = scene.getObjectByName('Mesh25'); //This is the shaft which the blades should rotate around
console.log(twentyFive); //x: 685.594482421875, y: 136.4067840576172, z: -501.9534606933594
twentyFive.add(pivotPoint);
const twentyEight = scene.getObjectByName('Mesh28');//These four are the blades
const twentyNine = scene.getObjectByName('Mesh29');
const twentySeven = scene.getObjectByName('Mesh27');
const twentySix = scene.getObjectByName('Mesh26');
group.add(twentyEight);
group.add(twentyNine);
group.add(twentySeven);
group.add(twentySix);
pivotPoint.add(group);
scene.add(pivotPoint);
scene.add(twentyFive);
},
progress => ...,
error => ...
);
and the following in the loop render function:
pivotPoint.rotation.y += 0.01;
However, either the four blades disappear once I add the nesting Object3Ds or upon changing the code into the above version with numerous mutations, the four blades would strangely rotate around some other point in sky, apart from the fuselage, while the awe-stricken pilot watches the catastrophe and amazed by the aforementioned code, as if the helicopter is about to crash any second!
I tried many changes to the code. Basically I had once used the Object3D parenting for some light sources on another scene, but have no idea what's the issue now. Besides, the rotation of the blades around Mesh25 (my wished pivot) is around a big circle with no contacts with the fuselage, although all four are beautifully revolve around their center of mass.
I really appreciate any help, as I really need to learn to wrestle with similar imported models.
Use attach instead of add in the appropriate places.
const twentyFive = scene.getObjectByName('Mesh25');
// add the pivot and group first so they are in the scene
pivotPoint.add(group);
twentyFive.add(pivotPoint);
const twentyEight = scene.getObjectByName('Mesh28');
const twentyNine = scene.getObjectByName('Mesh29');
const twentySeven = scene.getObjectByName('Mesh27');
const twentySix = scene.getObjectByName('Mesh26');
// use attach to move something in the scene hierarchy without
// changing its position
group.attach(twentyEight);
group.attach(twentyNine);
group.attach(twentySeven);
group.attach(twentySix);
This assumes the model is created correctly in the first place and that the the shaft's position Mesh25 is in the center of the shaft.
Note: If the shaft's origin is in the correct position and the blades are already children of the shaft you can just rotate the shaft.

threejs - creating 3d perspective for a line

I'm working on an app where I visualize ATV trails in a 3d perspective (NAIP imagery draped over elevation data). I am using three.js for the rendering engine.
In the above image, the white line you see is just a THREE.Line instance, where I convert a trails gps coordinates into threejs coordinates. I'd like to add more of 3d perspective to this line. I tried implementing a THREE.TubeGeometry where the path was a THREE.CatmullRomCurve3 using the same Vector3 points as how I built the line you see in the image above. That did not produce a desirable result...
From the many, many THREE examples I have looked at, I really think an extruded geometry would achieve the look I am after... But I cant for the life of me figure out how to extrude a geometry for the line. Any suggestions/thoughts?
UPDATE 1:
Here is my desired look (same trail - no imagery). This image was produced in QGIS using the Q2Threejs plugin
UPDATE 2: Here is a code of how I have attempted to create a tubegeometry. Maybe I am messing something up in there...
// trailVectors are an array of Vector3 - same as ones used to create line
var trailCurve = new THREE.CatmullRomCurve3(trailVectors);
var tubeGeometry = new THREE.TubeGeometry(trailCurve,80,1,15,false);
var material = new THREE.MeshBasicMaterial({color:0x00ff00});
var tubeMesh = new THREE.Mesh(tubeGeometry,material);
var wireframeMaterial = new THREE.LineBasicMaterial({color:0xffffff,lineWidth:2});
var wireframe = new THREE.Mesh(tubeGeometry,wireframeMaterial);
tubeMesh.add(wireframe);
scene.add(tubeMesh);
UPDATE 3
THREE.TubeGeometry(trailCurve,80,4,2,false) per mzartman request
I think that you should be able to achieve what you want with a TubeGeometry. I think the big thing is that your example (from the picture shown) has more than 2 radius segments. That gives it the tubular shape and makes it look sort of like a blob. If you set the radial segment count to 2 (as it's shown below) then I think it would look a lot better.
tubeGeometry = new THREE.TubeBufferGeometry(
[YOUR_PATH_HERE],
params.extrusionSegments, // <--- Edit this for higher resolution on the spline
3, // <--- This defines the height
2, // <--- This 2 keeps 2D (i.e. not a tube!!!!)
true );
var mesh = new THREE.Mesh( geometry, material );
var wireframe = new THREE.Mesh( geometry, wireframeMaterial );
mesh.add( wireframe );
scene.add( mesh );
Update:
I think that you might do better with a material that shows some shadow like the MeshPhong. Also, to do the wireframe you want to add it as an option in the material initialization. Give it a show with the following:
var tubeGeometry = new THREE.TubeGeometry(curve,80,1,2,false);
var material = new THREE.MeshPhongMaterial({color:0x00ff00, wireframe: true});
var tubeMesh = new THREE.Mesh(tubeGeometry,material);
scene.add(tubeMesh);

ThreeJS apply properties from one camera to another camera

In my webapp I'm using ThreeJS scenes in different modals/popups/dialogs with different width/height ratios.
Furthermore, I want to use multiple user defined camera settings (rotation, position, lookAt etc.) among these different scenes.
Therefore, I save the camera object via camera.toJSON() when the user clicks a capture camera settings button.
(Before I did this, I saved just the object camera, but unfortunately these objects are quite big and slow down the performance while multiple camera objects get stored. Nevertheless, this approach worked, since I was able to copy all the desired values between the saved camera object and the currently used camera [e.g. current_camera.position.x=saved_camera.position.x and so on])
In every scene I want now to use the saved properties I tried the following:
let m = new THREE.Matrix4();
m.fromArray(saved_camera.object.matrix);
current_camera.applyMatrix(m)
current_camera.updateMatrix();
Unfortunately this doesn't work.
"normal" camera object
camera.toJSON() object
If you're comfortable using matrices, then you can turn off the matrix auto-update that three.js does during the render process, and keep the world matrix up-to-date yourself. (This includes any time you change the camera's orientation, so keep that in mind if you're using some form of mouse interaction to control the camera angle.)
First, turn off automatic matrix updating for your camera by setting the autoUpdateMatrix property to false. You can still use the convenience properties (position, rotation, scale), but you'll have to manually update the world matrix by calling camera.updateMatrixWorld(true);.
Finally, when you're ready to restore a particular camera orientation, simply copy the matrix values using the matrixWorld's copy method.
var origin = new THREE.Vector3();
var theCamera = new THREE.PerspectiveCamera(35, 1, 1, 1000);
theCamera.autoUpdateMatrix = false; // turn off auto-update
theCamera.position.set(10, 10, 10);
theCamera.lookAt(origin);
theCamera.updateMatrixWorld(true); // manually update the matrix!
console.log("Camera original matrix: ", theCamera.matrixWorld.elements.toString());
var saveMatrix = new THREE.Matrix4();
saveMatrix.copy(theCamera.matrixWorld);
// saveMatrix now contains the current value of theCamera.matrixWorld
theCamera.position.set(50, -50, 75);
theCamera.lookAt(origin);
theCamera.updateMatrixWorld(true); // manually update the matrix!
console.log("Camera moved matrix: ", theCamera.matrixWorld.elements.toString());
// theCamera.matrixWorld now holds a value that's different from saveMatrix.
theCamera.matrixWorld.copy(saveMatrix);
// Don't upate the matrix, because you just SET it.
console.log("Camera moved matrix: ", theCamera.matrixWorld.elements.toString());
// theCamera.matrixWorld once again contains the saved value.
<script src="https://threejs.org/build/three.js"></script>
Edit to address OrbitControls:
It looks like OrbitControls uses the convenience properties, rather than gathering the information from the matrix. As such, when you restore a camera position, you'll also need to restore those properties. This is easily done by using decompose on the matrix, and copying the resulting values into the appropriate properties:
var d = new THREE.Vector3(),
q = new THREE.Quaternion(),
s = new THREE.Vector3();
camera.matrixWorld.decompose( d, q, s );
camera.position.copy( d );
camera.quaternion.copy( q );
camera.scale.copy( s );

Outline object (normal scale + stencil mask) three.js

For some time, I've been trying to figure out how to do an object selection outline in my game. (So the player can see the object over everything else, on mouse-over)
This is how the result should look:
The solution I would like to use goes like this:
Layer 1: Draw model in regular shading.
Layer 2: Draw a copy in red color, scaled along normals using vertex shader.
Mask: Draw a black/white flat color of the model to use it as a stencil mask for the second layer, to hide insides and show layer 1.
And here comes the problem. I can't really find any good learning materials about masks. Can I subtract the insides from the outline shape? What am I doing wrong?
I can't figure out how to stack my render passes to make the mask work. :(
Here's a jsfiddle demo
renderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, renderTargetParameters)
composer = new THREE.EffectComposer(renderer, renderTarget)
// composer = new THREE.EffectComposer(renderer)
normal = new THREE.RenderPass(scene, camera)
outline = new THREE.RenderPass(outScene, camera)
mask = new THREE.MaskPass(maskScene, camera)
// mask.inverse = true
clearMask = new THREE.ClearMaskPass
copyPass = new THREE.ShaderPass(THREE.CopyShader)
copyPass.renderToScreen = true
composer.addPass(normal)
composer.addPass(outline)
composer.addPass(mask)
composer.addPass(clearMask)
composer.addPass(copyPass)
Also I have no idea whether to use render target or renderer for the source of the composer. :( Should I have the first pass in the composer at all? Why do I need the copy pass? So many questions, I know. But there are just not enough resources to learn from, I've been googling for days.
Thanks for any advice!
Here's a js fiddle with working solution. You're welcome. :)
http://jsfiddle.net/Eskel/g593q/6/
Update with only two render passes (credit to WestLangley):
http://jsfiddle.net/Eskel/g593q/9/
The pieces missing were these:
composer.renderTarget1.stencilBuffer = true
composer.renderTarget2.stencilBuffer = true
outline.clear = false
Now I think I've found a bit simpler solution, from the THREEx library. It pre-scales the mesh so you dont need a realtime shader for it.
http://jeromeetienne.github.io/threex.geometricglow/examples/geometricglowmesh.html

Stretch image texture to fit mesh face in Three.js

I'm trying to make the following in three.js:
I made the model in sketchup with some simple coloured textures and used the collader importer, the result looks like this:
Now I want to dynamically load some photographs onto each of the different planes, however what I end up with is this:
So as you can see, each image is loaded but they are very small and repeated across the rest of the surface.
This is how I load the textures: (preloadTexture() is just a simple preloader)
for(i in cubeSidesArray)
{
preloadTexture(modelThumbsArray[i]);
var newTexture = new THREE.MeshPhongMaterial( { map: THREE.ImageUtils.loadTexture(modelThumbsArray[i]) } );
cubeSidesArray[i].material = newTexture;
}
How do I get the textures to fill the surface?
Thanks!
Edit - I played with the model in sketchup and managed to get it a little better, but not much!
Edit 2 - Still no luck, I'm starting to think building it in code from scratch would be simpler
Option 1: I would advise you to do or next.
1 -.Import the model blender
2 -.Export blender to threejs
3 -.Use this method of charging.
AgregarModeloBlender function (geometry, materials) {
console.log(materials);
material = new THREE.MeshFaceMaterial( materials );
modelo3d_ = new THREE.Mesh( geometry,material );
escenario.add(modelo3d_);
modelo3d_.add(camera);
modelo3d_.scale.set(5,5,5);
modelo3d_.position.set(-900,25,850);
modelo3d_.rotation.y=Math.PI;
}
4 -. Subsequently trabajr with textures independently.
Example: http://all.develoteca.com/builder/
Option 2: I would advise you to do or this:
1 -. Create the geometric shape (vertices) to modify each of the faces of the texture.
Example: http://develoteca.com/Panel/
Greetings.

Resources