Wrong location of texture on the second triangle of BufferGeometry square - three.js

I'm trying to add texture to a THREE.BufferGeometry. This is a working fiddle of what I'm accomplished so far.
This is my code:
var Test = new function () {
var scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(10, window.innerWidth / window.innerHeight, 1, 1000),
renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true
camera.position.set(-5, 5, 5);
camera.rotation.order = 'YXZ';
camera.rotation.y = -Math.PI / 4;
camera.rotation.x = Math.atan(-1 / Math.sqrt(2));
scene.add(camera)
scene.add(new THREE.AmbientLight(0x222222));
var light = new THREE.PointLight(0xffffff, 0.7);
camera.add(light);
this.init = function () {
$('body').append(renderer.domElement);
Test.render();
}
this.render = function () {
requestAnimationFrame(Test.render);
renderer.render(scene, camera);
}
this.getScene = function getScene() {
return scene
}
}
var CreateSimpleMesh = new function () {
var grassGeometry = new THREE.BufferGeometry(),
grassVertexPositions = [],
imageArray = [
[0, 0],
[1, 0],
[1, 1],
[0, 1]
],
image = new Image()
this.init = function () {
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wwLAi03pl5F1AAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAEkklEQVR42u3dTWzTdRzH8XfXdW03t3XPbmyMlLkNZA9kcwuKBBVBEjXEGDExEAwH44GDJpp4QYnCQcIZoiZGUaKeCFHjIBB5UnnSKW5zj7AVWvfvxsbWre22Dg+1SmHA/iwbHD7v2+/fZYfvK/+n9vCzrDmx7hrqvilBIxCIuk2JGsHsNth0hcvf9dB/1iBkBLFnOkgrc+HeWEr6oow7gwy1DuJt8NB/zs+oJ4DFmoAjx4GrMoviF92klqRryiY69dqxuHXICBIyghgnfFS/X0feyoLbg/y8+ccbjkwy0hNgpCfA5e+7qdxaS/6qQk16mrmWZFL4fDGZNTnYM+wEuodp2fUHg39eof2j5juDpJW7mLd2PlmP5ODMT2FyPMJIT4AL+9rpPeKlbXeTQExUv2dF/HwfclGxtYbjLx0i+PfonS9Zyz5ZGX/XtyWQXp7B4jer6D3iZWJkQlOe6Y3bGR178rwU8zf1ieAEwcsjXNjXDkD+ap0dM+3iVx0APDjFleaWIA3L98etnQXJlL6+mOL1JZroDPIe9HDhy3aSC1NY8HLJ3b+HjA2ECfeHQe/1M8I4/8E5rA4r1dvrsNqt0z9D1pxYF4UYGmPor0HadjfR/U0nWKB8S4WmazLPgYs072wEC1S+W0vqwvS7e1NPSksiuy6XpTvqAPAduqTpmr1nfN1B84eNcA0efnspuY/nz/xNfXI8eq2KjOopy0ydn7XS8XELAIvfqqbw2WJzX52cfeMk819wk74kE1uqjYnhcQabB2jf0xR90anM0pRNFMMAaN7ZGL1sXdcT364lyWW/NUj/GT/9Z/xT/nNbmo3yLUs05VnMcuPvIQPn+7l0oJsrv/oJ94VIsCXgLEghuz6XBetLsGc7NLXZfGm88UBGRRYZFbos3av0e4hAlEAEogQiECUQgSiBCEQJRAlEIEogAlECEYgSiECUQJRABKIEIhAlEIEogQhECUQJRCBKIAJRAhGIEohAlECUQASiBCIQJRCBKIEIRAlECUQgSiACUQIRiBKIQJRAlEAEogQiECUQgSiBCETdu6bcFGxyLELX3ja8P3gI+YM4cpwUPFOEe2MZCTYZmi3QPYxxzIdx3MfV5gHg/20JpwXy+7azGEd9/62DvlE6P20l0DVM9fY6TdhkJ185fPeXLP8vvRhHfViTE6nZtYxVh5+jZtcyrMmJ9B710nfa0IRNlrIglYWvlvHY50+aB/Ed9ADg3lBKdn0eVruV7Po83BtKAfA2eDRhky3/4ilKNi/iAXeaeZCrLYMA5DyaF3c8th5qGdCE5/IpK9wXBMBZEL/Xd2wd8oc0tbkEiYQiADftZBxbR8LaenVOQayO2OAj8VDhGFSipjaXIPZsZ/RR1zsSdzy2duRop885BUlf5Io+/v7UG/84/O86rdylqc0lSP7TRQB07W2j77TB5FiEvtMGXXvbop+vLtLUZrGbNicG+O2dUxjHfTf9ce6KfJbuqNfUTNawfP9tP7/+a5Qpv5iq2laLe1MZjjwnFqsFR54T96Yyqt6r1XTvxRmi7qN7iBKIEohA1DT7B5cwUaoY0hehAAAAAElFTkSuQmCC'
var texture = new THREE.Texture();
texture.image = image;
image.onload = function () {
texture.needsUpdate = true;
};
// first triangle
grassVertexPositions.push([0, 0, 0])
grassVertexPositions.push([1, 0, 0])
grassVertexPositions.push([0, 1, 0])
// second triangle
grassVertexPositions.push([1, 1, 0])
grassVertexPositions.push([0, 1, 0])
grassVertexPositions.push([1, 0, 0])
var grassVertices = new Float32Array(grassVertexPositions.length * 3),
normals = new Float32Array(grassVertexPositions.length * 3),
colors = new Float32Array(grassVertexPositions.length * 3),
uvs = new Float32Array(grassVertexPositions.length * 2)
for (var i = 0; i < grassVertexPositions.length; i++) {
var index = 3 * i
grassVertices[index + 0] = grassVertexPositions[i][0]
grassVertices[index + 1] = grassVertexPositions[i][1]
grassVertices[index + 2] = grassVertexPositions[i][2]
}
uvs[0] = imageArray[0][0]
uvs[1] = imageArray[0][1]
uvs[2] = imageArray[1][0]
uvs[3] = imageArray[1][1]
uvs[4] = imageArray[3][0]
uvs[5] = imageArray[3][1]
uvs[6] = imageArray[1][0]
uvs[7] = imageArray[1][1]
uvs[8] = imageArray[2][0]
uvs[9] = imageArray[2][1]
uvs[10] = imageArray[3][0]
uvs[11] = imageArray[3][1]
grassGeometry.addAttribute('position', new THREE.BufferAttribute(grassVertices, 3))
grassGeometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3))
grassGeometry.addAttribute('color', new THREE.BufferAttribute(colors, 3))
grassGeometry.addAttribute('uv', new THREE.BufferAttribute(uvs, 2))
grassGeometry.computeVertexNormals()
//var textureLoader = new THREE.TextureLoader();
//textureLoader.load('/img/testface.png', function (texture) {
var grassMaterial = new THREE.MeshLambertMaterial({map: texture}),
grassMesh = new THREE.Mesh(grassGeometry, grassMaterial)
grassMesh.rotation.x = -Math.PI / 2;
Test.getScene().add(grassMesh)
var helper = new THREE.WireframeHelper(grassMesh, 0xff00ff); // alternate
helper.material.linewidth = 1;
Test.getScene().add(helper);
console.log(grassMesh.geometry.attributes)
//});
}
}
$(document).ready(function () {
Test.init()
CreateSimpleMesh.init()
});
The problem is that texture on first triangle of the rectangle is correct but on the second triangle something strange is happening with the texture.
This is how it should look like
This is how it look now

Try these texture coordinates (same as x and y of vertex coordinates):
uvs[0] = 0
uvs[1] = 0
uvs[2] = 1
uvs[3] = 0
uvs[4] = 0
uvs[5] = 1
uvs[6] = 1
uvs[7] = 1
uvs[8] = 0
uvs[9] = 1
uvs[10] = 1
uvs[11] = 0

Related

Is there any way to make a quad sphere with BufferGeometry?

I am new to three.js and I want to build a quad sphere with the BufferGeometry(). I want to recreate sebastin lague cube sphere and implementing in three js.
Here is my TerrainFace.js code:
class {
constructor(resolution, radius, localUp, scene) {
this._resolution = resolution;
this._radius = radius;
this._scene = scene;
this._localUp = localUp;
this._axisA = new THREE.Vector3(localUp.y, localUp.z, localUp.x);
this._axisB = new THREE.Vector3().crossVectors(this._localUp, this._axisA);
this._geometry = new THREE.BufferGeometry();
}
Start() {
this._Initialize();
let plane = new THREE.Mesh(this._geometry, new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }));
this._scene.add(plane);
}
_Initialize() {
let i;
let triIndex = 0;
this._positions = [];
this._normals = [];
this._indices = [];
for (let y = 0; y < this._resolution; y++) {
for (let x = 0; x < this._resolution; x++) {
i = x + (y * this._resolution);
let xPercent = x / (this._resolution - 1);
let yPercent = y / (this._resolution - 1);
let _P = new THREE.Vector3();
let _C = new THREE.Vector3();
let _A = new THREE.Vector3(this._axisA.x, this._axisA.y, this._axisA.z).multiplyScalar(2 * (xPercent - 0.5));
let _B = new THREE.Vector3(this._axisB.x, this._axisB.y, this._axisB.z).multiplyScalar(2 * (yPercent - 0.5));
_C.addVectors(this._localUp, _A);
_P.addVectors(_C, _B);
// _P.normalize(); // this is for cube sphere
_P.multiplyScalar(this._radius);
this._positions[i] = _P.x;
this._positions[i + 1] = _P.y;
this._positions[i + 2] = _P.z;
let ad = new THREE.Mesh(new THREE.SphereGeometry(0.1), new THREE.MeshNormalMaterial({ side: THREE.DoubleSide }));
ad.position.set(this._positions[i], this._positions[i + 1], this._positions[i + 2]);
this._scene.add(ad);
if (x != (this._resolution - 1) && y != (this._resolution - 1)) {
const a = i;
const b = i + 1;
const c = i + this._resolution;
const d = i = this._resolution + 1;
// a - - b
// | |
// | |
// c - - d
this._indices[triIndex] = a;
this._indices[triIndex + 1] = d;
this._indices[triIndex + 2] = c;
this._indices[triIndex + 3] = a;
this._indices[triIndex + 4] = b;
this._indices[triIndex + 5] = d;
triIndex += 6;
}
}
}
this._geometry.setIndex(this._indices);
this._geometry.setAttribute("position", new THREE.Float32BufferAttribute(this._positions, 3));
}
}
This is the basic scene
let groups = new THREE.Group();
let directions = [
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(0, -1, 0),
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(0, 0, 1),
new THREE.Vector3(0, 0, -1),
]
for (let i = 0; i < directions.length; i++) {
let plane = new TerrainFace1(2, 10, directions[i], groups);
plane.Start();
}
this._scene.add(groups);
for some reason It is not working and it is creating some weird shapes and I dont know why. Thanks.
Follow SebastianLague sphere generation.

How to levitate non-static objects with oimo.js

I use the oimo.js library (physic engine) with three.js and I would like my objects (not static) to levitate.
https://github.com/lo-th/Oimo.js/
I try to position the objects at a y value defined in my loop function but they keep falling.
How can I have these objects levitate while keeping non-static objects?
Here below my codepen to explain that :
https://codepen.io/ogames/pen/oNEWKLE?editors=0011
import * as THREE from 'https://cdn.jsdelivr.net/npm/three#0.117.1/build/three.module.js'
import {
OrbitControls
} from "https://cdn.jsdelivr.net/npm/three#0.117.1/examples/jsm/controls/OrbitControls.js"
//import * as oimo from 'https://cdnjs.cloudflare.com/ajax/libs/oimo/1.0.9/oimo.min.js'
import {
TWEEN
} from 'https://unpkg.com/three#0.125.2/examples//jsm/libs/tween.module.min'
import {
CSM
} from "https://cdn.jsdelivr.net/npm/three#0.117.1/examples/jsm/csm/CSM.js"
///////////////////////////////////////////////////////////////////////////////
// utils
///////////////////////////////////////////////////////////////////////////////
export const random = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// variables
///////////////////////////////////////////////////////////////////////////////
var scene, container, controls, camera, renderer, light, ambient_light, additionalDirectionalLight, csm, stats, physics, position, body, world, ground_body, camera_body;
var game_start = false
window.setTimeout(() => {
game_start = true
}, 1000)
//material
var m = {};
//geometry
var g = {};
//objects
var o = {};
//
o.enemy = [];
//class
class Enemy_instance {
constructor(config) {
var g = {}
var m = {}
this.config = config;
g.body = new THREE.BoxGeometry(1, 1, 1);
m.body = new THREE.MeshToonMaterial({
color: this.config.color_body,
});
// a lot of body
this.body = this.create_instance(g.body, m.body, this.config.count, true);
this.config.scene.add(this.body)
// create several group and add dummy into it
this.mesh = [];
this.body.dummy = []
this.physic_body = []
for (let i = 0; i < this.config.count; i++) {
this.body.dummy[i] = new THREE.Object3D()
this.mesh[i] = new THREE.Group()
this.mesh[i].is_alive = true;
this.mesh[i].is_collide = false;
// rename
let mesh = this.mesh[i]
mesh.position.x = this.config.posx[i]
mesh.position.y = this.config.posy
mesh.position.z = this.config.posz[i]
this.config.scene.add(mesh)
this.physic_body[i] = this.config.world.add({
type: 'box', // type of shape : sphere, box, cylinder
size: [1, 1, 1], // size of shape
pos: [mesh.position.x, mesh.position.y, mesh.position.z], // start position in degree
rot: [0, 0, 0], // start rotation in degree
move: true, // dynamic or statique
density: 10,
friction: .5,
restitution: 0.1,
belongsTo: 1, // The bits of the collision groups to which the shape belongs.
collidesWith: 0xffffffff // The bits of the collision groups with which the shape collides.
});
this.physic_body[i].position.copy(mesh.position)
this.physic_body[i].quaternion.copy(mesh.position)
console.log(this.physic_body[0])
};
}
//without having "this[obj]mesh"
create_instance(geo, mat, count, fl) {
let obj = new THREE.InstancedMesh(geo, mat, count);
obj.castShadow = fl
obj.receiveShadow = fl
obj.dummy = []
return obj
}
// for all instances
animate_instance(obj) {
obj.instanceMatrix.needsUpdate = true;
for (let i = 0; i < this.config.count; i++) {
obj.dummy[i].updateMatrix()
obj.setMatrixAt(i, obj.dummy[i].matrix);
this.add_element(obj.dummy, i)
}
}
add_element(obj, num) {
let master = this.mesh[num]
let slave = obj[num]
slave.position.set(master.position.x, master.position.y, master.position.z)
slave.rotation.set(master.rotation.x, master.rotation.y, master.rotation.z)
}
die(num) {
this.mesh[num].is_alive = false
this.hide(this.body.dummy[num])
}
hide(obj) {
new TWEEN.Tween(obj.scale)
.to({
x: 1.2,
y: 1.2,
z: 1.2,
}, 100)
.yoyo(true)
.delay(2000)
.repeat(1)
.easing(TWEEN.Easing.Linear.None)
.start()
.onComplete(() => {
new TWEEN.Tween(obj.scale)
.to({
x: 0,
y: 0,
z: 0,
}, 80)
.easing(TWEEN.Easing.Linear.None)
.start()
.onComplete(() => {});
});
}
animate() {
this.animate_instance(this.body)
for (let i = 0; i < this.config.count; i++) {
this.mesh[i].position.copy(this.physic_body[i].getPosition());
this.mesh[i].quaternion.copy(this.physic_body[i].getQuaternion());
// contacts
if (this.physic_body[i].numContacts > 0) {
this.mesh[i].is_collide = true
this.mesh[i].is_alive && this.die(i)
}
// no contacts
if (this.mesh[i].is_collide == false) {
this.physic_body[i].position.y = 2
}
}
}
};
var data = {
fade: true,
far: 200,
mode: 'practical',
lightX: -.31,
lightY: -1,
}
init_app();
function init_app() {
start_physic()
init();
animate();
}
// init physic with oimo
function start_physic() {
world = new OIMO.World({
timestep: 1 / 30,
// timestep: 1 / 60,
iterations: 1,
// iterations: 8,
broadphase: 2, // 1 brute force, 2 sweep and prune, 3 volume tree
worldscale: 1, // scale full world
random: true, // randomize sample
info: true, // calculate statistic or not
gravity: [0, -10, 0]
});
ground_body = world.add({
type: 'box', // type of shape : sphere, box, cylinder
size: [100000, .1, 100000], // size of shape
pos: [0, -.7, 0], // start position in degree
rot: [0, 0, 0], // start rotation in degree
move: false, // dynamic or statique
density: 1,
friction: 0.2,
restitution: 0.2,
belongsTo: 1, // The bits of the collision groups to which the shape belongs.
collidesWith: 0xffffffff // The bits of the collision groups with which the shape collides.
});
}
function init() {
Minimal()
create_object()
init_camera()
}
function create_object() {
create_ground();
create_enemy();
}
function init_camera() {
camera_body = world.add({
type: 'sphere', // type of shape : sphere, box, cylinder
size: [10, 10, 10], // size of shape
pos: [camera.position.x, camera.position.y, camera.position.z], // start position in degree
rot: [0, 0, 0], // start rotation in degree
move: true, // dynamic or statique
density: .0001,
friction: 0,
restitution: .5,
belongsTo: 1, // The bits of the collision groups to which the shape belongs.
collidesWith: 0xffffff1 // The bits of the collision groups with which the shape collides.
});
camera_body.position.copy(camera.position)
camera_body.quaternion.copy(camera.position)
}
function create_ground() {
g.ground = new THREE.BoxGeometry(100000, .1, 100000);
m.ground = new THREE.MeshStandardMaterial({
color: 0xfce098,
});
csm.setupMaterial(m.ground)
o.ground = new THREE.Mesh(g.ground, m.ground);
o.ground.position.y = -.7
o.ground.castShadow = true;
o.ground.receiveShadow = true;
scene.add(o.ground);
}
function create_enemy() {
let c = {
scene: scene,
world: world,
speed: random(8, 16) * .01,
color_body: 0xbb1a1a,
count: 50,
}
c.posx = []
c.posz = []
for (let i = 0; i < c.count; i++) {
c.posx[i] = random(-20, -29)
c.posy = 20
c.posz[i] = i * 7
};
o.enemy = new Enemy_instance(c)
}
function Minimal() {
container = document.getElementById('world');
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 2, 20000);
camera.position.y = 0
camera.position.z = -4
// camera.position.x = -4
camera.lookAt(0, 0, 0)
camera.updateMatrixWorld()
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
});
//Shadows
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.autoClear = false;
// ORBIT
controls = new OrbitControls(camera, renderer.domElement);
controls.target.y = 0
controls.maxPolarAngle = Math.PI
controls.maxDistance = 10000;
controls.minDistance = .01;
// LIGHTS
ambient_light = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient_light);
//PLUG IN SHADOW FOR HUGE SCENE
csm = new CSM({
maxFar: data.far,
cascades: 4,
mode: data.mode,
parent: scene,
shadowMapSize: 4048,
lightDirection: new THREE.Vector3(data.lightX, data.lightY, data.lightZ).normalize(),
camera: camera
});
csm.fade = true;
container.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
world.step();
o.enemy.animate()
// copy position and rotation to three mesh
o.ground.position.copy(ground_body.getPosition());
o.ground.quaternion.copy(ground_body.getQuaternion());
// physical camera
camera_body.position.copy(camera.position)
camera_body.quaternion.copy(camera.position)
TWEEN.update();
camera.updateMatrixWorld();
csm.update();
renderer.render(scene, camera);
};
Thank you for your precious help (I'm stuck on this question for 10 days without finding a real solution).
Thanks #Marquizzo,
It works with create an "new THREE.Object3D();" and set position with another object :
https://codepen.io/ogames/pen/mdXwbvr
import * as THREE from 'https://cdn.jsdelivr.net/npm/three#0.117.1/build/three.module.js'
import {
OrbitControls
} from "https://cdn.jsdelivr.net/npm/three#0.117.1/examples/jsm/controls/OrbitControls.js"
//import * as oimo from 'https://cdnjs.cloudflare.com/ajax/libs/oimo/1.0.9/oimo.min.js'
import {
TWEEN
} from 'https://unpkg.com/three#0.125.2/examples//jsm/libs/tween.module.min'
import {
CSM
} from "https://cdn.jsdelivr.net/npm/three#0.117.1/examples/jsm/csm/CSM.js"
///////////////////////////////////////////////////////////////////////////////
// utils
///////////////////////////////////////////////////////////////////////////////
export const random = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// variables
///////////////////////////////////////////////////////////////////////////////
var scene, container, controls, camera, renderer, light, ambient_light, additionalDirectionalLight, csm, stats, physics, position, body, world, ground_body, camera_body;
var game_start = false
window.setTimeout(() => {
game_start = true
}, 2000)
//material
var m = {};
//geometry
var g = {};
//objects
var o = {};
//
o.enemy = [];
//class
class Enemy_instance {
constructor(config) {
var g = {}
var m = {}
this.config = config;
g.body = new THREE.BoxGeometry(1, 1, 1);
m.body = new THREE.MeshToonMaterial({
color: this.config.color_body,
});
// a lot of body
this.body = this.create_instance(g.body, m.body, this.config.count, true);
this.config.scene.add(this.body)
// create several group and add dummy into it
this.mesh = [];
this.body.dummy = []
this.physic_body = []
for (let i = 0; i < this.config.count; i++) {
this.body.dummy[i] = new THREE.Object3D()
this.mesh[i] = new THREE.Group()
this.mesh[i].is_alive = true;
this.mesh[i].is_collide = false;
// rename
let mesh = this.mesh[i]
mesh.position.x = this.config.posx[i]
mesh.position.y = this.config.posy
mesh.position.z = this.config.posz[i]
this.config.scene.add(mesh)
this.physic_body[i] = this.config.world.add({
type: 'box', // type of shape : sphere, box, cylinder
size: [1, 1, 1], // size of shape
pos: [mesh.position.x, mesh.position.y, mesh.position.z], // start position in degree
rot: [0, 0, 0], // start rotation in degree
move: true, // dynamic or statique
density: 10,
friction: .5,
restitution: 0.1,
belongsTo: 1, // The bits of the collision groups to which the shape belongs.
collidesWith: 0xffffffff // The bits of the collision groups with which the shape collides.
});
this.physic_body[i].position.copy(mesh.position)
this.physic_body[i].quaternion.copy(mesh.position)
console.log(this.physic_body[0])
};
}
//without having "this[obj]mesh"
create_instance(geo, mat, count, fl) {
let obj = new THREE.InstancedMesh(geo, mat, count);
obj.castShadow = fl
obj.receiveShadow = fl
obj.dummy = []
return obj
}
// for all instances
animate_instance(obj) {
obj.instanceMatrix.needsUpdate = true;
for (let i = 0; i < this.config.count; i++) {
obj.dummy[i].updateMatrix()
obj.setMatrixAt(i, obj.dummy[i].matrix);
this.add_element(obj.dummy, i)
}
}
add_element(obj, num) {
let master = this.mesh[num]
let slave = obj[num]
slave.position.set(master.position.x, master.position.y, master.position.z)
slave.rotation.set(master.rotation.x, master.rotation.y, master.rotation.z)
}
die(num) {
this.mesh[num].is_alive = false
//console.log(num, "collide and die")
this.hide(this.body.dummy[num])
}
hide(obj) {
new TWEEN.Tween(obj.scale)
.to({
x: 1.2,
y: 1.2,
z: 1.2,
}, 100)
.yoyo(true)
.delay(2000)
.repeat(1)
.easing(TWEEN.Easing.Linear.None)
.start()
.onComplete(() => {
new TWEEN.Tween(obj.scale)
.to({
x: 0,
y: 0,
z: 0,
}, 80)
.easing(TWEEN.Easing.Linear.None)
.start()
.onComplete(() => {});
});
}
move(num) {
if (this.config.posx[num] < 0) {
this.physic_body[num].position.x += this.config.speed
}
if (this.config.posx[num] > 0) {
this.physic_body[num].position.x -= this.config.speed
}
if (this.physic_body[num].position.x > 30) {
this.physic_body[num].position.x = this.config.posx[num]
}
if (this.physic_body[num].position.x < -30) {
this.physic_body[num].position.x = this.config.posx[num]
}
}
animate() {
this.animate_instance(this.body)
for (let i = 0; i < this.config.count; i++) {
this.mesh[i].position.copy(this.physic_body[i].getPosition());
this.mesh[i].quaternion.copy(this.physic_body[i].getQuaternion());
// contacts
if (this.physic_body[i].numContacts > 0) {
this.mesh[i].is_collide = true
this.mesh[i].is_alive && this.die(i)
}
// no contacts
if (this.mesh[i].is_collide == false) {
this.physic_body[i].position.y = 2
this.move(i)
}
}
}
};
var data = {
fade: true,
far: 200,
mode: 'practical',
lightX: -.31,
lightY: -1,
}
init_app();
function init_app() {
start_physic()
init();
animate();
}
// init physic with oimo
function start_physic() {
world = new OIMO.World({
timestep: 1 / 30,
// timestep: 1 / 60,
iterations: 1,
// iterations: 8,
broadphase: 2, // 1 brute force, 2 sweep and prune, 3 volume tree
worldscale: 1, // scale full world
random: true, // randomize sample
info: true, // calculate statistic or not
gravity: [0, -20, 0]
});
ground_body = world.add({
type: 'box', // type of shape : sphere, box, cylinder
size: [100000, .1, 100000], // size of shape
pos: [0, -.7, 0], // start position in degree
rot: [0, 0, 0], // start rotation in degree
move: false, // dynamic or statique
density: 1,
friction: 0.2,
restitution: 0.2,
belongsTo: 1, // The bits of the collision groups to which the shape belongs.
collidesWith: 0xffffffff // The bits of the collision groups with which the shape collides.
});
}
function init() {
Minimal()
create_object()
init_camera()
}
function create_object() {
create_ground();
create_enemy();
o.paddel = new THREE.Object3D();
o.paddel.position.set(10, 10, 10)
scene.add(o.paddel);
}
function init_camera() {
camera_body = world.add({
type: 'sphere', // type of shape : sphere, box, cylinder
size: [10, 10, 10], // size of shape
pos: [camera.position.x, camera.position.y, camera.position.z], // start position in degree
rot: [0, 0, 0], // start rotation in degree
move: true, // dynamic or statique
density: .0001,
friction: 0,
restitution: .5,
belongsTo: 1, // The bits of the collision groups to which the shape belongs.
collidesWith: 0xffffff1 // The bits of the collision groups with which the shape collides.
});
camera_body.position.copy(camera.position)
camera_body.quaternion.copy(camera.position)
}
function create_ground() {
g.ground = new THREE.BoxGeometry(100000, .1, 100000);
m.ground = new THREE.MeshStandardMaterial({
color: 0xfce098,
});
csm.setupMaterial(m.ground)
o.ground = new THREE.Mesh(g.ground, m.ground);
o.ground.position.y = -.7
o.ground.castShadow = true;
o.ground.receiveShadow = true;
scene.add(o.ground);
}
function create_enemy() {
let c = {
scene: scene,
world: world,
speed: random(8, 16) * .01,
color_body: 0xbb1a1a,
count: 5000,
}
c.posx = []
c.posy = 20
c.posz = []
for (let i = 0; i < c.count; i++) {
// pour générer du choix
let rnd = Math.round(Math.random())
if (rnd == 0) {
c.posx[i] = random(20, 29)
} else {
c.posx[i] = random(-20, -29)
}
c.posz[i] = i * 7
};
o.enemy = new Enemy_instance(c)
}
function Minimal() {
container = document.getElementById('world');
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 2, 20000);
camera.position.y = 0
camera.position.z = -4
// camera.position.x = -4
camera.lookAt(0, 0, 0)
camera.updateMatrixWorld()
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
});
//Shadows
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.autoClear = false;
// ORBIT
controls = new OrbitControls(camera, renderer.domElement);
controls.target.y = 0
controls.maxPolarAngle = Math.PI
controls.maxDistance = 10000;
controls.minDistance = .01;
// LIGHTS
ambient_light = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient_light);
//PLUG IN SHADOW FOR HUGE SCENE
csm = new CSM({
maxFar: data.far,
cascades: 4,
mode: data.mode,
parent: scene,
shadowMapSize: 4048,
lightDirection: new THREE.Vector3(data.lightX, data.lightY, data.lightZ).normalize(),
camera: camera
});
csm.fade = true;
container.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
world.step();
o.enemy.animate()
o.enemy.physic_body[0].setPosition(o.paddel.position);
// copy position and rotation to three mesh
o.ground.position.copy(ground_body.getPosition());
o.ground.quaternion.copy(ground_body.getQuaternion());
// physical camera
camera_body.position.copy(camera.position)
camera_body.quaternion.copy(camera.position)
TWEEN.update();
camera.updateMatrixWorld();
csm.update();
renderer.render(scene, camera);
};

How to use line with width of three.js with morphTargets to animate one line moving to another

question reference
I want to implement an animation.
The animation should be a line move to another line. There will be some deformation in the process of the line moving
There is a correspondence between the points of the two lines.
I can use the basic line of three.js to animate one line moving to another.
<script type="module">
import * as THREE from '../../build/three.module.js'
import { OrbitControls } from '../jsm/controls/OrbitControls.js'
function main() {
var scene = new THREE.Scene()
const geometry = new THREE.BufferGeometry().setAttribute('position',
new THREE.Float32BufferAttribute([-2, 0, 0, -0.5, 0, -0.5, 0, 0, -2], 3))
const geometry1 = new THREE.BufferGeometry().setAttribute('position',
new THREE.Float32BufferAttribute([5, 0, 0, 1, 1, 1, 0, 0, 5], 3))
geometry.morphAttributes.position = [
geometry1.attributes.position,
]
var material = new THREE.LineBasicMaterial({
color: 0x0000ff,
})
var mesh = new THREE.Line(geometry, material)
mesh.morphTargetInfluences[0] = 0
scene.add(mesh)
/**
*
*/
var width = window.innerWidth
var height = window.innerHeight
var k = width / height
var s = 10
var camera = new THREE.OrthographicCamera(
-s * k,
s * k,
s,
-s,
-1500,
1500
)
camera.position.set(10, 10, 10)
camera.lookAt(10, 0, 0)
/**
*
*/
var renderer = new THREE.WebGLRenderer()
// window.renderer = renderer;
renderer.setSize(width, height)
renderer.setClearColor(0xb9d3ff, 1)
document.body.appendChild(renderer.domElement)
// renderer.render(scene, camera);
new OrbitControls(camera, renderer.domElement)
var clock = new THREE.Clock()
const axes = new THREE.AxesHelper(10)
scene.add(axes)
let flag = false
let index = 0
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
if (index <0) {
flag = true
} else if (index > 1) {
flag = false
}
if (flag) {
index += 0.01
} else {
index -= 0.01
}
mesh.morphTargetInfluences[0] = index
console.log(flag, index)
}
render()
}
main()
</script>
Now I want to use the LineGeometry of three.js because this line has a width property. How to use the morphtargets with LineGeometry?
If the LineGeometry can't do it,any other bufferGeometry to replace LineGeometry?
Three.js 136.
<script type="module">
import * as THREE from '../../build/three.module.js'
import { OrbitControls } from '../jsm/controls/OrbitControls.js'
import { Line2 } from '../jsm/lines/Line2.js'
import { LineMaterial } from '../jsm/lines/LineMaterial.js'
import { LineGeometry } from '../jsm/lines/LineGeometry.js'
function main() {
var scene = new THREE.Scene()
const geometry2 = new LineGeometry()
geometry2.setPositions([-3, 0, 0, -0.5, 0, -0.5, 0, 0, -2])
console.log(geometry2)
const geometry1 = new LineGeometry()
geometry1.setPositions([-3, 0, 0, -1, 0, -1, 0, 0, -3])
geometry2.morphAttributes.position = [geometry1.attributes.position]
let matLine = new LineMaterial({
color: '#ffdb2d',
linewidth: 5, // in world units with size attenuation, pixels otherwise
})
let line = new Line2(geometry2, matLine)
line.morphTargetInfluences[0] = 0
scene.add(line)
/**
*/
var width = window.innerWidth
var height = window.innerHeight
var k = width / height
var s = 5
var camera = new THREE.OrthographicCamera(
-s * k,
s * k,
s,
-s,
-1500,
1500
)
camera.position.set(5, 5, 5)
camera.lookAt(5, 0, 0)
var renderer = new THREE.WebGLRenderer()
// window.renderer = renderer;
renderer.setSize(width, height)
renderer.setClearColor(0xb9d3ff, 1)
document.body.appendChild(renderer.domElement)
// renderer.render(scene, camera);
new OrbitControls(camera, renderer.domElement)
var clock = new THREE.Clock()
const axes = new THREE.AxesHelper(10)
scene.add(axes)
let flag = false
let index = 0
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
if (index < 0) {
flag = true
} else if (index > 1) {
flag = false
}
if (flag) {
index += 0.01
} else {
index -= 0.01
}
line.morphTargetInfluences[0] = index
matLine.resolution.set(window.innerWidth, window.innerHeight) // resolution of the viewport
}
render()
}
main()
</script>
Use TubeGeometry:
const curve = new THREE.CatmullRomCurve3([new THREE.Vector3(-2, 0, 0), new THREE.Vector3(-0.5, 0, -0.5), new THREE.Vector3(0, 0, -2)])
var tubeGeometry = new THREE.TubeGeometry(curve, 100, 0.05, 50, false);
const curve1 = new THREE.CatmullRomCurve3([new THREE.Vector3(0, 0, 5,), new THREE.Vector3( 1, 1, 1,), new THREE.Vector3(5, 0, 0,)])
var tubeGeometry1 = new THREE.TubeGeometry(curve1, 100, 0.05, 50, false);
tubeGeometry.morphAttributes.position = [
tubeGeometry1.attributes.position,
]
var tubeMaterial = new THREE.MeshBasicMaterial({
side:THREE.DoubleSide,
color:'#ffffff',
});
var tube = new THREE.Mesh(tubeGeometry, tubeMaterial);
tube.morphTargetInfluences[0] = 0
scene.add(tube)
In the render function:
tube.morphTargetInfluences[0] += 0.01

Three.js. Get localToWorld from BufferGeometry vertices

I get faces from geometry attributes
get the coordinates for each point of the face
draw lines for each edge of the face
And everything is fine if the object is not moved:
without moving
But if you move, then the lines remain in place:
after moving
If I try to get the world coordinates of points after moving, then I see the same picture:
after moving + localToWorld
If I update matrix world for cube, then I see this:
after moving + localToWorld + updateMatrixWorld
var scene = new THREE.Scene();
scene.background = new THREE.Color().setStyle('#e0e0e0');
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000);
camera.position.set(0, 500, 3000);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
///////////////////////CREATE CUBE////////////////////////////////
const cube = new THREE.Mesh(
new THREE.BoxBufferGeometry(1000, 1000, 1000),
new THREE.MeshBasicMaterial({
color: 'red',
transparent: true,
opacity: 0.4
})
);
scene.add(cube);
//MOVE
cube.position.set(100, 100, 100);
//UPDATE
cube.updateMatrixWorld(true);
//GET EDGE LINES(THREE.Line3) FOR EDGES OF FACES
const lines = getLinesFromFaces(cube);
//CONVERT FROM LOCAL TO WORLD
lines.map(l => {
//l.applyMatrix4(cube.matrixWorld);
//l.start.applyMatrix4(cube.matrixWorld);
//l.end.applyMatrix4(cube.matrixWorld);
cube.localToWorld(l.start);
cube.localToWorld(l.end);
});
//DRAW
drawLines(lines);
function drawLines(lines) {
for (let i = 0; i < lines.length; i += 1) {
addLine(lines[i].start, lines[i].end);
}
}
function addLine(p1, p2) {
const material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
const points = [p1, p2];
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
scene.add(line);
}
function getLinesFromFaces(object) {
const facesWithPos = getFacesWithPos(object.geometry);
const lines = [];
for (let i = 0; i < facesWithPos.length; i += 1) {
const f = facesWithPos[i];
const lineAB = new THREE.Line3(f.a, f.b);
let isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineAB.start) && l.end.equals(lineAB.end)) ||
(l.start.equals(lineAB.end) && l.end.equals(lineAB.start));
});
if (!isExist) lines.push(lineAB);
const lineBC = new THREE.Line3(f.b, f.c);
isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineBC.start) && l.end.equals(lineBC.end)) ||
(l.start.equals(lineBC.end) && l.end.equals(lineBC.start));
});
if (!isExist) lines.push(lineBC);
const lineCA = new THREE.Line3(f.c, f.a);
isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineCA.start) && l.end.equals(lineCA.end)) ||
(l.start.equals(lineCA.end) && l.end.equals(lineCA.start));
});
if (!isExist) lines.push(lineCA);
}
return lines;
}
function getFacesWithPos(geometry) {
const faces = getFaces(geometry);
const facesWithPos = [];
const position = geometry.getAttribute('position');
for (let i = 0; i < faces.length; i += 1) {
const f = faces[i];
facesWithPos.push({
a: new THREE.Vector3(position.array[f.a * 3], position.array[f.a * 3 + 1], position.array[f.a * 3 + 2]),
b: new THREE.Vector3(position.array[f.b * 3], position.array[f.b * 3 + 1], position.array[f.b * 3 + 2]),
c: new THREE.Vector3(position.array[f.c * 3], position.array[f.c * 3 + 1], position.array[f.c * 3 + 2])
});
}
return facesWithPos;
}
function getFaces(geometry) {
const faces = [];
const index = geometry.getIndex();
for (let i = 0; i < index.count; i += 3) {
faces.push({
a: index.getX(i),
b: index.getX(i + 1),
c: index.getX(i + 2)
});
}
return faces;
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.117.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.117.0/examples/js/controls/OrbitControls.js"></script>
How to get world coordinates? what am I doing wrong?
Some of the line points were common objects. If you apply localToWorld to them, then the method was applied to them several times and the result was not correct. Below is the solution
var scene = new THREE.Scene();
scene.background = new THREE.Color().setStyle('#e0e0e0');
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000);
camera.position.set(0, 500, 3000);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
///////////////////////CREATE CUBE////////////////////////////////
const cube = new THREE.Mesh(
new THREE.BoxBufferGeometry(1000, 1000, 1000),
new THREE.MeshBasicMaterial({
color: 'red',
transparent: true,
opacity: 0.4
})
);
scene.add(cube);
//MOVE
cube.position.set(100, 100, 100);
//UPDATE
cube.updateMatrixWorld(true);
//GET EDGE LINES(THREE.Line3) FOR EDGES OF FACES
const lines = getLinesFromFaces(cube);
//CONVERT FROM LOCAL TO WORLD
lines.map(l => {
//l.applyMatrix4(cube.matrixWorld);
//l.start.applyMatrix4(cube.matrixWorld);
//l.end.applyMatrix4(cube.matrixWorld);
cube.localToWorld(l.start);
cube.localToWorld(l.end);
});
//DRAW
drawLines(lines);
function drawLines(lines) {
for (let i = 0; i < lines.length; i += 1) {
addLine(lines[i].start, lines[i].end);
}
}
function addLine(p1, p2) {
const material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
const points = [p1, p2];
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
scene.add(line);
}
function getLinesFromFaces(object) {
const facesWithPos = getFacesWithPos(object.geometry);
const lines = [];
for (let i = 0; i < facesWithPos.length; i += 1) {
const f = facesWithPos[i];
const lineAB = new THREE.Line3(f.a, f.b);
let isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineAB.start) && l.end.equals(lineAB.end)) ||
(l.start.equals(lineAB.end) && l.end.equals(lineAB.start));
});
if (!isExist) lines.push(lineAB.clone());
const lineBC = new THREE.Line3(f.b, f.c);
isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineBC.start) && l.end.equals(lineBC.end)) ||
(l.start.equals(lineBC.end) && l.end.equals(lineBC.start));
});
if (!isExist) lines.push(lineBC.clone());
const lineCA = new THREE.Line3(f.c, f.a);
isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineCA.start) && l.end.equals(lineCA.end)) ||
(l.start.equals(lineCA.end) && l.end.equals(lineCA.start));
});
if (!isExist) lines.push(lineCA.clone());
}
return lines;
}
function getFacesWithPos(geometry) {
const faces = getFaces(geometry);
const facesWithPos = [];
const position = geometry.getAttribute('position');
for (let i = 0; i < faces.length; i += 1) {
const f = faces[i];
facesWithPos.push({
a: new THREE.Vector3(position.array[f.a * 3], position.array[f.a * 3 + 1], position.array[f.a * 3 + 2]),
b: new THREE.Vector3(position.array[f.b * 3], position.array[f.b * 3 + 1], position.array[f.b * 3 + 2]),
c: new THREE.Vector3(position.array[f.c * 3], position.array[f.c * 3 + 1], position.array[f.c * 3 + 2])
});
}
return facesWithPos;
}
function getFaces(geometry) {
const faces = [];
const index = geometry.getIndex();
for (let i = 0; i < index.count; i += 3) {
faces.push({
a: index.getX(i),
b: index.getX(i + 1),
c: index.getX(i + 2)
});
}
return faces;
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.117.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.117.0/examples/js/controls/OrbitControls.js"></script>

Three.js + Tween.js tweening vertices of multiple objects

I am using three.js to create multiple BoxGeometries and tween.js to animate all vertices of the geometry.
At first all cubes should randomly distort untill i call stopTweens(). Then all vertices should go back to their default posiiton.
My problem is, that only the last cube gets reseted visually. The others are also tweened, but it seems like verticesNeedUpdate is not set to true and so nothing is happening on the screen.
Here is my Code:
var scene_03_TweenArr = [];
Function to create Cubes:
function createCubes(){
for (i = 0; i < 10; i++) {
var CubeGeometry = new THREE.BoxGeometry( 4, 4, 4, 5, 5, 5 );
var CubeMaterial = new THREE.MeshPhongMaterial( {color: 0xffffff} );
var cube = new THREE.Mesh( CubeGeometry, CubeMaterial );
scene_03.add( cube );
cube.position.x = -40+5*i;
tt_animate_vertices(cube);
}
}
Function to animate Cubes randomly:
function tt_animate_vertices(object) {
for( j = 0; j < object.geometry.vertices.length; j++ ){
var previousX = object.geometry.vertices[j].x;
var previousY = object.geometry.vertices[j].y;
var previousZ = object.geometry.vertices[j].z;
var tween = new TWEEN.Tween(object.geometry.vertices[j]);
tween.to({ x: (0.25*Math.random()+1)*previousX, y: (0.25*Math.random()+1)*previousY, z: (0.25*Math.random()+1)*previousZ }, 5000 + Math.random()*5000);
var tween2 = new TWEEN.Tween(object.geometry.vertices[j]);
tween2.to({ x: previousX, y: previousY, z: previousZ }, 5000 + Math.random()*5000);
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween2.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.chain(tween2);
tween2.chain(tween);
tween.start();
var verticeTween = [object, tween, tween2, j, previousX, previousY, previousZ ];
scene_03_TweenArr.push(verticeTween);
}
}
Function to stop tweens and reset cubevertices to default:
function stopTweens(){
for (let k = 0; k < scene_03_TweenArr.length; k++) {
scene_03_TweenArr[k][1].stop();
scene_03_TweenArr[k][2].stop();
var object = scene_03_TweenArr[k][0];
var index = scene_03_TweenArr[k][3];
var prevX = scene_03_TweenArr[k][4];
var prevY = scene_03_TweenArr[k][5];
var prevZ = scene_03_TweenArr[k][6];
var tween = new TWEEN.Tween(object.geometry.vertices[index]);
tween.to({x: prevX, y: prevY, z: prevZ }, 300)
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.start();
object.geometry.verticesNeedUpdate = true;
}
}
Here I managed to make it run in JSFiddle with the same behaviour:
http://jsfiddle.net/gfhyvou3/25/
From my experience, it's better to use let keyword inside loops, when you work with arrays.
function stopTweens(){
for (let k = 0; k < scene_03_TweenArr.length; k++) {
scene_03_TweenArr[k][1].stop();
scene_03_TweenArr[k][2].stop();
let object = scene_03_TweenArr[k][0];
let index = scene_03_TweenArr[k][3];
let prevX = scene_03_TweenArr[k][4];
let prevY = scene_03_TweenArr[k][5];
let prevZ = scene_03_TweenArr[k][6];
var tween = new TWEEN.Tween(object.geometry.vertices[index]);
tween.to({x: prevX, y: prevY, z: prevZ }, 300)
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.start();
object.geometry.verticesNeedUpdate = true;
}
}

Resources