I understand PlaneGeometry has been deprecated and we should use PlaneBufferGeometry with the latest releases. The following code worked with the build before R125, I just don't know how to tweak the code to make it work with PlaneBufferGeometry:
var flagGeom = new THREE.PlaneBufferGeometry(4.5, 2.2, 40, 100, 200); // Replaced "PlaneGeometry " with "PlaneBufferGeometry" here.
flagGeom.translate(2.2, 1.1, 0);
flagGeom.vertices.forEach(v => { // Undefined error here.
v.init = v.clone()
});
Looks like your question is related to this forum topic.
You can do the things this way (just an option, not the ultimate solution): use an additional array stored in userData, and use .setXYZ() method.
body{
overflow: hidden;
margin: 0;
}
<script src="https://josephg.github.io/noisejs/perlin.js"></script>
<script type="module">
console.clear();
import * as THREE from 'https://cdn.skypack.dev/three#0.129.0/build/three.module.js';
import { OrbitControls } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/controls/OrbitControls.js';
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(-1, 0.5, 2).setLength(2.5);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x401000);
document.body.appendChild(renderer.domElement);
var controls = new OrbitControls(camera, renderer.domElement);
var light = new THREE.DirectionalLight(0xff5500, 2);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xff0000, 1));
var loader = new THREE.TextureLoader();
// flag
var flagGeom = new THREE.PlaneGeometry(4, 2, 40, 20);
flagGeom.translate(2, 1, 0);
let pos = flagGeom.attributes.position;
flagGeom.userData = {
init: []
}
for(let i = 0; i < pos.count; i++){
flagGeom.userData.init.push(new THREE.Vector3().fromBufferAttribute(pos, i));
}
console.log(flagGeom.userData.init);
var flagMat = new THREE.MeshStandardMaterial({
color: 0x777777,
map: loader.load("https://cywarr.github.io/small-shop/PW_flag/PW_flag_map.png"),
alphaMap: loader.load("https://cywarr.github.io/small-shop/PW_flag/PW_flag_alphaMap.png"),
alphaTest: 0.5,
side: THREE.DoubleSide,
metalness: 0.5,
roughness: 0.5
});
var flag = new THREE.Mesh(flagGeom, flagMat);
flag.position.set(-2, -1, 0);
flag.rotation.x = THREE.Math.degToRad(-18);
scene.add(flag);
// staff
var staff = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 4, 8), new THREE.MeshStandardMaterial({
color: "black"
}));
flag.add(staff);
// background canvas texture
var canvas = document.createElement("canvas");
canvas.width = 128;
canvas.height = 64;
var ctx = canvas.getContext('2d');
var image = ctx.createImageData(canvas.width, canvas.height);
var data = image.data;
var canvasTexture = new THREE.CanvasTexture(canvas);
scene.background = canvasTexture;
window.addEventListener( 'resize', onWindowResize );
var clock = new THREE.Clock();
var delta = 0;
var time = 0;
var v = new THREE.Vector3();
render();
function render() {
requestAnimationFrame(render);
delta = clock.getDelta();
time += delta;
flagGeom.userData.init.forEach( (vi, idx) => {
v.copy(vi);
let yFade = Math.sin(v.y / flagGeom.parameters.height * Math.PI) * 0.25;
v.x = v.x + yFade;
let xFade = (v.x / flagGeom.parameters.width);
v.z = noise.perlin2((v.x - (time * 2)) / flagGeom.parameters.width * 4, v.y / flagGeom.parameters.height * 2) * xFade;
pos.setXYZ(idx, v.x, v.y, v.z);
});
flagGeom.computeVertexNormals();
pos.needsUpdate = true;
drawFrame(time);
canvasTexture.needsUpdate = true;
renderer.render(scene, camera);
}
function drawFrame(time) {
var cWidth = canvas.width;
var cHeight = canvas.height;
for (var x = 0; x < cWidth; x++) {
for (var y = 0; y < cHeight; y++) {
var value = Math.abs(noise.simplex3(x / cWidth, y / cHeight, time * 0.25));
var cell = (x + y * cWidth) * 4;
data[cell] = 128 + Math.floor(128 * value);
data[cell + 1] = Math.floor(256 * value);
data[cell + 2] = 0;
data[cell + 3] = 255; // alpha.
}
}
ctx.fillColor = 'black';
ctx.fillRect(0, 0, 100, 100);
ctx.putImageData(image, 0, 0);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
</script>
Related
I'm using code from this repo - scribble, which is using three.js r87. I followed the Updating THREE.Geometry to THREE.BufferGeometry tutorial in order to upgrade the code to three.js r144. I've got one function correct, but the other one is giving me trouble.
The mousePressed function was easy to update:
function mousePressed() {
const point = new THREE.Vector3(mouseX, mouseY, 0);
// const geometry = new THREE.Geometry();
// geometry.vertices.push(point);
let points = [];
points.push(point);
let geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
scene.add(line);
selected = line;
}
But I updated mouseDragged(), and I don't understand why it's not working:
function mouseDragged() {
const line = selected;
const point = new THREE.Vector3(mouseX, mouseY, 0);
const oldgeometry = line.geometry;
// const newgeometry = new THREE.Geometry();
// newgeometry.vertices = oldgeometry.vertices;
// newgeometry.vertices.push(point);
let newgeometry = new THREE.BufferGeometry();
let positions = oldgeometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const v = new THREE.Vector3(positions[i], positions[i + 1], positions[i + 2]);
positions[i] = v.x;
positions[i + 1] = v.y;
positions[i + 2] = v.z;
}
positions.push(point); // I should just be able to add the new point, no?
newgeometry.attributes.position.needsUpdate = true;
line.geometry = newgeometry;
scene.add(line); // I re-added the line, just in case. But the line does not show up, whether or not I do this.
selected = line;
}
Help is much appreciated!
Thanks
The code from the resource is highly inefficient since you continuously allocate geometries all the time without disposal management which will lead to a memory leak. Try the following approach:
let camera, scene, renderer, line;
const frustumSize = 4;
let index = 0;
const coords = new THREE.Vector3();
init();
render();
function init() {
const aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera(frustumSize * aspect / -2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 0.1, 20);
camera.position.z = 5;
scene = new THREE.Scene();
const geometry = new THREE.BufferGeometry();
const positionAttribute = new THREE.BufferAttribute(new Float32Array(1000 * 3), 3); // allocate large enough buffer
positionAttribute.setUsage(THREE.DynamicDrawUsage);
geometry.setAttribute('position', positionAttribute);
const material = new THREE.LineBasicMaterial()
line = new THREE.Line(geometry, material);
scene.add(line);
// initial points
addPoint(0, 0, 0); // start point
addPoint(1, 0, 0); // current pointer coordinate
//
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.domElement.addEventListener('pointerdown', onPointerDown);
renderer.domElement.addEventListener('pointermove', onPointerMove);
window.addEventListener('resize', onWindowResize);
}
function addPoint(x, y, z) {
const positionAttribute = line.geometry.getAttribute('position');
positionAttribute.setXYZ(index, x, y, z);
positionAttribute.needsUpdate = true;
index++;
line.geometry.setDrawRange(0, index);
}
function updatePoint(x, y, z) {
const positionAttribute = line.geometry.getAttribute('position');
positionAttribute.setXYZ(index - 1, coords.x, coords.y, 0);
positionAttribute.needsUpdate = true;
}
function onPointerDown(event) {
coords.x = (event.clientX / window.innerWidth) * 2 - 1;
coords.y = -(event.clientY / window.innerHeight) * 2 + 1;
coords.z = (camera.near + camera.far) / (camera.near - camera.far);
coords.unproject(camera);
addPoint(coords.x, coords.y, 0);
render();
}
function onPointerMove(event) {
coords.x = (event.clientX / window.innerWidth) * 2 - 1;
coords.y = -(event.clientY / window.innerHeight) * 2 + 1;
coords.z = (camera.near + camera.far) / (camera.near - camera.far);
coords.unproject(camera);
updatePoint(coords.x, coords.y, 0)
render();
}
function onWindowResize() {
const aspect = window.innerWidth / window.innerHeight;
camera.left = -frustumSize * aspect / 2;
camera.right = frustumSize * aspect / 2;
camera.top = frustumSize / 2;
camera.bottom = -frustumSize / 2;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
render();
}
function render() {
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.144/build/three.min.js"></script>
The idea is to allocate a single large buffer to store all current and future points of a line. You then use setDrawRange() to define, what parts of the buffer should be rendered.
I currently learning three js and I was following this tutorial however noticed that the code is invalid for the current version of threejs and you can see the main change below. Going by this document I've updated the bit of code to:
var k = 1;
const positions = sphere.geometry.attributes.position.array;
for (var i = 0; i < positions.length - 2; i++) {
const v = new THREE.Vector3(
positions[i],
positions[i + 1],
positions[i + 2]
).normalize()
.multiplyScalar(
1 + 0.3 * noise.perlin3(
positions[i] * k + time,
positions[i + 1] * k,
positions[i + 2] * k));
positions[i] = v.x;
positions[i + 1] = v.y;
positions[i + 2] = v.z;
}
sphere.geometry.attributes.position.needsUpdate = true;
However as you can see from the snippet the vertex displacement is not smooth and does not resemble the tutorial. What I'm missing here?
var renderer = new THREE.WebGLRenderer({ canvas : document.getElementById('canvas'), antialias:true});
// default bg canvas color //
renderer.setClearColor(0x7b7b7b);
// use device aspect ratio //
renderer.setPixelRatio(window.devicePixelRatio);
// set size of canvas within window //
renderer.setSize(window.innerWidth, window.innerHeight);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 5;
var sphere_geometry = new THREE.SphereGeometry(1, 128, 128);
const material = new THREE.MeshPhongMaterial({
color: 0x0000FF,
shininess: 1000,
})
var sphere = new THREE.Mesh(sphere_geometry, material);
scene.add(sphere);
const ambientLight = new THREE.AmbientLight(0x798296)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(5, 10, 7)
scene.add(directionalLight);
sphere.geometry.attributes.position.needsUpdate = true;
var update = function() {
// change '0.003' for more aggressive animation
var time = performance.now() * 0.003;
//console.log(time)
//go through vertices here and reposition them
// change 'k' value for more spikes
var k = 1;
const positions = sphere.geometry.attributes.position.array;
for (var i = 0; i < positions.length - 2; i++) {
const v = new THREE.Vector3(
positions[i],
positions[i + 1],
positions[i + 2]
).normalize()
.multiplyScalar(
1 + 0.3 * noise.perlin3(positions[i] * k + time, positions[i + 1] * k,
positions[i + 2] * k));
positions[i] = v.x;
positions[i + 1] = v.y;
positions[i + 2] = v.z;
}
sphere.geometry.attributes.position.needsUpdate = true;
}
function animate() {
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
update();
/* render scene and camera */
renderer.render(scene,camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
html, body {
margin:0;
overflow:hidden
}
<script src="https://fariskassim.com/stage/rebel9/teaf/blob/v4/js/perlin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<canvas id="canvas"></canvas>
This is how you can deform a sphere with noise, using BufferGeometry:
var renderer = new THREE.WebGLRenderer({ canvas : document.getElementById('canvas'), antialias:true});
// default bg canvas color //
renderer.setClearColor(0x7b7b7b);
// use device aspect ratio //
renderer.setPixelRatio(window.devicePixelRatio);
// set size of canvas within window //
renderer.setSize(window.innerWidth, window.innerHeight);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 5;
var sphere_geometry = new THREE.SphereGeometry(1, 128, 128);
const material = new THREE.MeshPhongMaterial({
color: 0x0000FF,
shininess: 1000,
})
var sphere = new THREE.Mesh(sphere_geometry, material);
scene.add(sphere);
const ambientLight = new THREE.AmbientLight(0x798296)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(5, 10, 7)
scene.add(directionalLight);
sphere.geometry.attributes.position.needsUpdate = true;
var update = function() {
// change '0.003' for more aggressive animation
var time = performance.now() * 0.003;
//console.log(time)
//go through vertices here and reposition them
// change 'k' value for more spikes
var k = 1;
var v3 = new THREE.Vector3();
const positions = sphere.geometry.attributes.position;
for (var i = 0; i < positions.count; i++) {
v3.fromBufferAttribute(positions, i).setLength(k);
let n = noise.perlin3(v3.x, v3.y, v3.z);
v3.setLength(1 + 0.3 * n);
positions.setXYZ(i, v3.x, v3.y, v3.z);
}
positions.needsUpdate = true;
sphere.geometry.computeVertexNormals(); // don't forget to call this
}
function animate() {
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
update();
/* render scene and camera */
renderer.render(scene,camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
html, body {
margin:0;
overflow:hidden
}
<script src="https://fariskassim.com/stage/rebel9/teaf/blob/v4/js/perlin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<canvas id="canvas"></canvas>
I am trying to achieve something like this:
I have a Polyline and its points now I want to create a sheet same as shown in the image. Above is polyline and below a sheet created from that polyline points.
I followed solution from this post Extruding a line in three.js but when I try this it renders nothing.
Here is the code which I tried:
let containerThreeJs = document.getElementById('threed-view-container');
let w = containerThreeJs.offsetWidth;
let h = containerThreeJs.offsetHeight;
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(w, h);
containerThreeJs.appendChild(renderer.domElement);
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(5, 1, 1, 1000);
camera.position.setScalar(300);
let threeDpoints = [
[88.5, 370],
[229.5, 268],
[300.5, 333],
[373.5, 290],
[426.5, 392]
];
let geometry = extrudePath(threeDpoints, 100);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
side: THREE.DoubleSide
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
render();
function resize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resize(renderer)) {
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
function extrudePath(points, depth) {
var geometry = new THREE.PlaneGeometry(10, 10, points.length - 1, 1);
var vertices = geometry.vertices;
// if I comment this loop then the plane is visible
for (var i = 0, l = points.length, p; i < l; i++) {
p = points[i];
vertices[i].x = vertices[i + l].x = p[0];
vertices[i].y = vertices[i + l].y = p[1];
vertices[i].z = p[2];
vertices[i + l].z = p[2] + depth;
}
geometry.computeFaceNormals();
return geometry;
}
<script src="http://mrdoob.github.io/three.js/build/three.min.js"></script>
<script src=http://mrdoob.github.io/three.js/examples/js/controls/OrbitControls.js></script>
<div id="threed-view-container" style="width: 100%; height: 500px"></div>
If I remove the for-loop from extrudePath then the simple plane is visible, but if I keep it nothing seems to render.
I'm not sure, but i see this: In the points array threeDpoints, every point has 2 parts. However in the extrudePath function it's looking for 3. (p[0], p[1], p[2])
`let threeDpoints = [
[88.5, 370],
[229.5, 268],
[300.5, 333],
[373.5, 290],
[426.5, 392]
];
`
`p = points[i];
vertices[i].x = vertices[i + l].x = p[0];
vertices[i].y = vertices[i + l].y = p[1];
vertices[i].z = p[2];
vertices[i + l].z = p[2] + depth;`
I got this javascript code from somewhere, but i'm not sure how assigning js codes to classes work. I'm not so good with Js..any help will be greatly appreciated.
I would like for the js to be assigned to the 'intro-section' class. Its 3D butterfly animation.
Initially though, I wanted it to be assigned to an entire page..and maybe make it stick to the back and not move when the page scrolls down.
Index.html
<!-- Intro section start -->
<section class="intro-section sp-pad spad" id="demo">
<div class="container-fluid">
<div class="row">
<div class="col-xl-5 intro-text">
<span class="sp-sub-title">Play, Learn & Grow Together.</span>
<h3 class="sp-title">Welcome To Bright Beginnings</h3>
<p>The atmosphere of the school will be relaxed and will encourage freedom of expression as a means
to individual growth. Children should be free to be children – to act, choose, explore, test,
investigate, react, and experiment. Hence, children grow naturally in their new awareness of
themselves, their relationships towards/with others, and with the world around them.</p>
Read More
</div>
<div class="col-xl-6 offset-xl-1">
<figure class="intro-img mt-5 mt-xl-0">
<img src="img/intro.jpg" alt="">
</figure>
</div>
</div>
</div>
</section>
<!-- Intro section end -->
Javascript:
const nbButterflies = 100;
var conf, scene, camera, cameraCtrl, light, renderer;
var whw, whh;
var butterflies;
var bodyTexture, wingTexture1, wingTexture2, wingTexture3, bodyTexture4, wingTexture4;
var destination = new THREE.Vector3();
var mouse = new THREE.Vector2();
var mouseOver = false;
var mousePlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
var mousePosition = new THREE.Vector3();
var raycaster = new THREE.Raycaster();
function init() {
conf = {
attraction: 0.03,
velocityLimit: 1.2,
move: true,
followMouse: true,
shuffle: shuffle
};
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
cameraCtrl = new THREE.OrbitControls(camera);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
initScene();
const gui = new dat.GUI();
gui.add(conf, 'move');
gui.add(conf, 'followMouse');
gui.add(conf, 'attraction', 0.01, 0.1);
gui.add(conf, 'velocityLimit', 0.1, 2);
gui.add(conf, 'shuffle');
gui.close();
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
document.addEventListener('mousemove', onMouseMove, false);
// document.addEventListener('mouseover', function () { mouseOver = true; }, false);
document.addEventListener('mouseout', function () { mouseOver = false; }, false);
animate();
};
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
camera.position.z = 75;
bodyTexture = new THREE.TextureLoader().load('https://klevron.github.io/codepen/butterflies/b1.png');
wingTexture1 = new THREE.TextureLoader().load('https://klevron.github.io/codepen/butterflies/b1w.png');
wingTexture2 = new THREE.TextureLoader().load('https://klevron.github.io/codepen/butterflies/b2w.png');
wingTexture3 = new THREE.TextureLoader().load('https://klevron.github.io/codepen/butterflies/b3w.png');
bodyTexture4 = new THREE.TextureLoader().load('https://klevron.github.io/codepen/butterflies/b4.png');
wingTexture4 = new THREE.TextureLoader().load('https://klevron.github.io/codepen/butterflies/b4w.png');
butterflies = [];
for (var i = 0; i < nbButterflies; i++) {
var b = new Butterfly();
butterflies.push(b);
scene.add(b.o3d);
}
shuffle();
}
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
cameraCtrl.update();
if (conf.move) {
for (var i = 0; i < butterflies.length; i++) {
butterflies[i].move();
}
}
renderer.render(scene, camera);
};
function shuffle() {
for (var i = 0; i < butterflies.length; i++) {
butterflies[i].shuffle();
}
}
function Butterfly() {
this.minWingRotation = -Math.PI / 6;
this.maxWingRotation = Math.PI / 2 - 0.1;
this.wingRotation = 0;
this.velocity = new THREE.Vector3(rnd(1, true), rnd(1, true), rnd(1, true));
this.destination = destination;
var confs = [
{ bodyTexture: bodyTexture, bodyW: 10, bodyH: 15, wingTexture: wingTexture1, wingW: 10, wingH: 15, wingX: 5.5 },
{ bodyTexture: bodyTexture, bodyW: 6, bodyH: 9, wingTexture: wingTexture2, wingW: 15, wingH: 20, wingX: 7.5 },
{ bodyTexture: bodyTexture, bodyW: 8, bodyH: 12, wingTexture: wingTexture3, wingW: 10, wingH: 15, wingX: 5.5 },
{ bodyTexture: bodyTexture4, bodyW: 6, bodyH: 10, bodyY: 2, wingTexture: wingTexture4, wingW: 15, wingH: 20, wingX: 8 },
];
this.init(confs[Math.floor(rnd(4))]);
}
Butterfly.prototype.init = function (bconf) {
var geometry = new THREE.PlaneGeometry(bconf.wingW, bconf.wingH);
var material = new THREE.MeshBasicMaterial({ transparent: true, map: bconf.wingTexture, side: THREE.DoubleSide, depthTest: false });
var lwmesh = new THREE.Mesh(geometry, material);
lwmesh.position.x = -bconf.wingX;
this.lwing = new THREE.Object3D();
this.lwing.add(lwmesh);
var rwmesh = new THREE.Mesh(geometry, material);
rwmesh.rotation.y = Math.PI;
rwmesh.position.x = bconf.wingX;
this.rwing = new THREE.Object3D();
this.rwing.add(rwmesh);
geometry = new THREE.PlaneGeometry(bconf.bodyW, bconf.bodyH);
material = new THREE.MeshBasicMaterial({ transparent: true, map: bconf.bodyTexture, side: THREE.DoubleSide, depthTest: false });
this.body = new THREE.Mesh(geometry, material);
if (bconf.bodyY) this.body.position.y = bconf.bodyY;
// this.body.position.z = -0.1;
this.group = new THREE.Object3D();
this.group.add(this.body);
this.group.add(this.lwing);
this.group.add(this.rwing);
this.group.rotation.x = Math.PI / 2;
this.group.rotation.y = Math.PI;
this.setWingRotation(this.wingRotation);
this.initTween();
this.o3d = new THREE.Object3D();
this.o3d.add(this.group);
};
Butterfly.prototype.initTween = function () {
var duration = limit(conf.velocityLimit - this.velocity.length(), 0.1, 1.5) * 1000;
this.wingRotation = this.minWingRotation;
this.tweenWingRotation = new TWEEN.Tween(this)
.to({ wingRotation: this.maxWingRotation }, duration)
.repeat(1)
.yoyo(true)
// .easing(TWEEN.Easing.Cubic.InOut)
.onComplete(function(object) {
object.initTween();
})
.start();
};
Butterfly.prototype.move = function () {
var destination;
if (mouseOver && conf.followMouse) {
destination = mousePosition;
} else {
destination = this.destination;
}
var dv = destination.clone().sub(this.o3d.position).normalize();
this.velocity.x += conf.attraction * dv.x;
this.velocity.y += conf.attraction * dv.y;
this.velocity.z += conf.attraction * dv.z;
this.limitVelocity();
// update position & rotation
this.setWingRotation(this.wingRotation);
this.o3d.lookAt(this.o3d.position.clone().add(this.velocity));
this.o3d.position.add(this.velocity);
};
Butterfly.prototype.limitVelocity = function (y) {
this.velocity.x = limit(this.velocity.x, -conf.velocityLimit, conf.velocityLimit);
this.velocity.y = limit(this.velocity.y, -conf.velocityLimit, conf.velocityLimit);
this.velocity.z = limit(this.velocity.z, -conf.velocityLimit, conf.velocityLimit);
};
Butterfly.prototype.setWingRotation = function (y) {
this.lwing.rotation.y = y;
this.rwing.rotation.y = -y;
};
Butterfly.prototype.shuffle = function () {
this.velocity = new THREE.Vector3(rnd(1, true), rnd(1, true), rnd(1, true));
var p = new THREE.Vector3(rnd(1, true), rnd(1, true), rnd(1, true)).normalize().multiplyScalar(100);
this.o3d.position.set(p.x, p.y, p.z);
var scale = rnd(0.4) + 0.1;
this.o3d.scale.set(scale, scale, scale);
}
function limit(number, min, max) {
return Math.min(Math.max(number, min), max);
}
function rnd(max, negative) {
return negative ? Math.random() * 2 * max - max : Math.random() * max;
}
function onWindowResize() {
whw = window.innerWidth / 2;
whh = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onMouseMove(event) {
// if (cameraCtrl.getState()!=-1) return;
var v = new THREE.Vector3();
camera.getWorldDirection(v);
v.normalize();
// console.log(v);
mousePlane.normal = v;
mouseOver = true;
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(mousePlane, mousePosition);
}
init();
So as the title states I'd like to know how to plot randomly generated meshes at the y-position that matches the terrain's corresponding y-position in three.js. I've looked through the docs and feel like using a raycaster might work, but I can only see examples that uses the detection as a mouse event, and not before render, so I'm not sure how to implement it.
Here is my code for the terrain, heightmap, and mesh plotting so far. It all works technically, but as you can see the plotAssets meshes y-positions are just sitting at zero right now. Any insights would be very much appreciated, I'm pretty new to three.js.
Terrain:
var heightmaploader = new THREE.ImageLoader();
heightmaploader.load(
"assets/noisemaps/cloud.png",
function(img) {
data = getHeightData(img);
var terrainG = new THREE.PlaneBufferGeometry(700, 700, worldWidth - 1, worldDepth - 1);
terrainG.rotateX(-Math.PI / 2);
var vertices = terrainG.attributes.position.array;
for (var i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) {
vertices[j + 1] = data[i] * 5;
}
terrainG.computeFaceNormals();
terrainG.computeVertexNormals();
var material = new THREE.MeshLambertMaterial({
map: terrainT,
//side: THREE.DoubleSide,
color: 0xffffff,
transparent: false,
});
terrain = new THREE.Mesh(terrainG, material);
terrain.receiveShadow = true;
terrain.castShadow = true;
terrain.position.y = 0;
scene.add(terrain);
plotAsset('boulder-photo-01.png', 30, 18, data);
plotAsset('boulder-outline-01.png', 20, 20, data);
plotAsset('birch-outline-01.png', 10, 50, data);
plotAsset('tree-photo-01.png', 20, 50, data);
plotAsset('grass-outline-01.png', 10, 20, data);
plotAsset('grass-outline-02.png', 10, 20, data);
}
);
Plot Assets:
function plotAsset(texturefile, amount, size, array) {
console.log(array);
var loader = new THREE.TextureLoader();
loader.load(
"assets/textures/objects/" + texturefile,
function(texturefile) {
var geometry = new THREE.PlaneGeometry(size, size, 10, 1);
var material = new THREE.MeshBasicMaterial({
color: 0xFFFFFF,
map: texturefile,
side: THREE.DoubleSide,
transparent: true,
depthWrite: false,
depthTest: false,
alphaTest: 0.5,
});
var uniforms = { texture: { value: texturefile } };
var vertexShader = document.getElementById( 'vertexShaderDepth' ).textContent;
var fragmentShader = document.getElementById( 'fragmentShaderDepth' ).textContent;
// add bunch o' stuff
for (var i = 0; i < amount; i++) {
var scale = Math.random() * (1 - 0.8 + 1) + 0.8;
var object = new THREE.Mesh(geometry, material);
var x = Math.random() * 400 - 400 / 2;
var z = Math.random() * 400 - 400 / 2;
object.rotation.y = 180 * Math.PI / 180;
//object.position.y = size * scale / 2;
object.position.x = x;
object.position.z = z;
object.position.y = 0;
object.castShadow = true;
object.scale.x = scale; // random scale
object.scale.y = scale;
object.scale.z = scale;
scene.add(object);
object.customDepthMaterial = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.DoubleSide
} );
}
}
);
}
Height Data:
function getHeightData(img) {
var canvas = document.createElement('canvas');
canvas.width = 2048 / 8;
canvas.height = 2048 / 8;
var context = canvas.getContext('2d');
var size = 2048 / 8 * 2048 / 8,
data = new Float32Array(size);
context.drawImage(img, 0, 0);
for (var i = 0; i < size; i++) {
data[i] = 0
}
var imgd = context.getImageData(0, 0, 2048 / 8, 2048 / 8);
var pix = imgd.data;
var j = 0;
for (var i = 0, n = pix.length; i < n; i += (4)) {
var all = pix[i] + pix[i + 1] + pix[i + 2];
data[j++] = all / 40;
}
return data;
}
Yes, using of THREE.Raycaster() works well.
A raycaster has the .set(origin, direction) method. The only thing you have to do here is to set the point of origin higher than the highest point of the height map.
var n = new THREE.Mesh(...); // the object we want to aling along y-axis
var collider = new THREE.Raycaster();
var shiftY = new THREE.Vector3();
var colliderDir = new THREE.Vector3(0, -1, 0); // down along y-axis to the mesh of height map
shiftY.set(n.position.x, 100, n.position.z); // set the point of the origin
collider.set(shiftY, colliderDir); //set the ray of the raycaster
colliderIntersects = collider.intersectObject(plane); // plane is the mesh of height map
if (colliderIntersects.length > 0){
n.position.y = colliderIntersects[0].point.y; // set the position of the object
}
jsfiddle example