Three.js the model is abnormally large - three.js

Add the GLTF model into the scene, traverse the node, add a water surface map, and the water is abnormal
gltf.scene.traverse((node) => {
if (node.name == "CNC-water") {
const water = new Water(node.geometry, {
scale: 2,
textureWidth: 1024,
textureHeiht: 1024,
color: 0x6495ed,
reflectivity: 0.3,
normalMap0: new THREE.TextureLoader().load("../src/assets/water/Water_1_M_Normal.jpg"),
normalMap1: new THREE.TextureLoader().load("../src/assets/water/Water_2_M_Normal.jpg"),
});
scene.add(water);
objects.push(water)
}
})
After that, the size of "CNC-water" is normal, but the "water" becomes very large
The red interior is the building

Related

Meshes culled after buffer geom vertex positions updated

Some objects are being frustrum culled after I amend their buffer geometry vertex positions. I don't want to set frustrumCulled = false, nor can I (feasibly) change the mesh position to anything other than (0,0,0). What options do I have here? My expectation is that the meshes should behave consistently with each other. I'm obviously missing something here.
Demo:
2 Meshes, one green, one red
Green mesh is initialised with position 0,0,0 with vertices around 0,10,0
Red mesh is initialised with position 0,0,0 with vertices around 0,0,0, but after 2 seconds, vertices are updated to around 0,10,0
Orbit controls allow zooming (Just zoom in see see actual behaviour)
Expected behaviour: Both meshes are visible when I zoom / move the camera. Or at least, they both behave in the same way
Actual behaviour, green mesh behaves as expected, red mesh is sometimes culled
const OrbitControls = THREE.OrbitControls // CDN shim
// Helper to create mesh with buffer geom
const createGeo = (initialY, color) => {
const geometry = new THREE.BufferGeometry()
const vertices = new Float32Array([
0, initialY, 0,
0, initialY + 1, 0,
0, initialY, 1,
0, initialY, 0,
0, initialY + 1, 0,
1, initialY, 0
])
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
const material = new THREE.MeshBasicMaterial({
color: color,
side: THREE.DoubleSide
})
mesh = new THREE.Mesh(geometry, material)
// Note: Setting frustumCulled to false always allows this to be seen, but I don't this object to always be seen
// mesh.frustumCulled = false
scene.add(mesh)
return mesh
}
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 100)
let controls
const greenMesh = createGeo(10, 0x00FF00) // Set Green at 0,10,0
const redMesh = createGeo(0, 0xFF0000) // Set red at 0,0,0, then update to 0,10,0
const renderer = new THREE.WebGLRenderer({antialias: true})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setAnimationLoop(() => {
controls.update()
greenMesh.rotation.y += 0.1
redMesh.rotation.y -= 0.1
renderer.render(scene, camera)
})
document.body.appendChild(renderer.domElement)
controls = new OrbitControls(camera, renderer.domElement)
camera.position.set(15, -20, 15)
controls.target.set(0, 10, 0)
window.onresize = function() {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
scene.add(new THREE.AxesHelper(100))
// Update red mesh to same position as green mesh
setTimeout(() => {
redMesh.geometry.attributes.position.setXYZ(0, 0, 10, 0)
redMesh.geometry.attributes.position.setXYZ(1, 0, 11, 0)
redMesh.geometry.attributes.position.setXYZ(2, 0, 10, 1)
redMesh.geometry.attributes.position.setXYZ(3, 0, 10, 0)
redMesh.geometry.attributes.position.setXYZ(4, 0, 11, 0)
redMesh.geometry.attributes.position.setXYZ(5, 1, 10, 0)
// As per - https://threejs.org/docs/#manual/en/introduction/How-to-update-things
redMesh.geometry.attributes.position.needsUpdate = true
redMesh.geometry.computeBoundingBox()
// When zooming / panning, red mesh is not always visible
}, 2000)
<script src="https://cdn.jsdelivr.net/npm/three#0.147.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.147.0/examples/js/controls/OrbitControls.min.js"></script>
As mentioned by #kikon, both
mesh.geometry.computeBoundingBox()
mesh.geometry.computeBoundingSphere()
are required. Whilst it doesn't explicitly state these in this docs, they have to both be used when updating vertex positions (I had made the incorrect assumption that you would use either the box or sphere).
The code for frustrum culling uses the bounding sphere, and a bounding sphere is automatically created once first invoked (hence the difference in red / green mesh behaviour), and has to be recomputed and both methods need to be called in this case.

Threejs issue with CSG substract rendering

I try to substract text geometry from an mesh which has been previously omported (GLB with GLTFLoader).
Unfortunatly object seems not intersect rather than when I add the text geometry in my scene for debugging I see it properly and my mesh should have removed part.
See photo below:
Image 1
But I got this result:
Image 2
I tried to lower up the size of my font, and with x100 ratio I got the result below (here the mesh has removed part but I do not know what is refering to):
Image 3
The code:
let font
function engraving() {
const loaderFont = new FontLoader()
loaderFont.load('fonts/helvetiker_regular.typeface.json', function (f) {
font = f
regenerateGeometry()
})
}
function regenerateGeometry() {
let newGeometry
newGeometry = new TextGeometry("AAAAAAAAA", {
font: font,
size: 0.003,
height: 0.003,
curveSegments: 2,
})
newGeometry.center()
//bender.bend(newGeometry, 'y', Math.PI / 16)
newGeometry.translate(0, 0, 0)
const material = new THREE.MeshStandardMaterial({
envMap: '',
metalness: 1.0,
roughness: 0.0,
color: 0xffd700,
})
const textCSG = CSG.fromGeometry(newGeometry)
var engraved = engravedCSG.subtract(textCSG)
engravedMesh.geometry.dispose()
engravedMesh.geometry = CSG.toMesh(
engraved,
new THREE.Matrix4()
).geometry
}
Looking forward for some advise

SpriteMaterial with ThreeJs | How can i set the Sprite position to stay with mesh?

I need to set the Sprite(Hello) position to stay with the mesh...Now, the Sprite is independent. If I rotate the object, the Sprite should stay with the mesh
LATER EDIT:
Mesh position is always 0..
I tried for couple of hours but nothing worked..
Here is a screenshot of the code:
And the code...
loader.load('/3D_Cars/Dacia_1410_Tuned/components/scene.gltf', function (gltf) {
carModel = gltf.scene.children[0];
gltf.scene.traverse(function (child) {
if (child.isMesh) {
var v = new THREE.Vector3(0, 0, 0);
domEvents.addEventListener(carModel.getObjectByName(child.name), 'click', function (event) {
app.RenameAsCarParts(child.name)
console.log(child.name)
}, false)
if (child.name == 'rim_RSL1_Aluminium_0') {
child.position.set(100, 50, 3); //testing
var spritey = makeTextSpriteNew(" Hello ",
{ fontsize: 15, textColor: { r: 0, g: 0, b: 0, a: 1.0 } });
scene.add(spritey);
}
}
});
carModel.getObjectByName('Body_paint_0').material = bodyMaterial;
carModel.rotation.set(Math.PI / 2, 0, 0);
carModel.rotateY(Math.PI);
scene.add(carModel);
});
Thank you!
Where is this method defined makeTextSpriteNew?
You can refer this three.js example for drawing Text Sprites in three.js
https://stemkoski.github.io/Three.js/Sprite-Text-Labels.html
The idea is, you need to provide a 3d position to this sprite so that it will project that position on screen and display the Sprite.
In your case you need to provide it the mesh's position (bounding box corner/center based on where you need to show the sprite).
spritey.position.set(posX, posY, posZ);

How to set a part of a box object with a diffrent color in three.js and the part dimension will get dynamically?

I am new to Three.js. This may be very basic question. But I tried to get a solution, unfortunately i didn't get any even from google. I have a 'mesh' with 'box geometry' and applied a 'MeshStandardMaterial' material. Specified a color in the material. Now I want show the part of this box is filled with some other color.This percentage of part will be dynamically.For example half of water filled in the glass the quantity of water will be get dynamically.
I want to represent level of some filling.Another option to make some part of this box is transparent that is also fine.
let cube_geometry = new THREE.BoxGeometry(tankWidth, tankHeight, tankDepth);
let ct_material = new THREE.MeshStandardMaterial({
color: colourCode,
metalness: 1.0,
roughness: 0.5,
transparent: true,
opacity: params['CT Opacity'],
alphaTest: 0.1
});
cube.name = name;
let cube = new THREE.Mesh(cube_geometry , ct_material);
group_ct.add(cube);//will add set of boxes to this group
//---------------------------
function SetQty(qty)
{
group_ct.traverse(function (tank) {
if (tank instanceof THREE.Mesh) {
tankName = tank.name;
qty =GetQty(tankName);
if(qty > 0) {
//want to display filled level inside the box as per the quantity
}
}
There are a couple ways to do this.
The first and easiest is to create two cubes of your desired material, and scale them along the same axis. The difficulty there is that you would also need to recompute their position so they maintain the same barrier edge for the varying values of "percentage filled".
Another option that requires fewer calculations (but is more conceptually complex) is to employ Material.clippingPlanes (example), and render your geometry using two different materials/meshes.
In the example below, I use the same cube geometry for both Mesh objects, but I use different materials with clipping planes pointing in opposite directions. I update their displacement in the render loop, which has the effect of changing the "fill".
// three.js r110
const W = window.innerWidth
const H = window.innerHeight
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
})
renderer.setSize(W, H)
document.body.appendChild(renderer.domElement)
// turn on clipping!
renderer.localClippingEnabled = true
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(28, W / H, 1, 1000)
camera.position.set(10, 20, 50)
camera.lookAt(scene.position)
scene.add(camera)
const light = new THREE.PointLight(0xffffff, 1)
//scene.add(light)
camera.add(light)
// define the clipping planes
const emptyClip = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)
const fillClip = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0)
const boxGeo = new THREE.BoxGeometry(10, 10, 10)
const emptyMaterial = new THREE.MeshStandardMaterial({
color: 0x55ddff,
metalness: 0,
roughness: 0.2,
transparent: true,
opacity: 0.5,
side: THREE.DoubleSide,
clippingPlanes: [emptyClip]
})
const filledMaterial = new THREE.MeshStandardMaterial({
color: 0xff5555,
metalness: 0,
roughness: 0.2,
side: THREE.DoubleSide,
clippingPlanes: [fillClip]
})
const emptyMesh = new THREE.Mesh(boxGeo, emptyMaterial)
scene.add(emptyMesh)
const filledMesh = new THREE.Mesh(boxGeo, filledMaterial)
scene.add(filledMesh)
// layer separator
const separatorGeo = new THREE.PlaneGeometry(10, 10)
const separatorMat = new THREE.MeshStandardMaterial({
color: 0xff5555,
metalness: 0,
roughness: 0.2,
side: THREE.DoubleSide
})
const separator = new THREE.Mesh(separatorGeo, separatorMat)
separator.rotation.x = Math.PI / 2
scene.add(separator)
let count = 0
function animate() {
requestAnimationFrame(animate)
// compute the clipping plane displacement
let displacement = Math.sin(count) * 0.5 // min = 25%, max = 75%
displacement *= 5 // each cube side is 5 units
// set the displacement of the planes (relative to their normals!)
emptyMaterial.clippingPlanes[0].constant = displacement
filledMaterial.clippingPlanes[0].constant = -displacement
// update the separator
separator.position.y = -displacement
renderer.render(scene, camera)
count += 0.05
}
animate()
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>

three.js - apply different material to extruded shape

I have Three.Shape which I can apply Extrusion
To get a plane I drew a square first and then apply extrusion
var squareShape = new THREE.Shape();
squareShape.moveTo( 0,0 );
squareShape.lineTo( 0, sqLength );
squareShape.lineTo( sqLength, sqLength );
squareShape.lineTo( sqLength, 0 );
squareShape.lineTo( 0, 0 );
var geometry = new THREE.ExtrudeGeometry( squareShape,extrudeSettings);
now a 3D square is appearing on my browser.
Next I want to apply a image texture of 256x256 on its top face. How to do this?
If you look at src/extras/geometries/ExtrudeGeometry.js in THREE.js you will see these comments.
material: // material index for front and back faces
extrudeMaterial: // material index for extrusion and beveled faces
So for example you can say (obviously won't run directly because it is incomplete)
// The face material will be index #0. The extrusion (sides) will be #1.
var extrudeSettings = { amount: 10, bevelEnabled: true, bevelSegments: 3, steps: 4, bevelThickness: 8, material: 0, extrudeMaterial: 1 };
And when you create your mesh you create 2 materials and assign them to your mesh.
var mesh = THREE.SceneUtils.createMultiMaterialObject( geometry, [ new THREE.MeshLambertMaterial( { color: color } ), new THREE.MeshBasicMaterial( { color: 0x000000, wireframe: true, transparent: true } ) ] );
Hope this gets you on the right path.
On a side note, related to the other answer, you make want to use extrude to create something that dimensionally is similar to a square but has bevels. One example would be if you were trying to draw dice and wanted to round the edges.
If all you want is a cube why are you extruding? Why don't you use the CubeGeometry.

Resources