How do I get the rotation of an element relative to the world coordination system?
I'm getting the information of an element by the following:
this.viewerComponent.viewer.impl.hitTest(event.layerX, event.layerY, false);
The result of this function is the following
{distance: 186476.22640731235, point: X.Vector3, face: X.Face3, faceIndex: 0, fragId: 372, …}
distance: 186476.22640731235
point: X.Vector3 {x: 70297.79662079967, y: 8922.73091035225, z: 9109.446866256267}
face: X.Face3 {a: 0, b: 1, c: 2, normal: X.Vector3, vertexNormals: Array(0), …}
faceIndex: 0
fragId: 372
dbId: 1959
object: X.Mesh
eulerOrder: (...)
useQuaternion: (...)
uuid: "A3D04442-BB20-4E0C-B371-3A987D212255"
name: ""
type: "Mesh"
parent: undefined
children: []
up: X.Vector3 {x: 0, y: 1, z: 0}
position: X.Vector3 {x: 0, y: 0, z: 0}
rotation: X.Euler {_x: 0, _y: 0, _z: 0, _order: "XYZ", onChangeCallback: ƒ}
quaternion: X.Quaternion {_x: 0, _y: 0, _z: 0, _w: 1, onChangeCallback: ƒ}
scale: X.Vector3 {x: 1, y: 1, z: 1}
rotationAutoUpdate: true
matrix: X.Matrix4
elements: Float32Array(16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
__proto__: Object
matrixWorld: X.Matrix4
elements: Float32Array(16) [10, 0, 0, 0, 0, 10, 0, 0, 0, 0, 10, 0, 79719.9609375, -6109.12646484375,
1962.4998779296875, 1]
__proto__: Object
matrixAutoUpdate: true
matrixWorldNeedsUpdate: false
visible: true
castShadow: false
receiveShadow: false
frustumCulled: true
renderOrder: 0
userData: {}
geometry: h {id: 2265, attributes: {…}, __webglInit: undefined, byteSize: 28, vb: Float32Array(6),
…}
material: X.LineBasicMaterial {uuid: "E36F7B3D-C885-475C-9AA6-A3D1024F7687", name: "", type:
"LineBasicMaterial", side: 0, opacity: 1, …}
isTemp: true
dbId: 529
modelId: 1
fragId: 2264
hide: false
isLine: true
isWideLine: false
isPoint: false
themingColor: undefined
id: 1
__proto__: X.Object3D
As seen, we have the information matrix (local coordination system which is connected to the element coordination system) and matrixWorld which should be the transformation matrix for element -> global coordination system. How do I now get the angles out of the matrixWorld to know what is the rotation by the elmenent in relation to the world coordination system.
Hope it is clear what i want, thank you in advance.
If I understand you correctly you may try:
//Getting fragment info by fragId if necessary
//const matrixWorld = new THREE.Matrix4();
//const fragProxy = NOP_VIEWER.impl.getFragmentProxy(NOP_VIEWER.model, fragId)
//fragProxy.getWorldMatrix(matrixWorld);
const position = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
const scale = new THREE.Vector3();
matrixWorld.decompose( position, quaternion, scale );
See more on extracting rotation here
Thank you. It's a similar approach I've taken. Your solution would probably work, too.
public onMouseMove(event) {
var snapper = new
Autodesk.Viewing.Extensions.Snapping.Snapper(this.viewerComponent.viewer, {});
const hitTestResult = this.viewerComponent.viewer.impl.snappingHitTest(event.layerX,
event.layerY);
snapper.snapping3D(hitTestResult);
// Arrow
const geometry = new THREE.CylinderGeometry(1, 800, 2000, 100);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const mesh = new THREE.Mesh(geometry, material);
mesh.rotateZ(this.arcSinFunction(normal.x, normal.y, normal.z, 0, 1, 0)[0]);
mesh.name = 'section-mesh';
mesh.position.set(snapper.getSnapResult().intersectPoint.x,
snapper.getSnapResult().intersectPoint.y, snapper.getSnapResult().intersectPoint.z);
if (!this.viewerComponent.viewer.overlays.hasScene('section-scene')) {
this.viewerComponent.viewer.overlays.addScene('section-scene');
}
this.viewerComponent.viewer.overlays.addMesh([mesh, mesh_test], 'section-scene');
}
public arcSinFunction(n1: number, n2: number, n3: number, u1: number, u2: number,
u3:
number): Array<number> {
var zähler: number = Math.abs(n1 * u1 + n2 * u2 + n3 * u3);
// console.log(zähler);
var nenner: number = (Math.sqrt(n1 * n1 + n2 * n2 + n3 * n3)) * (Math.sqrt(u1 * u1 +
u2 * u2 + u3 * u3));
// console.log(nenner);
var resultRadian: number = Math.asin(zähler / nenner);
// console.log(resultRadian);
var pi = Math.PI;
var resultDegree = resultRadian * (180 / pi);
// console.log(resultDegree);
var res: Array<number> = [resultRadian, resultDegree];
// console.log(res);
return res;
}
Best regards
Related
I am trying to edit the edges/triangulation of a planebuffer geometry in three js.
Example of problem
I want to change the corners in red to be triangulated like the corner in green.
Is this possible via editing a plane, or do I have to start building my own custom mesh buffer.
Here is a fiddle for an example of the kind of manipulation I am doing on the plane. (can click and drag to rotate it)
var camera, scene, renderer;
var geometry, material, mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 5;
scene = new THREE.Scene();
geometry = new THREE.PlaneBufferGeometry( 10, 10, 10, 10 );
const pointLight = new THREE.PointLight(0xFFFFFF, 1);
const ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.1);
pointLight.position.y += 2;
pointLight.position.x = -1;
scene.add(pointLight);
scene.add(ambientLight);
const grid = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 2, 2, 2, 2, 2, 1, 1, 0],
[0, 1, 1, 2, 2, 2, 2, 2, 1, 1, 0],
[0, 1, 1, 2, 2, 2, 2, 2, 1, 1, 0],
[0, 1, 1, 2, 2, 2, 2, 2, 1, 1, 0],
[0, 1, 1, 2, 2, 2, 2, 2, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
const planePos = geometry.attributes.position;
for (let x = 0; x < grid.length; x++) {
const row = grid[x];
for (let y = 0; y < row.length; y++) {
const h = row[y];
const i = (x * row.length) + y;
planePos.setZ(i, h * 0.5);
}
}
//
material = new THREE.MeshPhongMaterial({flatShading:true});
mesh = new THREE.Mesh( geometry, material );
mesh.rotateX(Math.PI * -0.5);
mesh.translateZ(-1);
scene.add( mesh );
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 );
}
function animate(t) {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Ok, as #prisoner849 commented, buffergeometryies index property was the answer.
I redid the triangulation of the mesh by looping over the "squares" of my mesh, getting their corners and then changing the indexes for each squares triangles to one of a pre calculated set, based on which corner had a unique height. It also seems that you want the indexes to go in a specific order to control which side is "up", it seems to be that counter clockwise or so.
const size = 10;
const vertMap = [0, 3, 1, 3, 2, 1];
const triSets = [
[0, 3, 2, 0, 2, 1],
[1, 3, 2, 1, 0, 3],
[0, 3, 2, 0, 2, 1],
[1, 3, 2, 1, 0, 3],
];
const getSquareVerts = (x, y) => {
const i = (y * size) + x + y;
return [i, i + 1, i + (size + 2), i + (size + 1)];
}
const getUniqueHeightPoint = (verts) => {
let unique = -1;
const set = verts.reduce((p, n) => {
const c = p[n.toString()] || 0;
p[n.toString()] = c + 1;
return p;
}, {});
const keys = Object.keys(set);
if (keys.length == 2 && (set[keys[0]] == 1 || set[keys[0]] == 3)) {
for (const key of keys) {
if (set[key] == 1) {
unique = verts.findIndex((v) => v == parseFloat(key));
break;
}
}
}
return unique;
}
const reworkTris = (plane) => {
let indexOffset = 0;
const planeIdx = plane.geometry.index;
for (let y = 0; y < this.size; y++) {
for (let x = 0; x < this.size; x++) {
const verts = this.getSquareVerts(x, y);
const heights = verts.map((v) => this.planePos.getZ(v));
const uniqueHeight = this.getUniqueHeightPoint(heights);
let tris = vertMap.map((i) => verts[i]);
if (uniqueHeight >= 0) {
tris = triSets[uniqueHeight].map((i) => verts[i]);
}
planeIdx.set(tris, indexOffset);
indexOffset += 6;
}
}
planeIdx.needsUpdate = true;
}
const plane = new THREE.Mesh(new THREE.PlaneBufferGeometry());
reqorkTris(plane);
I'm working with 3D objects, and suddenly have this issue. I am saving my scene in the server so that when I refresh my the webpage the objects inside the scene will not disappear, but the problem is the objects inside the scene aren't in the right position, they always go in the (0, 0, 0) position, which, I think, shouldn't happen because I already set its position before render, but I don't know if I'm doing it the right way.
const ObjectBuilder = (props) => {
const { geometry, object } = props;
const objRef = useRef();
const matrix = new THREE.Matrix4();
const loader = new THREE.BufferGeometryLoader();
const position = new THREE.Vector3();
const parsedGeom = loader.parse( geometry );
matrix.set(...object.matrix);
position.setFromMatrixPosition( matrix );
const handleClick = (e) => {
e.stopPropagation();
props.click({ data: objRef });
}
console.log('matrix', matrix); //elements: [0, 0, 1111, 0, 50, 0, 0, 0, 0, 50, 0, 0, 0, 0, 1]
console.log('position:', position); // x: 0, y:0, z:0
return(
<mesh
name="map_object"
ref={objRef}
onClick={handleClick}
receiveShadow
castShadow
scale={50}
geometry={parsedGeom}
position={position}
matrix={matrix}
>
<meshStandardMaterial
color="white"
metalness={0.3}
roughness={0.5}
/>
</mesh>
);
}
Thank you guys so much in advance!
console.log('matrix', matrix); //elements: [0, 0, 1111, 0, 50, 0, 0, 0, 0, 50, 0, 0, 0, 0, 1]
This looks wrong. Your comment shows only 15 array elements although the elements property should hold 16.
Assuming object is an instance of THREE.Object3D the following line is incorrect, too.
matrix.set(...object.matrix);
You can't use the spread operator with an instance of THREE.Matrix4.
I have a ThreeJS scene with 16 objects that are supposed to end up in a 4 by 4 square grid. However, when I run the code, I only see one of the objects. I wrote a "dump" function to show me all the current XYZ values of the mesh objects's position property, which you can see below. The values all look good to me and I believe I should see a nice 4 by 4 square grid of objects given the positioning those values present.
I am using this ThreeJS Javascript file for ThreeJS:
https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js
I am using this code to create the mesh. The mesh is a cube with initially the side facing the camera is a cat image:
function makeCatCube(catImageUrl, textureBackSide, locX, locY, locZ) {
let errPrefix = '(makeCatCube) ';
// TODO: Should we use BoxBufferGeometry here for greater speed?
let cubeGeometry = new THREE.BoxGeometry(2, 0.1, 2);
let loader = new THREE.TextureLoader();
let materialArray = [
new THREE.MeshBasicMaterial( { map: loader.load('/images/cards/white-square-400x400.png') } ),
new THREE.MeshBasicMaterial( { map: loader.load('/images/cards/white-square-400x400.png') } ),
// Card face.
new THREE.MeshBasicMaterial( { map: loader.load(catImageUrl) } ),
// Card back side.
new THREE.MeshBasicMaterial(
{
map: textureBackSide
}
),
new THREE.MeshBasicMaterial( { map: loader.load('/images/cards/white-square-400x400.png') } ),
new THREE.MeshBasicMaterial( { map: loader.load('/images/cards/white-square-400x400.png') } ),
];
cube = new THREE.Mesh( cubeGeometry, materialArray );
if (g_ShowGraphicsDebugInfo) {
console.log(errPrefix + `Setting cube position to - X: ${locX}, Y: ${locY}, Z: ${locZ}`);
}
cube.position.set(locX, locZ, locY);
// TODO: Magic number to set the cube's X rotation so it looks flat facing the viewer.
cube.rotation.x = THREE.Math.radToDeg(60);
return cube;
}
Here is the main promise that builds all the game assets and shows where I add the mesh objects to the scene. The global g_aryCatCards array that contains all the cat cards that were built is prepared in a much larger module elsewhere. It contains each of the cat cards and each card has a meshThreeJS property that contains the ThreeJS mesh object (i.e. - the cube) that was built using the makeCatCube() function shown above :
function initializeGameAssets_promise(gameAreaDomElementID, threeJSCanvasAreaDomElementID, catCardWidth, catCardHeight) {
let errPrefix = '(initializeGameAssets_promise) ';
return new Promise(function(resolve, reject) {
try {
buildAllCatCards_promise(gameAreaDomElementID, threeJSCanvasAreaDomElementID, 1, catCardWidth, catCardHeight)
.then(result => {
g_Scene = new THREE.Scene();
g_Scene.background = new THREE.Color('yellow');
initCamera();
initRenderer();
for (let cardLabelKey in g_aryCatCards) {
let catCard = g_aryCatCards[cardLabelKey];
g_Scene.add(catCard.meshThreeJS);
}
let threeJSCanvasAreaDOMElement = document.getElementById(threeJSCanvasAreaDomElementID);
if (!threeJSCanvasAreaDOMElement)
throw new Error(errPrefix + `Unable to find the DOM element for the cat cards underlay table using ID: ${threeJSCanvasAreaDomElementID}`);
threeJSCanvasAreaDOMElement.appendChild(g_Renderer.domElement);
let catCardsTableElementOffset = getElementOffsetById(ELEMENT_ID_CAT_CARDS_TABLE);
threeJSCanvasAreaDOMElement.left = catCardsTableElementOffset.left;
threeJSCanvasAreaDOMElement.top = catCardsTableElementOffset.top;
// Start the rendering process.
render();
resolve(true);
})
.catch(err => {
reject(err);
});
}
catch(err) {
reject(err);
}
});
}
Here are the functions I use to initialize the camera and the renderer:
function initCamera() {
g_Camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
g_Camera.position.set(0, 3.5, 5);
g_Camera.lookAt(g_Scene.position);
}
function initRenderer() {
g_Renderer = new THREE.WebGLRenderer(
{
antialias: true
});
}
This dump shows the XYZ values of the mesh object's position property:
------------- DUMPING MESH POSITIONS -------------
[label: A1] - X: 2, Y: 0, Z: 2
[label: A2] - X: 176, Y: 0, Z: 2
[label: A3] - X: 350, Y: 0, Z: 2
[label: A4] - X: 525, Y: 0, Z: 2
[label: E1] - X: 2, Y: 0, Z: 364
[label: E2] - X: 176, Y: 0, Z: 364
[label: E3] - X: 350, Y: 0, Z: 364
[label: E4] - X: 525, Y: 0, Z: 364
[label: L1] - X: 2, Y: 0, Z: 183
[label: L2] - X: 176, Y: 0, Z: 183
[label: L3] - X: 350, Y: 0, Z: 183
[label: L4] - X: 525, Y: 0, Z: 183
[label: X1] - X: 2, Y: 0, Z: 545
[label: X2] - X: 176, Y: 0, Z: 545
[label: X3] - X: 350, Y: 0, Z: 545
[label: X4] - X: 525, Y: 0, Z: 545
I really don't know what to do at this point to debug this problem. Can anyone give me some general tips on what to inspect, or what diagnostic code I could write to try and figure this out?
Your camera can only see 9 units deep
new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
but your objects are up to 545 units away
Try
const near = 1;
const far = 1000;
new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, near, far);
see
You also seem to have the camera looking in the wrong direction.
g_Camera.position.set(0, 3.5, 5);
g_Camera.lookAt(g_Scene.position);
AFAIK g_scene.position is 0, 0, 0 which means the camera is at z = 5 looking toward Z = 0 but your list of objects are almost all behind the camera.
Try
g_Camera.position.set(0, 3.5, -50);
g_Camera.lookAt(g_Scene.position);
I am new to Three.js. I was trying to load a STL model using the STLLoader from the examples. The model I am trying to load is the Eiffel Tower model. I downloaded the STL file which is in ASCII format and almost 33 MB in size. I have the following setup to display the model:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 5% auto; }
canvas { width: 80%; height: 80% }
#progress {
margin-bottom: 2%;
min-width: 50%;
}
</style>
<script src="js/three.js"></script>
<script src="js/STLLoader.js"></script>
</head>
<body>
<div id="progress"></div>
<script>
// window properties
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// camera properties
const FOV = 35;
const ASPECT_RATIO = windowWidth / windowHeight;
const NEAR = 0.1;
const FAR = 1000;
// scene settings
const SCENE_BKG = new THREE.Color("rgb(220,220,220)");
const scene = new THREE.Scene();
scene.background = SCENE_BKG;
const camera = new THREE.PerspectiveCamera( FOV, ASPECT_RATIO, NEAR, FAR );
const STLLoader = new THREE.STLLoader();
STLLoader.load('./sample_stl/Eiffel_tower_sample.STL', function(geometry) {
console.dir(geometry);
const materials = [];
const nGeometryGroups = geometry.groups.length;
let colorMap = []; // Some logic to index colors.
let material;
// create a random colorMap
let startColor = 0x010101;
let clr = startColor;
let count = 0;
while (count++ < nGeometryGroups) {
colorMap.push(clr);
clr = ( parseInt(clr, 16) + startColor ).toString();
}
console.log(colorMap);
for (let i = 0; i < nGeometryGroups; i++) {
material = new THREE.MeshPhongMaterial({
color: colorMap[i],
wireframe: false
});
}
materials.push(material);
const mesh = new THREE.Mesh(geometry, materials);
console.dir(mesh);
scene.add(mesh);
// should i call animate here?
}, function (xhr) {
// show progress here
progressBar.innerHTML = `<span style="color: green;">${(xhr.loaded/xhr.total) * 100}%</span> have been loaded`;
}, function(err) {
console.error('[!] Fatal Error: Could not load model');
console.error(err);
});
const renderer = new THREE.WebGLRenderer();
renderer.setSize( windowWidth, windowHeight );
const progressBar = document.querySelector('#progress');
document.body.appendChild( renderer.domElement );
const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
camera.position.set(0, 0, 10);
animate();
</script>
</body>
</html>
There are two things I am not sure about. First, what is the colorMap array? I looked at the MeshPhongMaterial class documentation and figured out it was a hex color value. I copied this code directly from the STLLoader example folder here. I found a quick hack to generate some hex colors and populate the colorMap array (an empty array was throwing errors). Secondly, where should I call the animate() function? I tried calling it inside the modelLoaded handler and also outside it with the only difference being that inside the handler, it throws Violation: handler took 500ms. I checked the networks tab on both Firefox and Chromium to see that the STL file was loaded properly. I also printed the Mesh object in the console which is as follows:
Mesh
castShadow: false
children: []
drawMode: 0
frustumCulled: true
geometry: BufferGeometry
attributes: {position: Float32BufferAttribute, normal: Float32BufferAttribute}
boundingBox: null
boundingSphere: Sphere {center: Vector3, radius: 71.76963889659893}
drawRange: {start: 0, count: Infinity}
groups: [{…}]
index: null
morphAttributes: {}
morphTargetsRelative: false
name: ""
type: "BufferGeometry"
userData: {}
uuid: "28A4B269-2828-477D-9C6D-5C9A30E95A7F"
_listeners: {dispose: Array(1)}
drawcalls: (...)
id: 7
offsets: (...)
__proto__: EventDispatcher
layers: Layers
mask: 1
__proto__: Object
material: Array(1)
0: MeshPhongMaterial {uuid: "1E33986A-A979-49C0-ADF5-823CACC6F3AA", name: "", type: "MeshPhongMaterial", fog: true, blending: 1, …}
length: 1
__proto__: Array(0)
matrix: Matrix4
elements: Array(16)
0: 1
1: 0
2: 0
3: 0
4: 0
5: 1
6: 0
7: 0
8: 0
9: 0
10: 1
11: 0
12: 0
13: 0
14: 0
15: 1
length: 16
__proto__: Array(0)
__proto__: Object
matrixAutoUpdate: true
matrixWorld: Matrix4
elements: Array(16)
0: 1
1: 0
2: 0
3: 0
4: 0
5: 1
6: 0
7: 0
8: 0
9: 0
10: 1
11: 0
12: 0
13: 0
14: 0
15: 1
length: 16
__proto__: Array(0)
__proto__: Object
matrixWorldNeedsUpdate: false
name: ""
parent: Scene
autoUpdate: true
background: Color {r: 0.8627450980392157, g: 0.8627450980392157, b: 0.8627450980392157}
castShadow: false
children: [Mesh]
fog: null
frustumCulled: true
layers: Layers {mask: 1}
matrix: Matrix4 {elements: Array(16)}
matrixAutoUpdate: true
matrixWorld: Matrix4 {elements: Array(16)}
matrixWorldNeedsUpdate: false
name: ""
overrideMaterial: null
parent: null
position: Vector3 {x: 0, y: 0, z: 0}
quaternion: Quaternion {_x: 0, _y: 0, _z: 0, _w: 1, _onChangeCallback: ƒ}
receiveShadow: false
renderOrder: 0
rotation: Euler {_x: 0, _y: 0, _z: 0, _order: "XYZ", _onChangeCallback: ƒ}
scale: Vector3 {x: 1, y: 1, z: 1}
type: "Scene"
up: Vector3 {x: 0, y: 1, z: 0}
userData: {}
uuid: "3F9C4993-4CB4-4541-9C64-34FCBB12B1E3"
visible: true
_listeners: {dispose: Array(2)}
eulerOrder: (...)
id: 4
modelViewMatrix: Matrix4 {elements: Array(16)}
normalMatrix: Matrix3 {elements: Array(9)}
useQuaternion: (...)
__proto__: Object3D
position: Vector3 {x: 0, y: 0, z: 0}
quaternion: Quaternion {_x: 0, _y: 0, _z: 0, _w: 1, _onChangeCallback: ƒ}
receiveShadow: false
renderOrder: 0
rotation: Euler {_x: 0, _y: 0, _z: 0, _order: "XYZ", _onChangeCallback: ƒ}
scale: Vector3 {x: 1, y: 1, z: 1}
type: "Mesh"
up: Vector3 {x: 0, y: 1, z: 0}
userData: {}
uuid: "72BA29AF-F939-45F8-869A-D074E6696D7F"
visible: true
eulerOrder: (...)
id: 10
modelViewMatrix: Matrix4 {elements: Array(16)}
normalMatrix: Matrix3 {elements: Array(9)}
useQuaternion: (...)
__proto__: Object3D
This is the printed Mesh object. I am not sure what's wrong and would be glad if someone can explain me how to use the colorMap property and also give me a definitive answer on the animate function calling scope.
EDIT
Added a fiddle here.
First, what is the colorMap array?
The code from your example assumes that the model's geometry has multiple groups. If this is true, it's possible to assign multiple materials to a single 3D object. However, it's not mandatory to do so. If you want just a single material color, do this:
const material = new THREE.MeshPhongMaterial( { color: 0x0000ff } );
const mesh = new THREE.Mesh( geometry, material );
Secondly, where should I call the animate() function?
three.js examples normally have an init() and an animate() function. Meaning you start animating right after initializing the scene (creating camera, renderer, lights etc.). You can do this also in your application, however the STL file will pop in as soon as its loading and parsing progress is finished. As an alternative, you can also start animating in the onLoad() callback. It really depends on your use case (so there is no right or wrong).
three.js R111
As part of prototyping some debugger objects and methods, I created a simple cube with a different color on each side. Rather than creating multiple meshes, I decided to use draw groups and THREE.Multimaterial. Unfortunately, it's only half-working, and I don't understand why.
What IS working is the first two sides (4 triangles). What's NOT working is any of the other sides. None of the other sides are visible. I've added debug code to draw the (index-based) vertex normals, the (index-based, manually calculated) face normals, and the wireframe (THREE.WireframeHelper).
If I increase the count for the second group to 9, nothing happens. If I change the groups to reference different start/count values within the first 8 vertices, those changes work as expected. Doing anything above 8th vertex has no effect.
I've also checked that the drawRange is set to draw from 0 to Infinity. I've also ruled out typos in my data, because otherwise the normals and wireframe wouldn't work. Is there anything else I'm missing? Thanks!
(I found the issue in r76. Code below referenced r79 at the time of writing.)
jsfiddle: http://jsfiddle.net/TheJim01/gumftkm4/
HTML:
<script src="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<div id="host"></div>
<script>
// INITIALIZE
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
document.getElementById('host').appendChild(renderer.domElement);
var stats= new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 250;
var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
trackballControl.rotateSpeed = 5.0; // need to speed it up a little
var scene = new THREE.Scene();
var light = new THREE.PointLight(0xffffff, 1, Infinity);
light.position.copy(camera.position);
scene.add(light);
function draw(){
light.position.copy(camera.position);
renderer.render(scene, camera);
stats.update();
}
trackballControl.addEventListener('change', draw);
function navStartHandler(e) {
renderer.domElement.addEventListener('mousemove', navMoveHandler);
renderer.domElement.addEventListener('mouseup', navEndHandler);
}
function navMoveHandler(e) {
trackballControl.update();
}
function navEndHandler(e) {
renderer.domElement.removeEventListener('mousemove', navMoveHandler);
renderer.domElement.removeEventListener('mouseup', navEndHandler);
}
renderer.domElement.addEventListener('mousedown', navStartHandler);
renderer.domElement.addEventListener('mousewheel', navMoveHandler);
</script>
CSS:
html *{
padding: 0;
margin: 0;
width: 100%;
overflow: hidden;
}
#host {
width: 100%;
height: 100%;
}
JavaScript:
// New Color Cube
(function () {
var pos = new Float32Array([
// front
-1, 1, 1,
-1, -1, 1,
1, 1, 1,
1, -1, 1,
// right
1, 1, 1,
1, -1, 1,
1, 1, -1,
1, -1, -1,
// back
1, 1, -1,
1, -1, -1,
-1, 1, -1,
-1, -1, -1,
// left
-1, 1, -1,
-1, -1, -1,
-1, 1, 1,
-1, -1, 1,
// top
-1, 1, -1,
-1, 1, 1,
1, 1, -1,
1, 1, 1,
// bottom
-1, -1, 1,
-1, -1, -1,
1, -1, 1,
1, -1, -1
]),
nor = new Float32Array([
// front
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
// right
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
// back
0, 0, -1,
0, 0, -1,
0, 0, -1,
0, 0, -1,
// left
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
// top
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
// bottom
0, -1, 0,
0, -1, 0,
0, -1, 0,
0, -1, 0
]),
idx = new Uint32Array([
// front
0, 1, 2,
3, 2, 1,
// right
4, 5, 6,
7, 6, 5,
// back
8, 9, 10,
11, 10, 9,
// left
12, 13, 14,
15, 14, 13,
// top
16, 17, 18,
19, 18, 17,
// bottom
20, 21, 22,
23, 22, 21
]);
var sideColors = new THREE.MultiMaterial([
new THREE.MeshLambertMaterial({ color: 'red' }), // front
new THREE.MeshLambertMaterial({ color: 'green' }), // right
new THREE.MeshLambertMaterial({ color: 'orange' }), // back
new THREE.MeshLambertMaterial({ color: 'blue' }), // left
new THREE.MeshLambertMaterial({ color: 'white' }), // top
new THREE.MeshLambertMaterial({ color: 'yellow' }) // bottom
]);
var cubeGeometry = new THREE.BufferGeometry();
cubeGeometry.addAttribute("position", new THREE.BufferAttribute(pos, 3));
cubeGeometry.addAttribute("normal", new THREE.BufferAttribute(nor, 3));
cubeGeometry.setIndex(new THREE.BufferAttribute(idx, 3));
cubeGeometry.clearGroups();
cubeGeometry.addGroup(0, 6, 0);
cubeGeometry.addGroup(6, 6, 1);
cubeGeometry.addGroup(12, 6, 2);
cubeGeometry.addGroup(18, 6, 3);
cubeGeometry.addGroup(24, 6, 4);
cubeGeometry.addGroup(30, 6, 5);
THREE.ColorCube = function (scaleX, scaleY, scaleZ) {
THREE.Mesh.call(this, cubeGeometry, sideColors);
var scaler = new THREE.Matrix4().makeScale(scaleX, scaleY, scaleZ);
this.applyMatrix(scaler);
};
THREE.ColorCube.prototype = Object.create(THREE.Mesh.prototype);
THREE.ColorCube.prototype.constructor = THREE.ColorCube;
THREE.ColorCube.prototype.clearVertexNormals = function () {
if (this.vNormals === undefined) {
this.vNormals = [];
}
for (var i = 0, len = this.vNormals.length; i < len; ++i) {
this.parent.remove(this.vNormals[i]);
}
this.vNormals.length = 0;
}
THREE.ColorCube.prototype.drawVertexNormals = function (scale, color) {
this.clearVertexNormals();
scale = (scale === undefined) ? 1 : scale;
color = (color === undefined) ? 0xff : color;
var origin = new THREE.Vector3(),
normalArrow = null,
vert = null,
norm = null,
index = null,
vertexArray = this.geometry.attributes.position.array,
normalArray = this.geometry.attributes.normal.array,
indexArray = this.geometry.index.array;
for (var i = 0, len = indexArray.length; i < len; ++i) {
index = indexArray[i];
vert = new THREE.Vector3(...vertexArray.slice((index * 3), (index * 3) + 3)).applyMatrix4(this.matrix);
norm = new THREE.Vector3(...normalArray.slice((index * 3), (index * 3) + 3));
normalArrow = new THREE.ArrowHelper(
norm,
origin,
1 * scale,
color,
0.2 * scale,
0.1 * scale
);
normalArrow.position.copy(vert);
this.vNormals.push(normalArrow);
this.parent.add(normalArrow);
}
};
THREE.ColorCube.prototype.clearFaceNormals = function () {
if (this.fNormals === undefined) {
this.fNormals = [];
}
for (var i = 0, len = this.fNormals.length; i < len; ++i) {
this.parent.remove(this.fNormals[i]);
}
this.fNormals.length = 0;
}
THREE.ColorCube.prototype.drawFaceNormals = function (scale, color) {
this.clearFaceNormals();
scale = (scale === undefined) ? 1 : scale;
color = (color === undefined) ? 0xffaa00 : color;
var origin = new THREE.Vector3(),
normalArrow = null,
vertices = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()],
normals = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()],
indices = [0, 0, 0],
centroid = new THREE.Vector3(),
faceNormal = new THREE.Vector3(),
vertexArray = this.geometry.attributes.position.array,
normalArray = this.geometry.attributes.normal.array,
indexArray = this.geometry.index.array;
for (var i = 0, len = indexArray.length; i < len; i += 3) {
indices = indexArray.slice(i, i + 3);
vertices[0].set(...vertexArray.slice((indices[0] * 3), (indices[0] * 3) + 3)).applyMatrix4(this.matrix);
vertices[1].set(...vertexArray.slice((indices[1] * 3), (indices[1] * 3) + 3)).applyMatrix4(this.matrix);
vertices[2].set(...vertexArray.slice((indices[2] * 3), (indices[2] * 3) + 3)).applyMatrix4(this.matrix);
normals[0].set(...normalArray.slice((indices[0] * 3), (indices[0] * 3) + 3));
normals[1].set(...normalArray.slice((indices[1] * 3), (indices[1] * 3) + 3));
normals[2].set(...normalArray.slice((indices[2] * 3), (indices[2] * 3) + 3));
centroid.set(
(vertices[0].x + vertices[1].x + vertices[2].x) / 3,
(vertices[0].y + vertices[1].y + vertices[2].y) / 3,
(vertices[0].z + vertices[1].z + vertices[2].z) / 3
);
faceNormal.set(
(normals[0].x + normals[1].x + normals[2].x) / 3,
(normals[0].y + normals[1].y + normals[2].y) / 3,
(normals[0].z + normals[1].z + normals[2].z) / 3
);
faceNormal.normalize();
normalArrow = new THREE.ArrowHelper(
faceNormal,
origin,
1 * scale,
color,
0.2 * scale,
0.1 * scale
);
normalArrow.position.copy(centroid);
this.fNormals.push(normalArrow);
this.parent.add(normalArrow);
}
};
THREE.ColorCube.prototype.clearAllNormals = function () {
THREE.ColorCube.prototype.clearVertexNormals();
THREE.ColorCube.prototype.clearFaceNormals();
}
THREE.ColorCube.prototype.drawWireframe = function (color) {
if (this.wireframe === undefined) {
color = (color === undefined) ? 0 : color;
this.wireframe = new THREE.WireframeHelper(this, color);
this.parent.add(this.wireframe);
}
}
THREE.ColorCube.prototype.clearWireframe = function () {
if (this.wireframe) {
this.parent.remove(this.wireframe);
delete this.wireframe;
}
}
})();
var cc = new THREE.ColorCube(25, 25, 25);
scene.add(cc);
cc.drawVertexNormals(25);
cc.drawFaceNormals(25);
cc.drawWireframe(0xffffff);
draw();
You are setting indices wrong. You have 1 index per vertex. However your code shows 3 index per vertex which does not make sense.
if you change
cubeGeometry.setIndex(new THREE.BufferAttribute(idx, 3));
to
cubeGeometry.setIndex(new THREE.BufferAttribute(idx, 1));
your problem is solved.
Here is the working jsfiddle