I am trying to create and display a model with per-vertix color and transparency.
Demo app here: https://diego3d.azurewebsites.net/repl (click on Run and wait 10 to 15 seconds).
Using SharpGLTF to generate the model as follows:
for (int i = 0; i < mesh.IndexCount / 3; i++)
{
var v0 = mesh.Vertices[mesh.Indices[3 * i + 0]];
var v1 = mesh.Vertices[mesh.Indices[3 * i + 1]];
var v2 = mesh.Vertices[mesh.Indices[3 * i + 2]];
var VERTEX0 = new VERTEXPOSITIONNORMAL((float)v0.Position.X, (float)v0.Position.Y, (float)v0.Position.Z, (float)v0.Normal.X, (float)v0.Normal.Y, (float)v0.Normal.Z);
var VERTEX1 = new VERTEXPOSITIONNORMAL((float)v1.Position.X, (float)v1.Position.Y, (float)v1.Position.Z, (float)v1.Normal.X, (float)v1.Normal.Y, (float)v1.Normal.Z);
var VERTEX2 = new VERTEXPOSITIONNORMAL((float)v2.Position.X, (float)v2.Position.Y, (float)v2.Position.Z, (float)v2.Normal.X, (float)v2.Normal.Y, (float)v2.Normal.Z);
var c0 = new VERTEXCOLOR(new Vector4(v0.Color.R / 255f, v0.Color.G / 255f, v0.Color.B / 255f, v0.Color.A / 255f));
var c1 = new VERTEXCOLOR(new Vector4(v1.Color.R / 255f, v1.Color.G / 255f, v1.Color.B / 255f, v1.Color.A / 255f));
var c2 = new VERTEXCOLOR(new Vector4(v2.Color.R / 255f, v2.Color.G / 255f, v2.Color.B / 255f, v2.Color.A / 255f));
glbPrim.AddTriangle((VERTEX0, c0), (VERTEX1, c1), (VERTEX2, c2));
}
Using Three.js GLTFLoader to display the model as follows:
new RGBELoader()
.setDataType(THREE.UnsignedByteType)
.setPath('../js/ThreeJs/models/textures/equirectangular/')
.load('studio_small_04_1k.hdr', function (texture) {
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
scene.background = envMap;
scene.environment = envMap;
texture.dispose();
pmremGenerator.dispose();
render();
const loader = new GLTFLoader().setPath('../api/').setRequestHeader({ codeText: '' });
loader.parse(data, '../api/', function (gltf) {
var object = gltf.scene;
object.scale.set(scale, scale, scale);
scene.remove(scene.getObjectByName("glbModel"));
object.name = 'glbModel';
lastModel = object;
scene.add(object);
render();
});
console.log(data.length)
});
The model displays as created but no transparency. I'm new to GLTF and Three.js, any pointers on this? Thanks.
Related
I'm trying to add the length of the start point and endpoint of the line geometry. I have a line but I have no idea how to show some unit measurement data in the form of the text while drawing the wall in mouse move itself.
Here's the fiddle
var renderer, scene, camera;
var line;
var count = 0;
var mouse = new THREE.Vector3();
init();
animate();
function init() {
// info
var info = document.createElement('div');
info.style.position = 'absolute';
info.style.top = '30px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.style.color = '#fff';
info.style.fontWeight = 'bold';
info.style.backgroundColor = 'transparent';
info.style.zIndex = '1';
info.style.fontFamily = 'Monospace';
info.innerHTML = "three.js - animated line using BufferGeometry";
document.body.appendChild(info);
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 0, 1000);
// geometry
var geometry = new THREE.BufferGeometry();
var MAX_POINTS = 500;
positions = new Float32Array(MAX_POINTS * 3);
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
// material
var material = new THREE.LineBasicMaterial({
color: 0xff0000,
linewidth: 2
});
// line
line = new THREE.Line(geometry, material);
scene.add(line);
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener('mousedown', onMouseDown, false);
}
// update line
function updateLine() {
positions[count * 3 - 3] = mouse.x;
positions[count * 3 - 2] = mouse.y;
positions[count * 3 - 1] = mouse.z;
line.geometry.attributes.position.needsUpdate = true;
}
// mouse move handler
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
mouse.z = 0;
mouse.unproject(camera);
if( count !== 0 ){
updateLine();
}
}
// add point
function addPoint(event){
console.log("point nr " + count + ": " + mouse.x + " " + mouse.y + " " + mouse.z);
positions[count * 3 + 0] = mouse.x;
positions[count * 3 + 1] = mouse.y;
positions[count * 3 + 2] = mouse.z;
count++;
line.geometry.setDrawRange(0, count);
updateLine();
}
// mouse down handler
function onMouseDown(evt) {
// on first click add an extra point
if( count === 0 ){
addPoint();
}
addPoint();
}
// render
function render() {
renderer.render(scene, camera);
}
// animate
function animate() {
requestAnimationFrame(animate);
render();
}
I'm trying to achieve the measurement like the above.
The simplest way is to use CSS2D labels. You can assign a regular HTML <div> to act as a label. It displays flat on top of your <canvas> renderer without rotations so it's always facing the camera. See here for a demo on how to set up your CSS2DRenderer:
https://threejs.org/examples/#css2d_label
All you'd have to do is take the average of 2 Vector3s, and assign that as your label's position.
// Get distance and midpoint
var distance = vectorA.distanceTo(vectorB);
var midpoint = new Vector3();
midpoint.copy(vectorA);
midpoint.add(vectorB).multiplyScalar(0.5);
// Create label, set distance and position
const labelDiv = document.createElement( 'div' );
labelDiv.className = 'label';
labelDiv.textContent = "Distance: " + distance;
labelDiv.style.marginTop = '-1em';
const distLabel = new CSS2DObject( labelDiv );
distLabel.position.copy( midpoint );
cssScene.add( distLabel );
// THREEJS RELATED VARIABLES
var scene, camera, renderer, container, controls, raycaster, sphere_radius, HEIGHT, WIDTH;
//INIT THREE JS, SCREEN AND MOUSE EVENTS
function createScene() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
scene = new THREE.Scene();
aspectRatio = WIDTH / HEIGHT;
camera = new THREE.PerspectiveCamera(60, WIDTH / HEIGHT, 1, 10000);
camera.position.x = 0;
camera.position.z = 1500;
camera.position.y = 0;
// ORBIT CAMERA
controls = new THREE.OrbitControls( camera );
controls.update();
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(WIDTH, HEIGHT);
container = document.getElementById('world');
container.appendChild(renderer.domElement);
}
function randomSpherePoint(x0,y0,z0,radius){
var u = Math.random();
var v = Math.random();
var theta = 2 * Math.PI * u;
var phi = Math.acos(2 * v - 1);
var x = x0 + (radius * Math.sin(phi) * Math.cos(theta));
var y = y0 + (radius * Math.sin(phi) * Math.sin(theta));
var z = z0 + (radius * Math.cos(phi));
return [x,y,z];
}
Island = function(){
this.mesh = new THREE.Object3D();
// number of cubes
this.nLands = 30;
this.lands = [];
for(var i = 0; i < this.nLands; i++){
var c = new Land();
this.lands.push(c);
var stepAngle = Math.PI*2 * Math.random();
var a = stepAngle*i;
var h = sphere_radius;
var randPos = randomSpherePoint(0,0,0,sphere_radius);
c.mesh.position.y = randPos[1];
c.mesh.position.x = randPos[0];
c.mesh.position.z = randPos[2];
this.mesh.add(c.mesh);
}
}
Land = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = "land";
var geom = new THREE.CylinderGeometry( 2, 2, 50, 64 );
var mat = new THREE.MeshPhongMaterial({color:0x59332e});
var m = new THREE.Mesh(geom.clone(), mat);
m.position.x = 0;
m.position.y = Math.random()*2;
m.position.z = Math.random()*10;
this.mesh.add(m);
}
Sea = function(){
// radius top, radius bottom, height, number of segments on the radius, number of segments vertically
sphere_radius = 300;
var geom = new THREE.SphereGeometry(sphere_radius,sphere_radius,32,32);
var mat = new THREE.MeshNormalMaterial({
transparent:true,
opacity:.5,
flatShading:true,
});
this.mesh = new THREE.Mesh(geom, mat);
this.mesh.name = "sea";
}
// 3D Models
var sea;
function createSea(){
sea = new Sea();
sea.mesh.position.y = 0;
scene.add(sea.mesh);
}
function createIsland(){
island = new Island();
island.mesh.position.y = 0;
scene.add(island.mesh);
}
function loop(){
renderer.render(scene, camera);
requestAnimationFrame(loop);
}
function init(event){
createScene();
createSea();
createIsland();
loop();
}
window.addEventListener('load', init, false);
function handleWindowResize() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
window.addEventListener('resize', handleWindowResize, false);
html, body {
overflow: hidden;
margin:0;
padding:0;
}
<div id="world"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r94/js/controls/OrbitControls.js"></script>
Related codepen:
https://codepen.io/farisk/pen/zLymrz
On three.js, i have a bunch of cylinders added to random positions on the exterior of sphere.
The issue im having now is that they're all facing straight up to the z-axis.
Im thinking that what i should do is to make the cylinder lookAt the angle of where the cylinder intersects the sphere. But im not sure how to achieve this. Any help is appreciated.
Heres a image that describes the situation.
I am trying to add different image to each face of a cylinder in three.js, basically I want the top, bottom and side to be different images.
This is code where I have added one image which wraps the complete cylinder.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.CylinderGeometry(0.9,1,0.5,32,1, false);
var material = new THREE.MeshPhongMaterial({color: 0xffffff, side:THREE.DoubleSide, map: THREE.ImageUtils.loadTexture('cake-texture-nice-golden-ginger-33420104.jpg')});
var cone = new THREE.Mesh(geometry, material);
scene.add(cone);
var width = window.innerWidth; var height = window.innerHeight; var screenW = window.innerWidth; var screenH = window.innerHeight; /*SCREEN*/ var spdx = 0, spdy = 0; mouseX = 0, mouseY = 0, mouseDown = false; /*MOUSE*/ document.body.addEventListener("mousedown", function(event) { mouseDown = true }, false); document.body.addEventListener("mouseup", function(event) { mouseDown = false }, false); function animate() { spdy = (screenH / 2 - mouseY) / 40; spdx = (screenW / 2 - mouseX) / 40; if (mouseDown){ cone.rotation.x = spdy; cone.rotation.y = spdx; } requestAnimationFrame( animate ); render(); } // create a point light var pointLight = new THREE.PointLight( 0xFFFF8F ); // set its position pointLight.position.x = 10; pointLight.position.y = 50; pointLight.position.z = 130; // add to the scene scene.add(pointLight); camera.position.z = 5; var render = function () { requestAnimationFrame(render); //cone.rotation.x += 0.01; //cone.rotation.y += 0.001; //cone.rotation.z -= 0.02; window.addEventListener('mousemove', function (e) { var mouseX = ( e.clientX - width / 2 ); var mouseY = ( e.clientY - height / 2 ); cone.rotation.x = mouseY * 0.005; cone.rotation.y = mouseX * 0.005; cone.rotation.y += mouseY; //console.log(mouseY); }, false); renderer.render(scene, camera); }; render();
This is the pen for the cylinder: http://codepen.io/dilipmerani/pen/XmWNdV
Update-25Sep
var materialTop = new THREE.MeshPhongMaterial({color: 0xffffff, side:THREE.DoubleSide, map: THREE.ImageUtils.loadTexture('chocolate_brown_painted_textured_wall_tileable.jpg')});
var materialSide = new THREE.MeshPhongMaterial({color: 0xffffff, side:THREE.DoubleSide, map: THREE.ImageUtils.loadTexture('cake-texture-nice-golden-ginger-33420104.jpg')});
var materialBottom = new THREE.MeshPhongMaterial({color: 0xffffff, side:THREE.DoubleSide, map: THREE.ImageUtils.loadTexture('cake-texture-nice-golden-ginger-33420104.jpg')});
var materialsArray = [];
materialsArray.push(materialTop); //materialindex = 0
materialsArray.push(materialSide); // materialindex = 1
materialsArray.push(materialBottom); // materialindex = 2
var material = new THREE.MeshFaceMaterial(materialsArray);
var geometry = new THREE.CylinderGeometry(0.9,1,0.5,3,1, false);
var aFaces = geometry.faces.length;
console.log(aFaces);
for(var i=0;i<aFaces;i++) {
geometry.faces[i].materialindex;
}
var cone = new THREE.Mesh(geometry, material);
scene.add(cone);
Thanks
Create MeshFaceMaterial:
var materialTop = new THREE.MeshPhongMaterial(...);
var materialSide = new THREE.MeshPhongMaterial(...);
var materialBottom = new THREE.MeshPhongMaterial(...);
var materialsArray = [];
materialsArray.push(materialTop); //materialindex = 0
materialsArray.push(materialSide); // materialindex = 1
materialsArray.push(materialBottom); // materialindex = 2
var material = new THREE.MeshFaceMaterial(materialsArray);
Update geometry:
var geometry = new THREE.CylinderGeometry(0.9,1,0.5,32,1, false);
faces you can get from geometry.faces
Loop faces and change materialindex: geometry.faces[faceIndex].materialindex
Print geometry.faces to console and check what it has.
var aFaces = geometry.faces.length;
for(var i=0;i<aFaces;i++) {
if(i < 64){
geometry.faces[i].materialIndex = 0;
}else if(i > 63 && i < 96){
geometry.faces[i].materialIndex = 1;
}else{
geometry.faces[i].materialIndex = 2;
}
}
Build your cone
var cone = new THREE.Mesh(geometry, material);
Example of your updated code
You should create faces in mesh's geometry with some materialindex. So you'll have 3 surfaces. And than use MeshFaceMaterial(array of material for each surface).
Design.prototype.get3dPointZAxis = function(event) {
var mouseX = event.clientX - this.container.getClientRects()[0].left;
var mouseY = event.clientY - this.container.getClientRects()[0].top;
var vector = new THREE.Vector3(( mouseX / this.container.offsetWidth ) * 2 - 1, mouseY / this.container.offsetHeight ) * 2 + 1, 0.5 );
this.projector.unprojectVector( vector, this.camera );
var dir = vector.sub( this.camera.position ).normalize();
var distance = - this.camera.position.z / dir.z;
var pos = this.camera.position.clone().add( dir.multiplyScalar( distance ) );
return pos;
};
Design.prototype.stopDraw= function(event) {
this.twoDLine.push(this.tempLine.geometry);
this.tempLine = null;
this.lastPoint = null;
};
Design.prototype.startDraw= function(event) {
this.lastPoint = this.get3dPointZAxis(event);
};
Design.prototype.handleMouseMove = function(event) {
if (this.lastPoint) {
var pos = this.get3dPointZAxis(event);
if (!this.tempLine) {
var material = new THREE.LineBasicMaterial({
color: 0x0089ff
});
var geometry = new THREE.Geometry();
geometry.vertices.push(this.lastPoint);
geometry.vertices.push(pos);
this.tempLine = new THREE.Line(geometry, material);
this.scene.add(this.tempLine);
}
else {
this.tempLine.geometry.verticesNeedUpdate = true;
this.tempLine.geometry.vertices[1] = pos;
}
}
};
I am using mouse events to draw 2D lines and shape inside a container. I need to convert these lines and shapes into 3D. I was trying the following technique... but getting no result. Please please Help!
Design.prototype.convertTo3D = function() {
console.log("convertTo3D");
this.tempLine.geometry.vertices.z = 10;
};
Design.prototype.convertTo3D = function () {
for (var i in this.linesToDraw) {
var line = this.linesToDraw[i];
this.scene.remove(line);
var x1 = line.geometry.vertices[0].x;
var y1 = line.geometry.vertices[0].y;
var x2 = line.geometry.vertices[1].x;
var y2 = line.geometry.vertices[1].y;
var length = Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));
var slope = (y2 - y1) / (x2 - x1);
var xMid = (x2 + x1) / 2;
var yMid = (y2 + y1) / 2;
slope = Math.atan(slope);
var material = new THREE.MeshPhongMaterial({ambient: 0x030303,shading: THREE.FlatShading});
var geometry = new THREE.CubeGeometry(length, 1, 6);
var cube = new THREE.Mesh(geometry, material);
cube.addEventListener("click", function (event) {
console.log("CUBE CLICKED");
}, false);
cube.rotateZ(slope);
cube.position = new THREE.Vector3(xMid, yMid, 3);
cube.castShadow = true;
cube.receiveShadow = true;
this.scene.add(cube);
}
When we load an Object3D with OBJMTLLoader, it is not possible to use raycaster to pick this object with mouse. Intersection array length is always 0. Any one knows the reason? Below is the code...
The loader routine
var loader2 = new THREE.OBJMTLLoader();
loader2.load('/assets/unwrap/masa/dogtasmasa.obj', '/assets/unwrap/masa/dogtasmasa.mtl', function (object) {
object.position.y = 1.5;
object.position.x = 0;
object.position.z = 2;
object.rotateX(-Math.PI / 2);
object.rotateZ(-Math.PI / 2);
object.scale.set(0.04, 0.04, 0.04);
object.castShadow = true;
scene.add(object);
});
and the picking
function onDocumentMouseDown(event) {
event.preventDefault();
SCREEN_WIDTH = window.innerWidth - 5;
SCREEN_HEIGHT = window.innerHeight - 5;
var vector = new THREE.Vector3((event.clientX / SCREEN_WIDTH) * 2 - 1, -(event.clientY / SCREEN_HEIGHT) * 2 + 1, 0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
for (var i = 0; i < intersects.length; i++) {
var obj = intersects[i].object;
controls.enabled = false;
tControls.attach();
}
}
else {
controls.enabled = true;
tControls.detach();
}
}
The scene is the whole browser window. Any other mesh cerated via THREE types can be picked, but object3d not...
Thanks for all kinds of help
Add the recursive flag like so:
var intersects = raycaster.intersectObjects( objects, true );
three.js r.66