js/Mobilizing/renderer/3D/three/shape/Mesh.js
import * as THREE from 'three';
// import Line3 from '../types/Line3';
import Vector2 from '../types/Vector2';
import Vector3 from '../types/Vector3';
import Matrix4 from '../types/Matrix4';
import Material from '../scene/Material';
import Transform from '../scene/Transform';
import * as _Math from '../../../../core/util/Math';
import EventEmitter from "../../../../core/util/EventEmitter";
/**
A Mesh is the aggregation of a geomtery, made of vertices (Vector3 Array), texture coordinates (or uv, Vector2 Array), vertexColor (Color Array) and various methods to create and modify 3D objects that have a material (see {{#crossLink "Material"}}{{/crossLink}}) and a transform (see {{#crossLink "Transform"}}{{/crossLink}}).
*/
export default class Mesh {
/**
* Construct an empty and unfinished Mesh for which a geometry must be constructed. Once filled with vertices, constructMesh method must be called, or a method generating a complete Mesh must be used (i.e generateFillMesh)
*
* @param {Object} params Parameters object, given by the constructor.
* @param {String} params.name The name of the primitive to create, can be one of the listed names in this class description.
* @param {String} [params.material="phong"] The name of the material to attach to the created primitive, can be one of : lambert, basic, phong.
* @param {Object} [params.geometry=undefined] An already existing geometry
* @param {Number} [params.Number=2000] maxVertices for dynamically defined geometry, we must use a bufferArray already filled to add vertices
*/
constructor({
name = undefined,
material = "phong",
geometry = undefined,
type = undefined,
maxVertices = 500
} = {}) {
this.name = name;
this.material = material;
this.geometry = geometry;
this.maxVertices = maxVertices;
if (!this.geometry) {
//used for custom geom drawing
this.vertexCount = 0;
//used for custom normals counting
this.normalsCount = 0;
//used for custom UV counting
this.uvCount = 0;
//faces indices, defines how faces are drawn
this.indices = [];
//produce bufferAttributes for each array
this.makeGeometryBuffers();
this.attachMaterial(this.material);
//generate the mesh from the empty geometry and material
this.genereateMesh();
}
else {
//we have a geometry, so attach the material and generate the mesh
this.attachMaterial(this.material);
this.genereateMesh();
}
this.type = type;
this.events = new EventEmitter({ "scope": window });
}
setEventEmitterTrigger(scope, eventType) {
if (scope.events) {
scope.events.trigger(eventType, this);
console.log(scope, eventType);
}
}
/**
* @returns the Three.js native objects used in this class
*/
getNativeObject() {
return this._mesh;
}
/**
* @returns the Three.js native geometry of this mesh
*/
getNativeGeometry() {
return this.geometry;
}
/**
*
* @param {String} name
*/
setRootName(name) {
this.rootName = name;
}
/**
*
* @param {*} val
*/
setMaxVertices(val) {
this.maxVertices = val;
this.makeGeometryBuffers();
}
/**
* Generate Float32Array and BufferAttribute for this geometry
*/
makeGeometryBuffers() {
//fill the 3 Arrays with enough memory to draw geometry
const vertices = new Float32Array(this.maxVertices * 3);
const normals = new Float32Array(this.maxVertices * 3);
const uv = new Float32Array(this.maxVertices * 2);
const colors = new Float32Array(this.maxVertices * 3);
//empty geometry
if (!this.geometry) {
this.geometry = new THREE.BufferGeometry();
}
//this is what we need to do to build a geometry
//make a vertices buffer for geometries
this.geometryVerticesAttributes = new THREE.Float32BufferAttribute(vertices, 3);
this.geometry.setAttribute('position', this.geometryVerticesAttributes);
//make a normal buffer
this.geometryNormalAttributes = new THREE.Float32BufferAttribute(normals, 3);
this.geometry.setAttribute('normal', this.geometryNormalAttributes);
//make a UV buffer for texture mapping
this.geometryUVAttributes = new THREE.Float32BufferAttribute(uv, 2);
this.geometry.setAttribute('uv', this.geometryUVAttributes);
//make a vertexColor buffer
this.geometryColorAttributes = new THREE.Float32BufferAttribute(colors, 3);
this.geometry.setAttribute('color', this.geometryColorAttributes);
}
/**
* genereate a mesh from already made geometry and material
*/
genereateMesh() {
this._mesh = new THREE.Mesh(this.geometry, this.material._material);
this.geometryVerticesAttributes = this.geometry.attributes.position;
this.geometryNormalAttributes = this.geometry.attributes.normal;
this.geometryUVAttributes = this.geometry.attributes.uv;
this.transform = new Transform(this);
}
/**
* Generate the mesh for the given vertices and generates flat uvs for it. Usefull for 2D shapes
*/
generateFillMesh(vertices) {
//main shape to hold paths
const shape = new THREE.Shape();
//draw shape
shape.moveTo(vertices[0].x, vertices[0].y);
for (let i = 1; i < vertices.length; i++) {
shape.lineTo(vertices[i].x, vertices[i].y);
}
shape.closePath();
//make a geometry from the shape
const geometry = new THREE.ShapeGeometry(shape);
//update buffer attributes for further manipulations
this.geometryVerticesAttributes = geometry.attributes.position;
this.geometryNormalAttributes = geometry.attributes.normal;
this.geometryUVAttributes = geometry.attributes.uv;
//construct the Mob Mesh
this.geometry = geometry;
this.constructMesh();
//generate flat uvs
this.generateFlatUVs();
}
/**
* Generate the flat uvs for this mesh.
*/
generateFlatUVs() {
const bounds = this.getBoundingBox();
for (let i = 0; i < this.geometryUVAttributes.count; i++) {
const vertex = new Vector3(this.geometryVerticesAttributes.getX(i),
this.geometryVerticesAttributes.getY(i),
this.geometryVerticesAttributes.getZ(i)
);
const uv = new Vector2(
_Math.map(vertex.x, bounds.min.x, bounds.max.x, 0, 1),
_Math.map(vertex.y, bounds.min.y, bounds.max.y, 0, 1)
);
this.geometryUVAttributes.setXY(i, uv.x, uv.y);
}
this.geometryUVAttributes.needsUpdate = true;
}
/**
* Static Utils to generate the stroke for the given vertices with the given stroke width
* @static
* @param {Mesh} mesh to use as a base to get vertices
* @param {Number} inflateValue stroke width scale factor
* @return (Shape) a Three.js Shape to make a geometry from
*/
static generateStrokeShape(mesh, inflateValue) {
const verticesArrayBuffer = mesh.geometry.attributes.position;
//convert buffer back to a Vector3 array
const vertices = [];
for (let i = 0; i < verticesArrayBuffer.array.length; i += verticesArrayBuffer.itemSize) {
const vec = new Vector3(
verticesArrayBuffer.array[i],
verticesArrayBuffer.array[i + 1],
verticesArrayBuffer.array[i + 2]
);
vertices.push(vec);
}
//console.log(mesh, inflateValue);
//console.log(Mesh.geometryIsCW(mesh));
const outterVertices = [];
const rot = Mesh.geometryIsCW(mesh) ? "ccw" : "cw";
for (let i = 0; i < vertices.length; ++i) {
// get this point (pt1), the point before it
// (pt0) and the point that follows it (pt2)
const pt0 = vertices[(i > 0) ? i - 1 : vertices.length - 1];
const pt1 = vertices[i];
const pt2 = vertices[(i < vertices.length - 1) ? i + 1 : 0];
//console.log(pt0, pt1, pt2);
// find the line vectors of the lines going
// into the current point
//const v01 = { x: pt1.x - pt0.x, y: pt1.y - pt0.y };
const v01 = new Vector2(pt1.x - pt0.x, pt1.y - pt0.y);
//const v12 = { x: pt2.x - pt1.x, y: pt2.y - pt1.y };
const v12 = new Vector2(pt2.x - pt1.x, pt2.y - pt1.y);
if (rot === 'ccw') {
v01.rotate90CCW();
v12.rotate90CCW();
}
else {
v01.rotate90CW();
v12.rotate90CW();
}
// find the normals of the two lines, multiplied
// to the distance that polygon should inflate
//const d01 = vecMul(vecUnit(rot(v01)), distance);
//const d12 = vecMul(vecUnit(rot(v12)), distance);
const d01 = v01.normalize().multiplyScalar(inflateValue).clone();
const d12 = v12.normalize().multiplyScalar(inflateValue).clone();
//console.log("d01", d01, "d12", d12);
// use the normals to find two points on the
// lines parallel to the polygon lines
const ptx0 = new Vector2(pt0.x + d01.x, pt0.y + d01.y);
const ptx10 = new Vector2(pt1.x + d01.x, pt1.y + d01.y);
const ptx12 = new Vector2(pt1.x + d12.x, pt1.y + d12.y);
const ptx2 = new Vector2(pt2.x + d12.x, pt2.y + d12.y);
//console.log("ptx0", ptx0, "ptx10", ptx10, "ptx12", ptx12, "ptx2", ptx2);
// find the intersection of the two lines, and
// add it to the expanded polygon
/* const l1 = new Line3(ptx0, ptx10);
const l2 = new Line3(ptx2, ptx12);
const intersect = l1.intersectionPoint(l2); */
const intersect = _Math.intersectionPoint([ptx0, ptx10], [ptx12, ptx2]);
outterVertices.push(intersect);
}
//console.log("outterVertices", outterVertices);
//Shape and subPath
const outterPath = new THREE.Shape();
const innerPath = new THREE.Path();
outterPath.moveTo(outterVertices[0].x, outterVertices[0].y);
for (let i = 1; i < outterVertices.length; i++) {
outterPath.lineTo(outterVertices[i].x, outterVertices[i].y);
}
innerPath.moveTo(vertices[0].x, vertices[0].y);
for (let i = 1; i < outterVertices.length; i++) {
innerPath.lineTo(vertices[i].x, vertices[i].y);
}
outterPath.closePath();
innerPath.closePath();
outterPath.holes.push(innerPath);
//console.log(outterPath);
return outterPath;
}
/**
* Static Utils to generate the stroke Mesh from a given shape
* @static
* @param {Mesh} mesh to use as a base to get vertices
* @param {Number} inflateValue stroke width
* @return {Mesh} the resulting Mesh
*/
static generateStrokeMesh(mesh, inflateValue) {
const shape = Mesh.generateStrokeShape(mesh, inflateValue);
const geometry = new THREE.ShapeGeometry(shape);
/** @TODO refactor */
const strokeMesh = new Mesh();
strokeMesh.geometry = geometry;
strokeMesh.constructMesh();
//strokeMesh.material.setWireframe(true);
return strokeMesh;
}
/**
* Utils to regenerate the stroke Mesh when already existing
* @param {Number} inflateValue stroke width
*/
/* updateStroke(mesh, inflateValue) {
const shape = Mesh.generateStrokeShape(mesh, inflateValue);
const geometry = new THREE.ShapeGeometry(shape);
this.setVertices(geometry.vertices);
this.updateMesh();
} */
/**
* Static Utils to generate the stroke Mesh from a given shape
* @static
* @param {Shape} shape
* @return {Mesh} the resulting Mesh
*/
/* static generateStrokeMeshFromShape(shape) {
const geometry = new THREE.ShapeGeometry(shape);
const strokeMesh = new Mesh({ geometry });
strokeMesh.constructMesh();
strokeMesh.material.setColor(this.strokeColor);
return strokeMesh;
} */
/**
* Static Utils to check the direction of a geometry (ccw o cw)
* @static
* @param {Geometry} geometry Three.js geometry
* @return {Boolean} is CW or not
*/
static geometryIsCW(mesh) {
const verticesArrayBuffer = mesh.geometry.toNonIndexed().getAttribute("position");
//convert buffer back to a Vector3 array
const vertices = [];
for (let i = 0; i < verticesArrayBuffer.array.length; i += verticesArrayBuffer.itemSize) {
const vec = new Vector3(
verticesArrayBuffer.array[i],
verticesArrayBuffer.array[i + 1],
verticesArrayBuffer.array[i + 2]
);
vertices.push(vec);
}
//console.log("vertices", vertices);
for (let i = 0; i < vertices.length; i++) {
const v1 = new Vector2(vertices[i + 1].x - vertices[i].x, vertices[i + 1].y - vertices[i].y);
const v2 = new Vector2(vertices[i + 2].x - vertices[i + 1].x, vertices[i + 2].y - vertices[i + 1].y);
//v1.rotate90CW();
const dot = v1.dot(v2);
return (dot >= 0);
}
return null;
}
/*
* =====
* Boundings management
* =====
*/
/**
* Compute and returns the bounding box of the current geometry or Node, which is an object made of 2 Vector3.
* @param {Boolean} force force computation
* @return {Object} boundingBox object {min: new Vector3, max: new Vector3}
*/
getBoundingBox(force) {
if (this.geometry) {
if (!this.geometry.boundingBox) {
this.geometry.computeBoundingBox();
}
if (force) {
this.geometry.computeBoundingBox();
}
return this.geometry.boundingBox;
}
//no geometry, let three do the math
return new THREE.Box3().setFromObject(this._mesh);
}
/**
* Compute and returns the bounding sphere of the current geometry or Node, which is an object with a radius :
* @param {Boolean} force force computation
* @return {Object} boundingSphere object {radius: number}
*/
getBoundingSphere(force) {
if (this.geometry) {
if (!this.geometry.boundingSphere) {
this.geometry.computeBoundingSphere();
}
if (force) {
this.geometry.computeBoundingSphere();
}
return this.geometry.boundingSphere;
}
//no geometry, let three do the math
const bbox = new THREE.Box3().setFromObject(this._mesh);
return bbox.getBoundingSphere();
}
/**
* Call this method to construct a custom mesh
*/
constructMesh() {
//this.material = new Material({ type: "basic" }); //basic
this.attachMaterial(this.material);
this._mesh = new THREE.Mesh(this.geometry, this.material._material);
this.transform = new Transform(this);
this.geometry.computeBoundingBox();
}
//================
// VERTEX MANAGEMENT
//================
setVertex(index, vertex) {
if (index > this.geometry.attributes.position.count) {
this.indices = [];
for (let i = 0; i < this.geometry.attributes.position.count; i++) {
this.indices.push(i);
}
this.geometry.setDrawRange(0, this.indices.length);
}
this.geometryVerticesAttributes.setXYZ(index, vertex.x, vertex.y, vertex.z);
this.geometryVerticesAttributes.needsUpdate = true;
}
/**
* Set the vertices of this mesh geometry
* @param {Object} vertices And Array of Vector3 (points)
*/
setVertices(vertices) {
this.geometry.setFromPoints(vertices);
this.vertexCount = this.indices.length;
this.indices = [];
vertices.forEach((vertex, index) => {
this.indices.push(index);
});
this.geometry.setDrawRange(0, this.indices.length);
//update inner ref to buffer attributes
this.geometryVerticesAttributes = this.geometry.attributes.position;
this.geometryVerticesAttributes.needsUpdate = true;
}
/**
* Get the vertices of this mesh geometry
* @return {Array} vertices an array of Vector3 representing vertices
*/
getVertices() {
const vertices = [];
let count = 0;
if (this.vertexCount !== 0 && this.vertexCount < this.geometry.attributes.position.count) {
count = this.vertexCount;
}
else {
count = this.geometry.attributes.position.count;
}
for (let i = 0; i < count; i++) {
const vertex = new Vector3(
this.geometry.attributes.position.getX(i),
this.geometry.attributes.position.getY(i),
this.geometry.attributes.position.getZ(i)
);
vertices.push(vertex);
}
return vertices;
}
/**
* Adds a vertex to the current geometry
* @param {Vector3} v vertex to add
*/
pushVertex(vector, addIndex) {
const { x, y, z } = vector;
this.geometryVerticesAttributes.setXYZ(this.vertexCount, x, y, z);
this.vertexCount++;
if (addIndex) {
this.indices.push(this.indices.length);
this.geometry.setDrawRange(0, this.indices.length);
}
this.geometryVerticesAttributes.needsUpdate = true;
}
/**
* Adds a triangle to the current geometry
* @param {Vector3} v1 vertex coordinates of the triangle
* @param {Vector3} v2 vertex coordinates of the triangle
* @param {Vector3} v3 vertex coordinates of the triangle
*/
pushTriangle(v1, v2, v3) {
const maxIndex = this.indices.length;
this.pushVertex(v1);
this.pushVertex(v2);
this.pushVertex(v3);
this.indices.push(maxIndex + 0, maxIndex + 1, maxIndex + 2);
this.geometry.setDrawRange(0, this.indices.length);
this.geometryVerticesAttributes.needsUpdate = true;
//apply to the geometry
this.geometry.setIndex(this.indices);
}
/**
* Adds a Quad to the current geometry
* @param {Vector3} v1 vertex coordinates of the Quad
* @param {Vector3} v2 vertex coordinates of the Quad
* @param {Vector3} v3 vertex coordinates of the Quad
* @param {Vector3} v4 vertex coordinates of the Quad
*/
pushQuad(v0, v1, v2, v3) {
//add vertices to current geom
this.pushTriangle(v0, v1, v2);
this.pushTriangle(v2, v3, v0);
}
//===========
//UV
//===========
/**
* Adds a UV to the current geometry
*
* @param {Vector2} uv1 uv coordinates to add
* @param {Vector2} uv2 uv coordinates to add
* @param {Vector2} uv3 uv coordinates to add
*/
pushUV(vector) {
const { x, y } = vector;
this.geometryUVAttributes.setXY(this.uvCount, x, y);
this.uvCount++;
this.geometryUVAttributes.needsUpdate = true;
}
//=================
//Normals
//=================
/**
Adds a Normal to the current geometry
@param {Vector2} n1 n coordinates to add
@param {Vector2} n2 n coordinates to add
@param {Vector2} n3 n coordinates to add
*/
pushNormal(vector) {
const { x, y, z } = vector;
this.geometryVerticesAttributes.setXYZ(this.normalsCount, x, y, z);
this.normalsCount++;
this.geometryNormalAttributes.needsUpdate = true;
}
//=================
/**
* compute Face and Vertex Normals for the current geometry
*/
computeNormals() {
//compute normals automatically
this.geometry.computeVertexNormals();
this.geometryNormalAttributes = this.geometry.attributes.normal;
}
/**
* convinient method to reverse this Mesh geometry's normals
*/
reverseNormals() {
//generates normals if necessary
this.computeNormals();
const normalsCount = this.geometryNormalAttributes.count;
for (let i = 0; i < normalsCount; i++) {
const normal = new Vector3(
this.geometryNormalAttributes.getX(i),
this.geometryNormalAttributes.getY(i),
this.geometryNormalAttributes.getZ(i)
);
normal.negate();
this.geometryNormalAttributes.setXYZ(i, normal.x, normal.y, normal.z);
this.geometryNormalAttributes.needsUpdate = true;
}
}
/**
* Erase this mesh's geometry and material from memory
* @param {Boolean} recursive if true, will erase all the children recursively
*/
erase(recursive) {
if (recursive) {
//get all children for this mesh
const recursiveChildren = this.transform.getChildren(true);
recursiveChildren.forEach((child) => {
let mobObject = child;
if (mobObject.geometry) {
mobObject.geometry.dispose();
}
if (mobObject.material) {
mobObject.material.erase();
}
mobObject = undefined;
});
}
if (this.geometry) {
this.geometry.dispose();
}
if (this.material) {
this.material.erase();
}
}
/**
* Set the UV of this mesh geometry
* @param {integer} index
* @param {Array} uvs
*/
setUVs(index, uvs) {
this.geometry.faceVertexUvs[index] = uvs;
this.geometry.uvsNeedUpdate = true;
this.geometry.elementsNeedUpdate = true;
}
/**
* Get the UV of this mesh geometry
* @param {integer} index of the UV to get
* @return {Array} uvs
*/
getUVs(index) {
return this.geometry.faceVertexUvs[index];
}
//Vertex Color
/**
* Set the vertexColors of this mesh geometry's vertices
* @param {Array<Color>} colors
*/
setVertexColors(colors) {
this.material._material.vertexColors = THREE.VertexColors;
this.geometry.colors = colors;
this.geometry.colorsNeedUpdate = true;
}
/**
* Get the vertexColors of this mesh geometry's vertices
* @return {Array<Color>} colors
*/
getVertexColors() {
return this.geometry.colors;
}
/**
* Defines the position of the geometry's center (NOT the transform!).
*
* @param {number} x x coordinate
* @param {number} y y coordinate
* @param {number} z z coordinate
*/
setCenter(x, y, z) {
this.geometry.applyMatrix4(new Matrix4().makeTranslation(x, y, z));
}
/**
* Returns the center of the geometry (vertices) based on boundingbox
* @return {Vector3} vector3 of the current geometry center
*/
getCenter() {
//force bounding box computation
if (this.geometry.boundingBox === null) {
this.geometry.computeBoundingBox();
}
const resultVec = new Vector3();
this.geometry.boundingBox.getCenter(resultVec);
return resultVec;
}
/**
* Returns the size of the geometry (vertices) based on boundingbox
* @return {Vector3} vector3 sizes of the current geometry
*/
getSize() {
//force bounding box computation
if (this.geometry.boundingBox === null) {
this.geometry.computeBoundingBox();
}
const resultVec = new Vector3();
this.geometry.boundingBox.getSize(resultVec);
return resultVec;
}
/**
* Shortcut for getSize().x
* @returns {Number} this Mesh width
*/
getWidth() {
return this.getSize().x;
}
/**
* Shortcut for getSize().y
* @returns {Number} this Mesh height
*/
getHeight() {
return this.getSize().y;
}
/**
* Shortcut for getSize().z
* @returns {Number} this Mesh length (z-axis)
*/
getLength() {
return this.getSize().z;
}
/**
* Set the scale of the geometry (vertices) - this is not like scaling the transform. Shouldn't be done in loop. Polymorphic : can take various agruments of various types. Possible arguments number is 1, 2 or 3.
* @param {float|Vector3|Vector2} number|Vector3|Vector2 Value for the new scale of the geometry. If a Vector3 is given, its x, y, z will be used for the scale x, y, z. If a Vector2 is given, its x, y will be used for the scale x and y, but z will be 1. If a number is given, it will be the scale x.
* @param {float} Number Value for the new y scale of the geometry.
* @param {float} Number Value for the new z scale of the geometry.
*/
setScale(arg1, arg2, arg3) {
if (arguments.length === 3) {
this.geometry.applyMatrix4(new Matrix4().makeScale(arg1, arg2, arg3));
}
else if (arguments.length === 2) {
if (typeof arg1 === "number" && typeof arg2 === "number") {
this.geometry.applyMatrix4(new Matrix4().makeScale(arg1, arg2, 1));
}
}
else if (arguments.length === 1) {
if (arg1 instanceof Vector3) {
this.geometry.applyMatrix4(new Matrix4().makeScale(arg1.x, arg1.y, arg1.z));
}
else if (arg1 instanceof Vector2) {
this.geometry.applyMatrix4(new Matrix4().makeScale(arg1.x, arg1.y, 1));
}
else if (typeof arg1 === "number") {
this.geometry.applyMatrix4(new Matrix4().makeScale(arg1, arg1, arg1));
}
}
}
/**
* Set the rotation around x axis of the vertices (not the transform!)
* @param {float} value Rotation value in degree
*/
setRotationX(arg1) {
this.geometry.applyMatrix4(new Matrix4().makeRotationX(_Math.degToRad(arg1)));
}
/**
* Set the rotation around y axis of the vertices (not the transform!)
* @param {float} value Rotation value in degree
*/
setRotationY(arg1) {
this.geometry.applyMatrix4(new Matrix4().makeRotationY(_Math.degToRad(arg1)));
}
/**
* Set the rotation around z axis of the vertices (not the transform!)
* @param {float} value Rotation value in degree
*/
setRotationZ(arg1) {
this.geometry.applyMatrix4(new Matrix4().makeRotationZ(_Math.degToRad(arg1)));
}
/**
* Set the translation along all axis of the vertices (not the transform!)
* @param {float} value x value
* @param {float} value y value
* @param {float} value z value
*/
setTranslation(arg1, arg2, arg3) {
//this.geometry.applyMatrix4( new Matrix4().makeTranslation( _Math.degToRad(arg1)));
if (arguments.length === 3) {
this.geometry.applyMatrix4(new Matrix4().makeTranslation(arg1, arg2, arg3));
}
else if (arguments.length === 2) {
if (typeof arg1 === "number" && typeof arg2 === "number") {
this.geometry.applyMatrix4(new Matrix4().makeTranslation(arg1, arg2, 0));
}
}
else if (arguments.length === 1) {
if (arg1 instanceof Vector3) {
this.geometry.applyMatrix4(new Matrix4().makeTranslation(arg1.x, arg1.y, arg1.z));
}
else if (arg1 instanceof Vector2) {
this.geometry.applyMatrix4(new Matrix4().makeTranslation(arg1.x, arg1.y, 0));
}
}
}
/**
* Set the visibility of this mesh
* @param {Boolean} value visible or not
*/
setVisible(val) {
this._mesh.visible = val;
}
/**
* Get the visibility of this mesh
* @return {Boolean} value visible or not
*/
getVisible() {
return this._mesh.visible;
}
/**
* Updates this mesh material
* @private
*/
updateMaterial() {
if (this._mesh.material) {
this._mesh.material = this.material._material;
}
}
/**
* Sets this Mesh material. Be warned that this method requiers to build a new mesh, so it can be slow on update. Better use it in setup.
* @param {Material|String} material the new material to set (if String used, one of "phong", "lambert", "basic")
*/
setMaterial(material) {
this.material = null;
if (material === undefined) {
this.material = new Material({ type: "default" });
}
else if (material instanceof Material) {
this.material = material;
}
else if (typeof material === "string") {
this.material = new Material({ type: material });
}
const tempMesh = new THREE.Mesh(this.geometry, this.material._material);
const tempTransform = this.transform;
this._mesh = tempMesh;
this.transform = new Transform(this);
this.transform.setLocalScale(tempTransform.getLocalScale().x,
tempTransform.getLocalScale().y,
tempTransform.getLocalScale().z);
this.transform.setLocalPosition(tempTransform.getLocalPosition().x,
tempTransform.getLocalPosition().y,
tempTransform.getLocalPosition().z);
this.transform.setLocalRotation(tempTransform.getLocalRotation().x,
tempTransform.getLocalRotation().y,
tempTransform.getLocalRotation().z);
}
/**
* Attach the material to this mesh. The material must have been created before calling this method
* @param {Material} material
*/
attachMaterial(material) {
//console.log("material instanceof THREE.Material", material instanceof THREE.Material);
if (material === undefined) {
this.material = new Material({ type: "default" });
}
else if (material instanceof Material) {
this.material = material;
}
else if (typeof material === "string") {
this.material = new Material({ type: material });
}
else if (material instanceof THREE.Material) {
this.material = new Material({ type: material });
}
if (material !== undefined && this._mesh) {
this.updateMaterial();
}
}
/**
* setCastShadow
* @param {Boolean} enabled
*/
setCastShadow(enabled) {
this._mesh.castShadow = enabled;
}
/**
* setReceiveShadow
* @param {Boolean} enabled
*/
setReceiveShadow(enabled) {
this._mesh.receiveShadow = enabled;
}
/**
* Compute the intersection points between a plane and a mesh !Works ONLY if this mesh is "plane" primitive made
* @param {Mesh} mesh the mesh to use for the intersections computation
* @return {Array} an array of object as {vertex: Vector3, uv: Vector2};
*/
/* getIntersectionsPoints(mesh) {
if (this.primitive === "plane") {
const plane = this._mesh;
const obj = mesh.mesh;
const intersectionPoints = [];
const a = new THREE.Vector3(),
b = new THREE.Vector3(),
c = new THREE.Vector3();
const planePointA = new THREE.Vector3(),
planePointB = new THREE.Vector3(),
planePointC = new THREE.Vector3();
const mathPlane = new THREE.Plane();
plane.localToWorld(planePointA.copy(plane.geometry.vertices[plane.geometry.faces[0].a]));
plane.localToWorld(planePointB.copy(plane.geometry.vertices[plane.geometry.faces[0].b]));
plane.localToWorld(planePointC.copy(plane.geometry.vertices[plane.geometry.faces[0].c]));
mathPlane.setFromCoplanarPoints(planePointA, planePointB, planePointC)
const uvs = mesh.getUVs(0);
obj.geometry.faces.forEach((face, index) => {
//faces vertices
const fvA = obj.geometry.vertices[face.a];
const fvB = obj.geometry.vertices[face.b];
const fvC = obj.geometry.vertices[face.c];
//intersection management
obj.localToWorld(a.copy(fvA));
obj.localToWorld(b.copy(fvB));
obj.localToWorld(c.copy(fvC));
const lineAB = new THREE.Line3(a, b);
const lineBC = new THREE.Line3(b, c);
const lineCA = new THREE.Line3(c, a);
const lines = [lineAB, lineBC, lineCA];
lines.forEach((line) => {
//new object to fill
const intersectionPoint = {};
//search an intersection
this.setPointOfIntersection(line, mathPlane, intersectionPoint);
//if we have one, search for uvs
if (intersectionPoint.vertex) {
//uv management
const uvA = new Vector2();
const uvB = new Vector2();
const uvC = new Vector2();
const uvs_f = uvs[index];
uvA.copy(uvs_f[0]);
uvB.copy(uvs_f[1]);
uvC.copy(uvs_f[2]);
const wIntersectionPoint = new Vector3();
obj.worldToLocal(wIntersectionPoint.copy(intersectionPoint.vertex));
const uv = this.getUVFromIntersectionPoint(wIntersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC);
intersectionPoint.uv = uv;
intersectionPoints.push(intersectionPoint);
}
});
});
return intersectionPoints;
}
return undefined;
} */
/**
* setPointOfIntersection
* @private
* @param {Object} line three.js line
* @param {Object} plane three.js plane
* @param {Object} intersectionPoint object to be used to store the resulting Vector3
* @return {Object} description
*/
/* setPointOfIntersection(line, plane, intersectionPoint) {
const pointOfIntersection = plane.intersectLine(line);
if (pointOfIntersection) {
const v = new Vector3(pointOfIntersection.x, pointOfIntersection.y, pointOfIntersection.z);
intersectionPoint.vertex = v;
}
} */
/**
* getUVFromIntersectionPoint
* @private
* @param {Vector3} point the intersection point as a Vector3
* @param {Vector3} p1 vertices of face1
* @param {Vector3} p2 vertices of face2
* @param {Vector3} p3 vertices of face3
* @param {Vector2} uv1 uvs of face1
* @param {Vector2} uv2 uvs of face2
* @param {Vector2} uv3 uvs of face3
* @return {Vector2} Uvs
*/
/* getUVFromIntersectionPoint(point, p1, p2, p3, uv1, uv2, uv3) {
const barycoord = new Vector3();
THREE.Triangle.barycoordFromPoint(point, p1, p2, p3, barycoord);
uv1.multiplyScalar(barycoord.x);
uv2.multiplyScalar(barycoord.y);
uv3.multiplyScalar(barycoord.z);
uv1.add(uv2).add(uv3);
const tempUvs = uv1.clone();
const uvs = new Vector2(tempUvs.x, tempUvs.y);
return uvs;
} */
/**
* deep clone this Mesh
* return {Mesh} a new clone (copy) of this Mesh in a new Mesh object
*/
clone() {
const tempTransform = this.transform;
const clone = new Mesh({ primitive: "custom" });
clone.geometry = this.geometry.clone();
clone.material = new Material({ type: this.material.type });
clone.material._material = this.material._material.clone();
clone.genereateMesh();
clone.transform.setLocalScale(tempTransform.getLocalScale());
clone.transform.setLocalPosition(tempTransform.getLocalPosition());
clone.transform.setLocalRotation(tempTransform.getLocalRotation());
return clone;
}
}