@sohale/implisolid
Version:
A Solid Modelling Kernel in Javascript based on Implicit Surfaces, for MP5 file format
478 lines (394 loc) • 15.9 kB
JavaScript
;
/*
Three ways:
1- Using WGeometry77 using faster=true
2- " " " using faster=false which uses make_geometry_core()
3- " " " using faster=false which uses make_geometry_core_slower()
4- UsingUpdatable geometry; WGeometry77.prototype.update()
*/
function make_geometry_core( verts, faces) {
var indices = faces;
var vertices = verts;
var normals = null, uvs = null;
//vertices = verts;
//indices = faces;
return {
indices: indices,
vertices: vertices,
normals: normals,
uvs: uvs
};
}
//Core contains the mesh (or changes to the mesh)
function make_geometry_core_slower( verts, faces) {
const ENABLE_NORMALS = false;
var vertexCount = verts.length/3;
var facecount = faces.length/3;
var indexCount = facecount*3;
if(VERBOSE){
console.log("vertexCount="+vertexCount+ " , facecount=" + facecount+ " facecount*3="+(facecount*3));
}
// buffers
var indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount );
var vertices = new Float32Array( vertexCount * 3 );
var normals, uvs; // plan
if(ENABLE_NORMALS){
normals = new Float32Array( vertexCount * 3 );
uvs = new Float32Array( vertexCount * 2 );
}
else{
normals = null;
uvs = null;
}
// offset variables
//var vertexBufferOffset = 0;
//var uvBufferOffset = 0;
//var indexBufferOffset = 0;
//var numberOfVertices = 0;
// group variables
//var groupStart = 0;
// build each side of the box geometry
//output: vertices, normals, uvs, indices
var nans_warnings = 0;
//console.log("vertexCount " + vertexCount)
var SCALE = 1.
var x,y,z,d;
for(var i=0; i < vertexCount; i++)
{
for(var di = 0; di < 3; di++)
vertices[i*3+di] = verts[i*3+di] * SCALE;
x = verts[i*3+0];
y = verts[i*3+1];
z = verts[i*3+2];
//console.log(x+" , " + y + " , " + z);
d = Math.sqrt(x*x+y*y+z*z);
d=d+0.;
//if(d==0) d=1.;
if(isNaN(d)) {
d=1.;x=1.;y=1.;z=1.;
console.log("Warning");
}
//console.log("x y z"+x+" "+y+" "+z+" / "+ d)
if(ENABLE_NORMALS){
var sgn = +1;
normals[i*3 + 0] = x/d*sgn;
normals[i*3 + 1] = y/d*sgn;
normals[i*3 + 2] = z/d*sgn;
if(isNaN(x/d))
nans_warnings ++;
var d2 = Math.sqrt(x*x+y*y);
uvs[i*2+0] = x/d2;
uvs[i*2+1] = y/d2;
}
}
for(var i=0; i < facecount; i++)
for(var si=0; si<3; si++)
indices[i*3+si] = faces[i*3+si];
//console.log(verts.subarray(0,3*3*3));
//console.log(indices.subarray(0,3*3*3));
if(nans_warnings > 0)
console.error("WARNING: NaN in vertices. "+nans_warnings/978+" out of "+vertexCount/978+ " subtract:"+(vertexCount-nans_warnings)/978);
return {
indices: indices,
vertices: vertices,
normals: normals,
uvs: uvs
};
}
function checktype(src, _type){
//if(typeof src !== type_name)
if(!(src instanceof _type))
{
console.error();
throw "Developer's error; " + (typeof src) + (Object.prototype.toString.call(src.buffer));
}
}
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays
function copy_Float32Array(src) {
checktype(src, Float32Array);
return new Float32Array(src);
}
function copy_Uint32Array(src) {
checktype(src, Uint32Array);
return new Uint32Array(src);
}
function copy_Uint16Array(src) {
checktype(src, Uint16Array);
return new Uint16Array(src);
}
function copy_Float32Array_preallocated(src, prealloc_size, initial_value) {
checktype(src, Float32Array);
var TYPE_SIZE = 4;
var len_bytes = Math.max(prealloc_size*TYPE_SIZE, src.byteLength);
var dst = new ArrayBuffer(len_bytes);
var r = new Float32Array(dst);
r.set(new Float32Array(src));
/*if(randomize){
for(var i=0*src.byteLength/TYPE_SIZE;i<r.length;i++){
r[i] = (Math.random()-0.5);
}
}*/
if(initial_value === "random"){
for(var i=0*src.byteLength/TYPE_SIZE;i<r.length;i++){
r[i] = (Math.random()-0.5);
}
}else if (Number(initial_value) === initial_value){
console.log("color number :" + initial_value + " " + len_bytes);
for(var i=(0*src.byteLength/TYPE_SIZE);i<r.length;i++){
r[i] = initial_value;
}
}
return r;
}
function copy_Uint32Array_preallocated(src, prealloc_size) {
checktype(src, Uint32Array);
var TYPE_SIZE = 4;
var len_bytes = Math.max(prealloc_size*TYPE_SIZE, src.byteLength);
var dst = new ArrayBuffer(len_bytes);
var r = new Uint32Array(dst);
r.set(new Uint32Array(src));
return r;
}
function copy_Uint16Array_preallocated(src, prealloc_size) {
checktype(src, Uint16Array);
var TYPE_SIZE = 2;
var len_bytes = Math.max(prealloc_size*TYPE_SIZE, src.byteLength);;
var dst = new ArrayBuffer(len_bytes);
var r = new Uint16Array(dst);
r.set(new Uint16Array(src));
return r;
}
/** Simply creates a geometry . This is static and cannot be modified when displayed. Instantiate a new one and make a new THREE.Mesh() */
function MyBufferGeometry77( verts, faces, re_allocate) {
THREE.BufferGeometry.call( this );
this.type = 'MyBufferGeometry77';
this.parameters = { };
this.initbg = function(){
const faster = true;
if(faster){
if(1){
//var materialIndex = 0;
//this.addGroup( 0, faces.length*1-10, materialIndex ); //not sure about *3 . Why??
//console.log("ok2");
}
if(re_allocate){
//Float32Array, Uint32Array
/*
faces = copy_Uint32Array(faces, prealloc_size);
verts = copy_Float32Array(verts, prealloc_size);
*/
console.log("Allocating separate space for verts,faces.");
faces = copy_Uint32Array_preallocated(faces, 30000*3);
verts = copy_Float32Array_preallocated(verts, 30000*3);
}
//WRONG! WHEN re_allocate is false
// build geometry
this.addAttribute( 'position', new THREE.BufferAttribute( verts, 3 ) );
this.setIndex( new THREE.BufferAttribute( faces, 3 ) ); //new Uint32Array(faces) ??
for(var j=0;j<faces.length;j++)
if(this.index.array[j] !== faces[j]){
console.error(j);break;
}
//my_assert(this.index.array === faces);
if(re_allocate){
console.log("Allocating separate space for norms, colors.");
var normals = copy_Float32Array_preallocated(verts, 30000*3, true);
var colors = copy_Float32Array_preallocated(verts, 30000*3, true);
//var uvs = copy_Float32Array_preallocated(new Float32Array([]), 30000*3 * 0, true);
}
this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3, true ) );
//Todo: color and uvs are not used in rendering. Remove them for now. But normal is important.
this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3, true ) );
//this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
//var materialIndex = 0;
//this.addGroup( 0, faces.length*1-10, materialIndex ); //not sure about *3 . Why??
}else{
var mesh_core = make_geometry_core(verts, faces);
//var mesh_core = make_geometry_core_slower(verts, faces);
// build geometry
this.setIndex( new THREE.BufferAttribute( mesh_core.indices, 3 ) );
this.addAttribute( 'position', new THREE.BufferAttribute( mesh_core.vertices, 3 ) );
if(mesh_core.normals) {
this.addAttribute( 'normal', new THREE.BufferAttribute( mesh_core.normals, 3 ) );
this.addAttribute( 'uv', new THREE.BufferAttribute( mesh_core.uvs, 2 ) );
}
if(0){
var materialIndex = 0;
this.addGroup( 0, mesh_core.indices.length, materialIndex ); //not sure about *3 . Why??
}
}
}
// ThreeJS does not use prototype-based OOP.
// still not refactored into update_geometry1
this.update_geometry_ = function(polygonizer_service) {
console.error("error: geometry.update_geometry(iservice) is deprecated. Use instead: iservice.update_geometry(geometry); ");
var geometry = this;
var nverts = polygonizer_service.get_v_size();
var nfaces = polygonizer_service.get_f_size();
var verts_address = polygonizer_service.get_v_ptr();
var faces_address = polygonizer_service.get_f_ptr();
var verts = Module.HEAPF32.subarray(
verts_address/_FLOAT_SIZE,
verts_address/_FLOAT_SIZE + 3*nverts);
var faces = Module.HEAPU32.subarray(
faces_address/_INT_SIZE,
faces_address/_INT_SIZE + 3*nfaces);
// REFACTORED. NOTE TESTED.
this.update_geometry1(verts, faces);
};
this.update_geometry1 = function(verts, faces) {
//NOT TESTED.
var geometry = this;
//var g_nverts = geometry.attributes.position.count/3; // Displayed size
//check allocated space
var g_nverts = geometry.attributes.position.array.length/3; // Physical space size.
var g_nfaces = geometry.index.array.length/3;
//console.log(nverts); console.log(g_nverts);
//console.log(nfaces); console.log(g_nfaces);
var nverts = verts.length / 3;
var nfaces = faces.length / 3;
//Bug revealed by commenting the following! Changing the geometry broke the C++ code !
var nv3 = Math.min(nverts, g_nverts) * 3;
var nf3 = Math.min(nfaces, g_nfaces) * 3;
//console.log(nv3/3. +" === "+ verts.length/3.)
my_assert(nv3 === verts.length);
my_assert(nf3 === faces.length);
/*
for(var i=0;i<nv3;i++){
geometry.attributes.position.array[i] = verts[i]; //or use copy
}
for(var i=0;i<nf3;i++){
geometry.index.array[ i ] = faces[i];
}
*/
/*
var geometryAttributes = geometry.attributes;
var geometryAttribute = geometryAttributes[ name ];
geometryAttribute instanceof THREE.InterleavedBufferAttribute
if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) {
var data = geometryAttribute.data;
if ( data instanceof THREE.InstancedInterleavedBuffer ) {
*/
if(0){
name = 'position';
var geometryAttributes = geometry.attributes;
var geometryAttribute = geometryAttributes[ name ];
var a_IL = geometryAttribute instanceof THREE.InterleavedBufferAttribute
if(a_IL){
var data = geometryAttribute.data;
var i_IL = data instanceof THREE.InstancedInterleavedBuffer;
console.log( a_IL + " " + i_IL );
}
else{
var instanced = geometryAttribute instanceof THREE.InstancedBufferAttribute;
console.log( a_IL + " not interleaved " + instanced ); //false, false
}
}
if(1){
// *************************************
// * The following code works only when we use a single instance of the geometry, i.e. used in one Mesh.
// * It will not work well if the same BufferGeometry is used in more than one Mesh
// * So the object will look fine if we disable the wireframe mesh.
// *************************************
geometry.attributes.position.array.set(verts);
geometry.index.array.set(faces);
//geometry.attributes.color.array.set(verts);
geometry.attributes.normal.array.set(verts);
}
geometry.setDrawRange( 0, nf3 );
/*geometry.clearGroups();
geometry.addGroup( 0, nf3, 0 );
*/
geometry.attributes.position.needsUpdate = true;
//geometry.attributes.color.needsUpdate = true;
geometry.attributes.normal.needsUpdate = true;
geometry.index.needsUpdate = true;
//geometry.index.array[i] === faces[i]
//geometry.setBou
//geometry.computeFaceNormals();
//geometry.computeBoundingSphere();
if(0){
for(var j=0;j<faces.length;j++)
if(!(geometry.index.array[j] === faces[j])){
console.error(j);break;
}
for(var j=0;j<verts.length;j++){
if(!(geometry.attributes.position.array[j] === verts[j]))
console.error(j);break;
}
}
};
this.use_default_normals_from_vertices = function () {
// use default normals
// candidate name: remove_normals()
console.warn("not tested for veriosn prior to r79");
this.removeAttribute('normal');
this.computeVertexNormals();
this.__set_needsUpdate_flag(false);
}
this.initbg()
};
MyBufferGeometry77.prototype = Object.create( THREE.BufferGeometry.prototype );
MyBufferGeometry77.prototype.constructor = MyBufferGeometry77;
// This class is not actually necessary.
function WGeometry77(verts, faces) {
//vects, faces
THREE.Geometry.call( this );
this.type = 'ImplicitGeometry'; //?
this.parameters = {
/*width: width,
height: height,
depth: depth,
widthSegments: widthSegments,
heightSegments: heightSegments,
depthSegments: depthSegments*/
};
//faces = faces.subarray(0, 300);
this.fromBufferGeometry( new MyBufferGeometry77( verts, faces ) );
//this.mergeVertices();
};
WGeometry77.prototype = Object.create( THREE.Geometry.prototype );
WGeometry77.prototype.constructor = WGeometry77;
/* The following code will not work, but is in progress.
WGeometry77.prototype.update = function(verts, faces) {
var geom = this;
//for ( var vi = 0, l = geom.vertices.length; vi < l; vi ++ ) {
// var time = Math.random();
// geom.vertices[ vi ].y += 0.01*(Math.random()-0.5) ; // 0.1* 35 * Math.sin( vi / 5 + ( time + vi ) / 7 );
//}
var l = Math.min(geom.vertices.length, verts.length/3);
for ( var vi = 0; vi < l; vi ++ ) {
//geom.vertices[ vi ].x += verts[vi*3+0];
//geom.vertices[ vi ].y += verts[vi*3+1];
//geom.vertices[ vi ].z += verts[vi*3+2];
geom.vertices[ vi ].x = verts[vi*3+0];
geom.vertices[ vi ].y = verts[vi*3+1];
geom.vertices[ vi ].z = verts[vi*3+2];
}
var lv = Math.min(geom.faces.length, faces.length/3);
for ( var fi = 0; fi < lv; fi ++ ) {
geom.faces[ fi ].a = faces[fi*3+0];
geom.faces[ fi ].b = faces[fi*3+1];
geom.faces[ fi ].c = faces[fi*3+2];
}
geom.verticesNeedUpdate = true;
}
*/
/*
function UpdatableGeometry77(verts, faces)
{
WGeometry77.call( this, verts, faces );
this.type = 'UpdatableGeometry77';
}
//this.geo = new WGeometry77(verts, faces);
UpdatableGeometry77.prototype.update = function(verts, faces) {
geom = this;
var time = math.random();
for ( var vi = 0, l = geom.vertices.length; vi < l; vi ++ ) {
geom.vertices[ vi ].y += 35 * Math.sin( vi / 5 + ( time + vi ) / 7 );
}
geom.verticesNeedUpdate = true;
}
UpdatableGeometry77.prototype = Object.create( WGeometry77.prototype );
UpdatableGeometry77.prototype.constructor = UpdatableGeometry77;
*/