var positions = new Float32Array(geometry.__directGeometry.vertices.length * 3);
geometry._bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3).copyVector3sArray(geometry.__directGeometry.vertices));
This is valid
geometry._bufferGeometry.getAttribute('position').copyVector3sArray(geometry.__directGeometry.vertices);
geometry._bufferGeometry.getAttribute('position').needsUpdate = true;
this is invalid, don't know why, three.js v119
Related
My image texture is positioned relative to the center of 3d space instead of mesh and I don't quite understand what determines its size.
Here is example showing how the same image is positioned on different meshes:
https://imgur.com/glHE97L
I'd like the image be in the center of the mesh and it's size set similar as 'contain' in css.
The mesh is flat plane created using ShapeBufferGeometry:
const shape = new THREE.Shape( edgePoints );
const geometry = new THREE.ShapeBufferGeometry( shape );
To see any image I have to set:
texture.repeat.set(0.001, 0.001);
Not sure if that matters but after creating the mesh I than set its position and rotation:
mesh.position.copy( position[0] );
mesh.rotation.set( rotation[0], rotation[1], rotation[2] );
I've tried setting those:
mesh.updateMatrixWorld( true );
mesh.geometry.computeBoundingSphere();
mesh.geometry.verticesNeedUpdate = true;
mesh.geometry.elementsNeedUpdate = true;
mesh.geometry.morphTargetsNeedUpdate = true;
mesh.geometry.uvsNeedUpdate = true;
mesh.geometry.normalsNeedUpdate = true;
mesh.geometry.colorsNeedUpdate = true;
mesh.geometry.tangentsNeedUpdate = true;
texture.needsUpdate = true;
I've played with wrapS / wrapT and offset.
I've checked UV's - I don't yet fully understand this concept but it seems fine. Example of UV for one mesh (I understand those are XY coordinates and they seem to reflect the actual corners of my mesh):
uv: Float32BufferAttribute
array: Float32Array(8)
0: -208
1: 188
2: 338
3: 188
4: 338
5: 12
6: -208
7: 12
I've tried setting:
texture.repeat.set(imgHeight/geometryHeight/1000, imgWidth/geometryWidth/1000);
This is how THREE.ShapeGeometry() computes UV coordinate:
https://github.com/mrdoob/three.js/blob/e622cc7890e86663011d12ec405847baa4068515/src/geometries/ShapeGeometry.js#L157
But you can re-compute them, to put in range [0..1].
Here is an example, click the button to re-compute uvs of the shape geometry:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var grid = new THREE.GridHelper(10, 10);
grid.rotation.x = Math.PI * 0.5;
scene.add(grid);
var points = [
new THREE.Vector2(0, 5),
new THREE.Vector2(-5, 4),
new THREE.Vector2(-3, -3),
new THREE.Vector2(2, -5),
new THREE.Vector2(5, 0)
];
var shape = new THREE.Shape(points);
var shapeGeom = new THREE.ShapeBufferGeometry(shape);
var shapeMat = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg")
});
var mesh = new THREE.Mesh(shapeGeom, shapeMat);
scene.add(mesh);
btnRecalc.addEventListener("click", onClick);
var box3 = new THREE.Box3();
var size = new THREE.Vector3();
var v3 = new THREE.Vector3(); // for re-use
function onClick(event) {
box3.setFromObject(mesh); // get AABB of the shape mesh
box3.getSize(size); // get size of that box
var pos = shapeGeom.attributes.position;
var uv = shapeGeom.attributes.uv;
for (let i = 0; i < pos.count; i++) {
v3.fromBufferAttribute(pos, i);
v3.subVectors(v3, box3.min).divide(size); // cast world uvs to range 0..1
uv.setXY(i, v3.x, v3.y);
}
uv.needsUpdate = true; // set it to true to make changes visible
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<button id="btnRecalc" style="position: absolute;">Re-calculate UVs</button>
My problem is very basic : I have a texture object, I want to clone it but Texture.clone doesn't seem to work as expected
My code is as basic as my problem :
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 512;
canvas.getContext("2d").fillStyle = "#ff0000";
canvas.getContext("2d").fillRect(0,0,512,512);
var texture = new THREE.Texture(canvas);
texture.needsUpdate= true;
var material = new THREE.MeshBasicMaterial({map:texture});
var mesh = new THREE.Mesh(new THREE.PlaneGeometry(100,100), material);
scene.add(mesh)
//please, don't focus on "scene" and the webglrenderer object,
//it's define on the top of my code but it's not important here.
This code works as expected.
BUT if I change the line containing the material definition by
var material = new THREE.MeshBasicMaterial({map:texture.clone() });
nothing appear on the screen !
Why ?!
EDIT : Thanks to "TheJim01" , I realized that I didn't apply the "needsUpdate = true" on my cloned-texture.
With
var cloneTexture = texture.clone();
cloneTexture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial({map:cloneTexture });
Everything works as expected.
Thank you
I haven't dug into the renderer code, so I don't know how it uses this information, but Texture.needsUpdate increments the "version" of the texture. CanvasTexture sets this right away, causing the version value to be 1 on the first render.
Texture.clone doesn't perpetuate the version information, and instead re-calls its constructor. Because you aren't setting needsUpdate after the clone, you are not following the same steps.
// from your code:
var texture = new THREE.Texture(canvas);
texture.needsUpdate= true;
// texture.version === 1
// but...
var material = new THREE.MeshBasicMaterial({ map:texture.clone() });
//material.map.version === 0
So the clone you're passing into the new material has a version of 0, which is apparently no good if you're using a canvas as the source.
This should resolve the issue:
var material = new THREE.MeshBasicMaterial({ map:texture.clone() });
material.map.needsUpdate = true;
SEE THE SOLUTION BELOW
I am really confused. The target i am trying to achieve is;
Use multiple textures on a loaded mesh.
I have searched multiple times and still i can see the similar questions but nothing helped me.
What i'v tried is (yet);
Created a new mesh with the target mesh's geometry and pushed to target object3d element. (Like a photoshop layer.)
var texture = new THREE.Texture(mapCanvas);
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({map: texture, transparent: true});
var targetMesh = book.children[0].children[1],
newMesh = new THREE.Mesh(targetMesh.geometry, material);
book.children[0].children.push(newMesh);
Result, wrong geometry attributes, or am i missing something?.
But i think it could be a easier solution like using multiple textures at the same time with a correct order.
Full code:
sampleImage.onload = function() {
var mapCanvas = document.createElement('canvas');
mapCanvas.width = sampleImage.width;
mapCanvas.height = sampleImage.height;
var ctx = mapCanvas.getContext('2d');
ctx.translate(sampleImage.width / 2, sampleImage.height / 2);
ctx.rotate(Math.PI);
ctx.translate(-sampleImage.width / 2, -sampleImage.height / 2);
ctx.drawImage(sampleImage, 0, 0, sampleImage.width, sampleImage.height);
var texture = new THREE.Texture(mapCanvas);
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({map: texture, transparent: true});
var targetMesh = book.children[0].children[1],
newMesh = new THREE.Mesh(targetMesh.geometry, material);
book.children[0].children.push(newMesh);
};
I found a solution with cloning.
Do not push directly to the mesh group using javascript array push.
use .add method.
book.children[0].add(newMesh);
I have a threejs scene with a simple plane and a loaded black and white png map on the plane's material. What I would like to do is change the color of the map without effecting any other meshes in the scene. Are there any ideas on this? a snippet of my code is blow:
var plane = new THREE.PlaneGeometry(foilWidth, foilHeight);
var mat = new THREE.MeshPhongMaterial();
var texloader = new THREE.TextureLoader();
mat.map = texloader.load(modelOptions.foil);
mat.color = new THREE.Color(0xff0000); // works is the map is not given
mat.transparent = false;
mat.needsUpdate = true;
mat.transparent = true;
mat.side = THREE.DoubleSide;
mat.depthWrite = true;
mat.needsUpdate = true;
let foil = new THREE.Mesh(plane, mat);
scene.add(foil);
while using Three.js i met a problem connected with vertexes colors.
I created BufferGeometry , which consists of squares made by 2 triangles.
var geometry = new THREE.BufferGeometry();
// filling positions and colors
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3));
var material = new THREE.MeshPhongMaterial(
{
side: THREE.OneSide, vertexColors: THREE.VertexColors
});
GridFloor = new THREE.Mesh(geometry, material);
GridFloor.rotation.x = Math.PI / 2;
GridFloor.geometry.dynamic = true;
GridFloor.geometry.__dirtyColors = true;
scene.add(GridFloor);
Everything works fine, until i change vertex colors. I am changing them , trying to update , but nothing happens...
var newColor = new THREE.Color(0xff0000);
var colors = GridFloor.geometry.attributes.color.array;
for (var i = 0, j = 0; i < 4; i++, j += 3) {
var index = INTERSECTED.indices[i % 3] * 3;
colors[index] = newColor.r;
colors[index + 1] = newColor.g;
colors[index + 2] = newColor.b;
}
GridFloor.geometry.colorsNeedUpdate = true;
INTERSECTED.colorsNeedUpdate = true;
Thank you for your help.
Here is how you set the needsUpdate flag for BufferGeometry.
bufferGeometry.attributes.attributeName.needsUpdate = true;
three.js r.68