@sohale/implisolid
Version:
A Solid Modelling Kernel in Javascript based on Implicit Surfaces, for MP5 file format
741 lines (573 loc) • 24.1 kB
HTML
<html>
<head>
<meta charset="utf-8"/>
</head>
<!-- Memory profiler code by "jj":
from https://groups.google.com/forum/#!topic/emscripten-discuss/HtAzVposlX4
-->
<script type="text/javascript" src="memoryprofiler.js"></script>
<script>
function letmeknow(){console.log("111111");}
Module={preRun:[letmeknow, memoryprofiler_add_hooks]};
</script>
<body>
<script type="text/javascript" src="mcc1.cpp.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.js"></script>
<!--script type="text/javascript" src="https://raw.githubusercontent.com/mrdoob/three.js/69079243426506ed86d0d0ea71b4fdda9dbb2f88/examples/js/controls/OrbitControls.js"></script
-->
<!-- Refused to execute script '...' because its MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.
-->
<script type="text/javascript" src="./controls/OrbitControls.js"></script>
<!--<body>-->
Based on MCC1
<br>
<script>
//'use strict';
//Version 1 has flaws: _free()s before returning the verts.
var CALL_VERSION = 2; //1 or 2
const VERBOSE = false;
var WIREFRAME = false;
var produce_object;
var main;
var newProducer;
function init1()
{
function letmeknow(){console.log("111111");}
//Module.preRun.push(letmeknow,memoryprofiler_add_hooks,letmeknow);
//Module={preRun:[letmeknow, memoryprofiler_add_hooks]};
main = Module.cwrap('main', 'number', []);
produce_object = Module.cwrap('produce_object', null, ['number', 'number', 'number', 'number', 'number']);
newProducer={};
newProducer.build_geometry = Module.cwrap('build_geometry', null, ['number', 'string', 'number']);
newProducer.get_v_size = Module.cwrap('get_v_size', 'number', []);
newProducer.get_f_size = Module.cwrap('get_f_size', 'number', []);
newProducer.get_v = Module.cwrap('get_v', null, ['number']);
newProducer.get_f = Module.cwrap('get_f', null, ['number']);
newProducer.finish_geometry = Module.cwrap('finish_geometry', null, []);
newProducer.about = Module.cwrap('about', null, []);
console.log("ok");
}
//const DBLFLOAT_SIZE = 8;
const _FLOAT_SIZE = 4; //Float32Array.BYTES_PER_ELEMENT
const _INT_SIZE = 4;
//4 int
//4 float
//4 long
var global_time = 0;
function do_vf(delta_t)
{
//var time = 0.2;
global_time += delta_t;
//var max_verts = 40000; // *2
//var max_faces = 15000; //*5
var max_verts = 16000; // *2
var max_faces = 13000; //*5
var verts_address = Module._malloc(_FLOAT_SIZE*3*max_verts);
var nv_address = Module._malloc(_INT_SIZE*1);
var faces_address = Module._malloc(_INT_SIZE*3*max_faces)
var nf_address = Module._malloc(_INT_SIZE*1);
//todo: how to make sure enough space is allocated.
/* ************************* */
/* Call C++ */
produce_object(verts_address, nv_address, faces_address, nf_address, global_time);
/* ************************* */
//Module.HEAPF64 is a Float64Array
var nverts = Module.HEAPU32[nv_address/_INT_SIZE];
var nfaces = Module.HEAPU32[nf_address/_INT_SIZE];
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);
//what if there is not enough memory?
// var result_faces = new Float32Array(dataHeap.buffer, dataHeap.byteOffset, data.length);
//verts_address is byteOffset?
//second attemtps: (still has problems)
var result_verts = new Float32Array(Module.HEAPU32.buffer, Module.HEAPU32.byteOffset + verts_address, 3*nverts * _FLOAT_SIZE);
//Thisrd solution:
//http://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html
var h_len = (Module.HEAPF32.length);
var v_end=(verts_address/_FLOAT_SIZE + 3*nverts);
var f_end = (faces_address/_INT_SIZE + 3*nfaces);
if(h_len <= v_end || h_len <= f_end)
console.error("Insufficient heap: "+(h_len - v_end)+ " , "+(h_len - f_end));
else
console.info("Heap size OK.");
//console.log(Module.HEAPF32.subarray(verts_address/_FLOAT_SIZE, verts_address/_FLOAT_SIZE + 3*2));
//console.log("subarray");
//console.log(verts.subarray(verts.length-100, verts.length));
// ****************************
// The actual leak was here !
// ****************************
//freeing resources
//also see http://kapadia.github.io/emscripten/2013/09/13/emscripten-pointers-and-pointers.html
Module._free(verts_address);
Module._free(nv_address);
Module._free(faces_address);
Module._free(nf_address);
//BUG! Accessed (returned) after "_Free"ing.
// FIXME: FREED before actually copying
//console.log(Module.HEAPF32.subarray(verts_address/_FLOAT_SIZE, verts_address/_FLOAT_SIZE + 3*2));
//console.log(result_verts.subarray(0, 3*2));
if(VERBOSE){
console.log("nverts : "+ nverts);
console.log("nfaces : "+ nfaces);
}
return [verts, faces];
//return [result_verts, faces];
}
// //third way:
// function vf(){
// //function _produce_object($verts,$nv,$faces,$nf,$time){}
//
// // $verts = $verts|0;
// // $nv = $nv|0;
// // $faces = $faces|0;
// // $nf = $nf|0;
// // $time = +$time;
// //$254 = $verts..
// //HEAPF32[$254>>2] = $251;
// //
// }
;
MyBufferGeometry = function ( verts, faces ) {
//MyBufferGeometry = function ( verts, faces, width, height, depth, widthSegments, heightSegments, depthSegments ) {
//console.log(faces);
if(VERBOSE)
console.log("MyBufferGeometry");
THREE.BufferGeometry.call( this );
//console.log("raw v size " + verts.length);
//console.log("vl " + verts.length/3);
//console.log("fl " + faces.length/3);
this.type = 'MyBufferGeometry';
this.parameters = {
//width: width,
//height: height,
//depth: depth,
//widthSegments: widthSegments,
//heightSegments: heightSegments,
//depthSegments: depthSegments
};
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 = new Float32Array( vertexCount * 3 );
var uvs = new Float32Array( vertexCount * 2 );
// 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
nans_warnings = 0;
//console.log("vertexCount " + vertexCount)
SCALE = 1.
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)
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 ++;
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));
//console.log(indices);
materialIndex = 0;
this.addGroup( 0, facecount*3, materialIndex ); //not sure about *3 . Why??
if(nans_warnings > 0)
console.error("WARNING: NaN in vertices. "+nans_warnings/978+" out of "+vertexCount/978+ " subtract:"+(vertexCount-nans_warnings)/978);
//this.addGroup( groupStart, groupCount, materialIndex ); //groupCount is same as indices' index.
//modified, but not output: indexBufferOffset, vertexBufferOffset, uvBufferOffset, numberOfVertices, groupStart
// build geometry
this.setIndex( new THREE.BufferAttribute( indices, 3 ) );
this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
// helper functions
function calculateVertexCount ( w, h, d ) {
var vertices = 0;
// calculate the amount of vertices for each side (plane)
vertices += (w + 1) * (h + 1) * 2; // xy
vertices += (w + 1) * (d + 1) * 2; // xz
vertices += (d + 1) * (h + 1) * 2; // zy
return vertices;
}
function calculateIndexCount ( w, h, d ) {
var index = 0;
// calculate the amount of squares for each side
index += w * h * 2; // xy
index += w * d * 2; // xz
index += d * h * 2; // zy
return index * 6; // two triangles per square => six vertices per square
}
};
MyBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
MyBufferGeometry.prototype.constructor = MyBufferGeometry;
MyGeometry = function (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 MyBufferGeometry( verts, faces ) );
//this.mergeVertices();
};
MyGeometry.prototype = Object.create( THREE.Geometry.prototype );
MyGeometry.prototype.constructor = MyGeometry;
/*
// MyLiveUpdatableGeometry
LiveGeometry = function () {
//material_wireframe = ...;
THREE.THREE.ImmediateRenderObject.call( this, material_wireframe );
this.setVF = function(verts, faces){
}
};
LiveGeometry.prototype = Object.create( THREE.Geometry.prototype );
LiveGeometry.prototype.constructor = LiveGeometry;
*/
function make_geometry(delta_t)
{
var startTime = new Date();
//new Date().getTime()
if(delta_t === undefined)
delta_t = 0.2;
vf = do_vf(delta_t);
v = vf[0];
f = vf[1];
var endTime = new Date();
var timeDiff = endTime - startTime;
console.log("TIME: "+timeDiff+ " msec.");
return new MyGeometry(v, f);
}
function make_geometry_new(delta_t)
{
var startTime = new Date();
if(delta_t === undefined)
delta_t = 0.2;
// Subjective time!
//var time = 0.2;
global_time += delta_t;
newProducer.build_geometry( 28, "sphere", global_time);
var nverts = newProducer.get_v_size();
var nfaces = newProducer.get_f_size();
if(VERBOSE){
console.log(nverts);
}
var verts_address = Module._malloc(_FLOAT_SIZE*3*nverts);
var faces_address = Module._malloc(_INT_SIZE*3*nfaces);
newProducer.get_v(verts_address, nverts);
newProducer.get_f(faces_address, nfaces);
var verts = Module.HEAPF32.subarray(verts_address/_FLOAT_SIZE, verts_address/_FLOAT_SIZE + 3*nverts);
//console.log(verts);
var faces = Module.HEAPU32.subarray(faces_address/_INT_SIZE, faces_address/_INT_SIZE + 3*nfaces);
var geom = new MyGeometry(verts, faces);
// Now you can free the resources.
Module._free(verts_address);
Module._free(faces_address);
newProducer.finish_geometry();
if(VERBOSE){
console.log("nverts : "+ nverts);
console.log("nfaces : "+ nfaces);
}
var endTime = new Date();
var timeDiff = endTime - startTime;
console.log("TIME: "+timeDiff);
return geom;
}
mesh_wireframe = null;
function init2() {
scene = new THREE.Scene();
WIDTH = 300;
HEIGHT = 300;
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight - 200; //for memory profiler
camera = new THREE.PerspectiveCamera( 75, WIDTH / HEIGHT, 1, 10000 );
camera.position.z = 1000;
materials_list = look_nice(scene);
material_wireframe = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true, opacity:0.3, transparent: true, } );
//geometry = new THREE.BoxGeometry( 200, 200, 200 );
if(CALL_VERSION==1){
geometry = make_geometry();
}
else{
geometry = make_geometry_new();
}
mesh_wireframe = new THREE.Mesh( geometry, material_wireframe );
mesh_wireframe.position.set( 0, 0, 0 );
//mesh_wireframe.scale.set( 10*10, 10*10, 10*10 );
mesh_wireframe.scale.set( 300, 300, 300 );
//mesh_wireframe.position.z = +0;
//mesh_wireframe.rotation.y = -Math.PI * .5;
scene.add( mesh_wireframe );
//this.fromBufferGeometry( new THREE.SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) );
material_f = new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: false, opacity:0.4, transparent: true } );
//mesh_solid = new THREE.Mesh( geometry, material_f );
//nice ones: 2, 3, (checked until 4)
mesh_solid = new THREE.Mesh( geometry, materials_list[3] );
mesh_solid.position.set( 0, 0, 0 );
mesh_solid.scale.set( 300, 300, 300 );
scene.add( mesh_solid );
geo2 = new THREE.BoxGeometry( 20, 20, 20 );
mesh2_box_wireframe = new THREE.Mesh( geo2, material_wireframe );
mesh2_box_wireframe .position.set( 0, 0, 0 );
scene.add( mesh2_box_wireframe );
//mesh2_box_wireframe .scale.set( 10, 10, 10 );
var dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(100, 100, 50);
scene.add(dirLight);
if(VERBOSE){
console.log("RENDERER");
}
renderer = new THREE.WebGLRenderer( { antialias: true } );
//renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setSize( WIDTH, HEIGHT );
document.body.appendChild( renderer.domElement );
/*
function update_mc_multiple() {
var mc_icounter=0;
setInterval(function(){ if(mc_icounter<10) update_mc(0.2/10.*5); mc_icounter++; }, 30);
}
*/
//var interval_active = 0;
var last_active = 0;
var intervals_counter = 0;
function update_mc_multiple() {
//var mc_icounter=0;
var already_busy_doing_it=0; // semaphore. (Refractory). Fast x1.
var burst_counter = 0;
//already_busy_doing_it level1: right now.
//already_busy_doing_it: interval (level 2)
intervals_counter++;
if(intervals_counter>1) //dont start if there is already one running
{
intervals_counter--;
return;
}
last_active = setInterval (
function()
{
/*
var ls="";
for (var lft=0;lft<100;lft++){
ls+=" ";
}
console.log(ls.slice(1));
console.log("1msec");
return;
*/
if(!last_active)
console.error("!last_active");
burst_counter++; // mc_icounter
if(burst_counter<10) //This code is amazingly similar to Bursting dyamics in neurons.
{
if(already_busy_doing_it<1) //removable
{
already_busy_doing_it++;
update_mc(0.2/10.*5);
if(already_busy_doing_it>1)
console.error(">>>already_busy_doing_it:"+already_busy_doing_it);
if(!last_active)
console.error("!last_active");
already_busy_doing_it--;
}else{
console.log("hit");
}
}
else
{
// The interval has finished its job. Time to go.
if(!last_active)
console.error("!last_active");
clearInterval(last_active); // But then don't we need to wait until the last one is finished?
intervals_counter--; // Allow next one in future
burst_counter = 0;
last_active = 0;
}
},
40 //30
);
/*
//broken code:
//fast x 1, fast x10, fast x 100
last_active = setInterval(
function()
{
console.info(interval_active+ " "+mc_icounter+" busy already_busy_doing_it:"+already_busy_doing_it);
if(interval_active === 0) // interval_active is shared "between" calls to this
{
console.log("****START****");
interval_active = 1;
if(mc_icounter<10)
{
if(already_busy_doing_it<1)
{
already_busy_doing_it++;
update_mc(0.2/10.*5);
console.log(">>>already_busy_doing_it:"+already_busy_doing_it);
already_busy_doing_it--;
}
}
else
{
if(last_active === 0)
console.error("a");
clearInterval(last_active);
last_active = 0;
interval_active=0;
}
//mc_icounter++;
console.log("****STOP****");
}
mc_icounter++;
if(mc_icounter>10)
{
clearInterval(last_active);
interval_active=0;
}
}, 30);
*/
}
function doKeyDownListener(event) {
if ( event.keyCode == 17 ) {
//update_mc();
update_mc_multiple();
}
else
console.log(event.keyCode);
}
window.addEventListener( "keydown", doKeyDownListener, false ); // keydown keypress
// CONTROLS
//see https://github.com/mrdoob/three.js/blob/master/examples/webgl_geometry_teapot.html
cameraControls = new THREE.OrbitControls( camera, renderer.domElement );
cameraControls.target.set( 0, 0, 0 );
cameraControls.addEventListener( 'change', function () {;} );
}
function update_mc(delta_t){
scene.remove(mesh_solid);
scene.remove(mesh_wireframe);
var geometry;
if(CALL_VERSION==1){
geometry = make_geometry(delta_t);
}
else{
geometry = make_geometry_new(delta_t);
}
mesh_wireframe = new THREE.Mesh( geometry, material_wireframe );
mesh_wireframe.position.set( 0, 0, 0 );
mesh_wireframe.scale.set( 300, 300, 300 );
scene.add( mesh_wireframe );
mesh_solid = new THREE.Mesh( geometry, materials_list[3] );
mesh_solid.position.set( 0, 0, 0 );
mesh_solid.scale.set( 300, 300, 300 );
scene.add( mesh_solid );
}
//based on http://threejs.org/examples/#webgl_materials
function generateTexture() {
var canvas = document.createElement( 'canvas' );
canvas.width = 256;
canvas.height = 256;
var context = canvas.getContext( '2d' );
var image = context.getImageData( 0, 0, 256, 256 );
var x = 0, y = 0;
for ( var i = 0, j = 0, l = image.data.length; i < l; i += 4, j ++ ) {
x = j % 256;
y = x == 0 ? y + 1 : y;
image.data[ i ] = 255;
image.data[ i + 1 ] = 255;
image.data[ i + 2 ] = 255;
image.data[ i + 3 ] = Math.floor( x ^ y );
}
context.putImageData( image, 0, 0 );
return canvas;
}
function look_nice(scene){
var materials = [];
// Grid
var line_material = new THREE.LineBasicMaterial( { color: 0x303030 } ),
geometry = new THREE.Geometry(),
floor = -75, step = 25;
for ( var i = 0; i <= 40; i ++ ) {
geometry.vertices.push( new THREE.Vector3( - 500, floor, i * step - 500 ) );
geometry.vertices.push( new THREE.Vector3( 500, floor, i * step - 500 ) );
geometry.vertices.push( new THREE.Vector3( i * step - 500, floor, -500 ) );
geometry.vertices.push( new THREE.Vector3( i * step - 500, floor, 500 ) );
}
var line = new THREE.LineSegments( geometry, line_material );
scene.add( line );
// Materials
var texture = new THREE.Texture( generateTexture() );
texture.needsUpdate = true;
materials.push( new THREE.MeshLambertMaterial( { map: texture, transparent: true } ) );
materials.push( new THREE.MeshLambertMaterial( { color: 0xdddddd, shading: THREE.FlatShading } ) );
materials.push( new THREE.MeshPhongMaterial( { color: 0xdddddd, specular: 0x009900, shininess: 30, shading: THREE.FlatShading } ) );
materials.push( new THREE.MeshNormalMaterial( ) );
materials.push( new THREE.MeshBasicMaterial( { color: 0xffaa00, transparent: true, blending: THREE.AdditiveBlending } ) );
//materials.push( new THREE.MeshBasicMaterial( { color: 0xff0000, blending: THREE.SubtractiveBlending } ) );
materials.push( new THREE.MeshLambertMaterial( { color: 0xdddddd, shading: THREE.SmoothShading } ) );
materials.push( new THREE.MeshPhongMaterial( { color: 0xdddddd, specular: 0x009900, shininess: 30, shading: THREE.SmoothShading, map: texture, transparent: true } ) );
materials.push( new THREE.MeshNormalMaterial( { shading: THREE.SmoothShading } ) );
materials.push( new THREE.MeshBasicMaterial( { color: 0xffaa00, wireframe: true } ) );
materials.push( new THREE.MeshDepthMaterial() );
materials.push( new THREE.MeshLambertMaterial( { color: 0x666666, emissive: 0xff0000, shading: THREE.SmoothShading } ) );
materials.push( new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x666666, emissive: 0xff0000, shininess: 10, shading: THREE.SmoothShading, opacity: 0.9, transparent: true } ) );
materials.push( new THREE.MeshBasicMaterial( { map: texture, transparent: true } ) );
return materials;
}
function animate() {
requestAnimationFrame( animate );
var SPEED = 3. / 100;
if(mesh_wireframe){
mesh_wireframe.rotation.x += 0.01*SPEED;
mesh_wireframe.rotation.y += 0.02*SPEED;
mesh_solid.rotation.x += 0.01*SPEED;
mesh_solid.rotation.y += 0.02*SPEED;
}
mesh2_box_wireframe .rotation.z += 0.01*SPEED;
mesh2_box_wireframe .rotation.y += 0.02*SPEED;
//materials_list[ materials.length - 2 ].emissive.setHSL( 0.54, 1, 0.35 * ( 0.5 + 0.5 * Math.sin( 35 * timer ) ) );
//materials_list[ materials.length - 3 ].emissive.setHSL( 0.04, 1, 0.35 * ( 0.5 + 0.5 * Math.cos( 35 * timer ) ) );
renderer.render( scene, camera );
}
var scene, camera, renderer;
var geometry, material_wireframe, mesh_wireframe;
window.setTimeout(function()
{
init1();
init2();
animate();
}, 3000);
</script>