Improve WebGL export javascript template
Several improvements to the javascript in the template: * Render on demand rather than continuously * Support high-DPI displays * Fix the frustum depth for both cameras * Deal correctly with the browser window being resized * Only show controls for objects with geometry Also some changes made for code clarity: * Use 'const' and 'let' instead of 'var', as appropriate * Use for...of and .map() instead of iterating over arrays using a loop counter
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
"""FreeCAD WebGL Exporter"""
|
||||
|
||||
import FreeCAD,Mesh,Draft,Part,OfflineRenderingUtils,json,six
|
||||
import textwrap
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
@@ -60,291 +61,324 @@ base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!#$%&()*+-
|
||||
baseFloat = ',.-0123456789'
|
||||
|
||||
def getHTMLTemplate():
|
||||
return """<!DOCTYPE html>
|
||||
return textwrap.dedent("""\
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>$pagetitle</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
|
||||
<meta name="generator" content="FreeCAD $version" />
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||||
<meta name="generator" content="FreeCAD $version">
|
||||
<style>
|
||||
* { margin:0; padding:0; }
|
||||
body{
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
background: #ffffff; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #e3e9fc 0%, #ffffff 70%, #e2dab3 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(top, #e3e9fc 0%,#ffffff 70%,#e2dab3 100%); /* Chrome10-25, Safari5.1-6 */
|
||||
background: linear-gradient(to bottom, #e3e9fc 0%,#ffffff 70%,#e2dab3 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
canvas { display: block; }
|
||||
#mainCanvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#arrowCanvas {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
z-index: 100;
|
||||
}
|
||||
select { width: 170px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="mainCanvas"></canvas>
|
||||
<canvas id="arrowCanvas"></canvas>
|
||||
<script type="module">
|
||||
|
||||
// Direct from mrdoob - r122(10/28/20): https://www.jsdelivr.com/package/npm/three
|
||||
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.122.0/build/three.module.js';
|
||||
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/controls/OrbitControls.js';
|
||||
import Stats from 'https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/libs/stats.module.js';
|
||||
import { GUI } from 'https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/libs/dat.gui.module.js';
|
||||
import { Line2 } from 'https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/lines/Line2.js';
|
||||
import { LineMaterial } from 'https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/lines/LineMaterial.js';
|
||||
import { LineGeometry } from 'https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/lines/LineGeometry.js';
|
||||
import { EdgeSplitModifier } from 'https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/modifiers/EdgeSplitModifier.js';
|
||||
|
||||
var disableCompression = $disableCompression;
|
||||
var base = '$base';
|
||||
var baseFloat = '$float';
|
||||
|
||||
THREE.Object3D.DefaultUp = new THREE.Vector3(0,0,1); // Z is up for FreeCAD
|
||||
|
||||
var raycasterObj = []; // list of obj that can mouseover highlight
|
||||
|
||||
var scene = new THREE.Scene();
|
||||
|
||||
var renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } ); // Clear bg so we can set it with css
|
||||
renderer.setClearColor( 0x000000, 0 );
|
||||
var mainCanvas = document.body.appendChild( renderer.domElement );
|
||||
mainCanvas.setAttribute('id', 'mainCanvas');
|
||||
|
||||
// HemisphereLight gives different colors of light from the top and bottom simulating reflected light from the 'ground' and 'sky'
|
||||
// Direct from mrdoob: https://www.jsdelivr.com/package/npm/three
|
||||
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.125.0/build/three.module.js';
|
||||
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/controls/OrbitControls.js';
|
||||
import { GUI } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/libs/dat.gui.module.js';
|
||||
import { Line2 } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/lines/Line2.js';
|
||||
import { LineMaterial } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/lines/LineMaterial.js';
|
||||
import { LineGeometry } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/lines/LineGeometry.js';
|
||||
import { EdgeSplitModifier } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/modifiers/EdgeSplitModifier.js';
|
||||
|
||||
const data = $data;
|
||||
|
||||
// Z is up for FreeCAD
|
||||
THREE.Object3D.DefaultUp = new THREE.Vector3(0, 0, 1);
|
||||
|
||||
const defaultWireColor = new THREE.Color('rgb(0,0,0)');
|
||||
const defaultWireLineWidth = 2; // in pixels
|
||||
|
||||
const raycasterObj = []; // list of obj that can mouseover highlight
|
||||
|
||||
const canvas = document.querySelector('#mainCanvas');
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
canvas: canvas
|
||||
}); // Clear bg so we can set it with css
|
||||
renderer.setClearColor(0x000000, 0);
|
||||
|
||||
let renderRequested = false;
|
||||
|
||||
// HemisphereLight gives different colors of light from the top
|
||||
// and bottom simulating reflected light from the 'ground' and
|
||||
// 'sky'
|
||||
scene.add(new THREE.HemisphereLight(0xC7E8FF, 0xFFE3B3, 0.4));
|
||||
|
||||
var dLight1 = new THREE.DirectionalLight( 0xffffff, 0.4 );
|
||||
dLight1.position.set( 5, -2, 3 );
|
||||
scene.add( dLight1 );
|
||||
var dLight2 = new THREE.DirectionalLight( 0xffffff, 0.4 );
|
||||
dLight2.position.set( -5, 2, 3 );
|
||||
scene.add( dLight2 );
|
||||
|
||||
var data = $data;
|
||||
|
||||
function baseDecode( input ) {
|
||||
var baseCt = base.length;
|
||||
var output = [];
|
||||
var len = parseInt( input[0] ); // num chars of each element
|
||||
for ( var i=1; i<input.length; i+=len ) {
|
||||
var str = input.substring(i, i+len).trim();
|
||||
var val = 0;
|
||||
for ( var s=0; s<str.length; s++ ) {
|
||||
var ind = base.indexOf(str[s]);
|
||||
val += ind * Math.pow(baseCt, s);
|
||||
|
||||
const dLight1 = new THREE.DirectionalLight(0xffffff, 0.4);
|
||||
dLight1.position.set(5, -2, 3);
|
||||
scene.add(dLight1);
|
||||
const dLight2 = new THREE.DirectionalLight(0xffffff, 0.4);
|
||||
dLight2.position.set(-5, 2, 3);
|
||||
scene.add(dLight2);
|
||||
|
||||
if (data.compressed) {
|
||||
const base = data.base;
|
||||
const baseFloat = data.baseFloat;
|
||||
|
||||
function baseDecode(input) {
|
||||
const baseCt = base.length;
|
||||
const output = [];
|
||||
const len = parseInt(input[0]); // num chars of each element
|
||||
for (let i = 1; i < input.length; i += len) {
|
||||
const str = input.substring(i, i + len).trim();
|
||||
let val = 0;
|
||||
for (let s = 0; s < str.length; s++) {
|
||||
const ind = base.indexOf(str[s]);
|
||||
val += ind * Math.pow(baseCt, s);
|
||||
}
|
||||
output.push(val);
|
||||
}
|
||||
output.push(val);
|
||||
return output;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
function floatDecode( input ) {
|
||||
var baseCt = base.length;
|
||||
var baseFloatCt = baseFloat.length;
|
||||
var numString = '';
|
||||
for ( var i=0; i<input.length; i+=4 ) {
|
||||
var b90chunk = input.substring(i, i+4).trim();
|
||||
var quotient = 0;
|
||||
for ( var s=0; s<b90chunk.length; s++ ) {
|
||||
var ind = base.indexOf(b90chunk[s]);
|
||||
quotient += ind * Math.pow(baseCt, s);
|
||||
|
||||
function floatDecode(input) {
|
||||
const baseCt = base.length;
|
||||
const baseFloatCt = baseFloat.length;
|
||||
let numString = '';
|
||||
for (let i = 0; i < input.length; i += 4) {
|
||||
const b90chunk = input.substring(i, i + 4).trim();
|
||||
let quotient = 0;
|
||||
for (let s = 0; s < b90chunk.length; s++) {
|
||||
const ind = base.indexOf(b90chunk[s]);
|
||||
quotient += ind * Math.pow(baseCt, s);
|
||||
}
|
||||
let buffer = '';
|
||||
for (let s = 0; s < 7; s++) {
|
||||
buffer = baseFloat[quotient % baseFloatCt] + buffer;
|
||||
quotient = parseInt(quotient / baseFloatCt);
|
||||
}
|
||||
numString += buffer;
|
||||
}
|
||||
var buffer = '';
|
||||
for ( var s=0; s<7; s++ ) {
|
||||
buffer = baseFloat[ quotient % baseFloatCt ] + buffer;
|
||||
quotient = parseInt(quotient / baseFloatCt);
|
||||
}
|
||||
numString += buffer;
|
||||
}
|
||||
var trailingCommas = 0;
|
||||
for ( var s=1; s<7; s++ ) {
|
||||
if( numString[ numString.length-s ] == baseFloat[0] ) { trailingCommas++; }
|
||||
}
|
||||
numString = numString.substring(0, numString.length - trailingCommas);
|
||||
return numString;
|
||||
}
|
||||
// Decode from base90 and distribute the floats
|
||||
for ( var obj=0; obj<data.objects.length; obj++ ) {
|
||||
if (!disableCompression) {
|
||||
data.objects[obj].floats = JSON.parse('[' + floatDecode( data.objects[obj].floats ) + ']');
|
||||
data.objects[obj].verts = baseDecode( data.objects[obj].verts );
|
||||
for ( var v=0; v<data.objects[obj].verts.length; v++ ) {
|
||||
data.objects[obj].verts[v] = data.objects[obj].floats[ data.objects[obj].verts[v] ];
|
||||
}
|
||||
data.objects[obj].facets = baseDecode( data.objects[obj].facets );
|
||||
for ( var w=0; w<data.objects[obj].wires.length; w++ ) {
|
||||
data.objects[obj].wires[w] = baseDecode( data.objects[obj].wires[w] );
|
||||
for ( var wv=0; wv<data.objects[obj].wires[w].length; wv++ ) {
|
||||
data.objects[obj].wires[w][wv] = data.objects[obj].floats[ data.objects[obj].wires[w][wv] ];
|
||||
let trailingCommas = 0;
|
||||
for (let s = 1; s < 7; s++) {
|
||||
if (numString[numString.length - s] == baseFloat[0]) {
|
||||
trailingCommas++;
|
||||
}
|
||||
}
|
||||
for ( var f=0; f<data.objects[obj].facesToFacets.length; f++ ) {
|
||||
data.objects[obj].facesToFacets[f] = baseDecode( data.objects[obj].facesToFacets[f] );
|
||||
numString = numString.substring(0, numString.length - trailingCommas);
|
||||
return numString;
|
||||
}
|
||||
|
||||
// Decode from base90 and distribute the floats
|
||||
for (const obj of data.objects) {
|
||||
obj.floats = JSON.parse('[' + floatDecode(obj.floats) + ']');
|
||||
obj.verts = baseDecode(obj.verts).map(x => obj.floats[x]);
|
||||
obj.facets = baseDecode(obj.facets);
|
||||
obj.wires = obj.wires.map(w => baseDecode(w).map(x => obj.floats[x]));
|
||||
obj.facesToFacets = obj.facesToFacets.map(x => baseDecode(x));
|
||||
}
|
||||
}
|
||||
|
||||
// Get bounds for global clipping
|
||||
const globalMaxMin = [{min: null, max: null},
|
||||
{min: null, max: null},
|
||||
{min: null, max: null}];
|
||||
for (const obj of data.objects) {
|
||||
for (let v = 0; v < obj.verts.length; v++) {
|
||||
if (globalMaxMin[v % 3] === null
|
||||
|| obj.verts[v] < globalMaxMin[v % 3].min) {
|
||||
globalMaxMin[v % 3].min = obj.verts[v];
|
||||
}
|
||||
if (globalMaxMin[v % 3] === null
|
||||
|| obj.verts[v] > globalMaxMin[v % 3].max) {
|
||||
globalMaxMin[v % 3].max = obj.verts[v];
|
||||
}
|
||||
}
|
||||
data.objects[obj].floats = false;
|
||||
}
|
||||
|
||||
// Get bounds for global clipping
|
||||
for ( var obj=0; obj<data.objects.length; obj++ ) {
|
||||
if (obj == 0) {
|
||||
var globalMaxMin = [{min:data.objects[obj].verts[0],max:data.objects[obj].verts[0]},
|
||||
{min:data.objects[obj].verts[1],max:data.objects[obj].verts[1]},
|
||||
{min:data.objects[obj].verts[2],max:data.objects[obj].verts[2]}]
|
||||
}
|
||||
for ( var v=0; v<data.objects[obj].verts.length; v++ ) {
|
||||
if ( data.objects[obj].verts[v] < globalMaxMin[v % 3].min ) { globalMaxMin[v % 3].min = data.objects[obj].verts[v]; }
|
||||
if ( data.objects[obj].verts[v] > globalMaxMin[v % 3].max ) { globalMaxMin[v % 3].max = data.objects[obj].verts[v]; }
|
||||
let bigrange = 0;
|
||||
// add a little extra
|
||||
for (const i of globalMaxMin) {
|
||||
const range = i.max - i.min;
|
||||
if (range > bigrange) {
|
||||
bigrange = range;
|
||||
}
|
||||
i.min -= range * 0.01;
|
||||
i.max += range * 0.01;
|
||||
}
|
||||
var bigrange = 0;
|
||||
for ( var i=0; i<globalMaxMin.length; i++ ) { // add a little extra
|
||||
var range = globalMaxMin[i].max - globalMaxMin[i].min;
|
||||
if ( range > bigrange ) { bigrange = range; }
|
||||
globalMaxMin[i].min -= range * 0.01;
|
||||
globalMaxMin[i].max += range * 0.01;
|
||||
}
|
||||
|
||||
var camCenter = new THREE.Vector3(
|
||||
|
||||
const camCenter = new THREE.Vector3(
|
||||
0.5 * (globalMaxMin[0].max - globalMaxMin[0].min) + globalMaxMin[0].min,
|
||||
0.5 * (globalMaxMin[1].max - globalMaxMin[1].min) + globalMaxMin[1].min,
|
||||
0.5 * (globalMaxMin[2].max - globalMaxMin[2].min) + globalMaxMin[2].min );
|
||||
var viewSize = 1.5 * bigrange; // make the view area a little bigger than the object
|
||||
var aspectRatio = window.innerWidth / window.innerHeight;
|
||||
var originalAspect = aspectRatio
|
||||
const viewSize = 1.5 * bigrange; // make the view area a little bigger than the object
|
||||
const aspectRatio = canvas.clientWidth / canvas.clientHeight;
|
||||
const originalAspect = aspectRatio;
|
||||
|
||||
function initCam(camera) {
|
||||
// XXX this needs to treat the perspective and orthographic
|
||||
// cameras differently
|
||||
camera.position.set(
|
||||
data.camera.position_x,
|
||||
data.camera.position_y,
|
||||
data.camera.position_z);
|
||||
camera.lookAt( camCenter );
|
||||
camera.lookAt(camCenter);
|
||||
camera.updateMatrixWorld();
|
||||
}
|
||||
var cameraType = data.camera.type;
|
||||
var persCamera = new THREE.PerspectiveCamera( data.camera.focalDistance, window.innerWidth / window.innerHeight, 1, 10000 );
|
||||
|
||||
let cameraType = data.camera.type;
|
||||
const persCamera = new THREE.PerspectiveCamera(
|
||||
50, aspectRatio, 1, 100000);
|
||||
initCam(persCamera);
|
||||
var orthCamera = new THREE.OrthographicCamera(-aspectRatio * viewSize / 2, aspectRatio * viewSize / 2, viewSize / 2, -viewSize / 2, -10000, 10000);
|
||||
const orthCamera = new THREE.OrthographicCamera(
|
||||
-aspectRatio * viewSize / 2, aspectRatio * viewSize / 2,
|
||||
viewSize / 2, -viewSize / 2, -100000, 100000);
|
||||
initCam(orthCamera);
|
||||
|
||||
function assignMesh( positions, color, opacity ) {
|
||||
|
||||
var geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
|
||||
|
||||
|
||||
function assignMesh(positions, color, opacity, faces) {
|
||||
const baseGeometry = new THREE.BufferGeometry();
|
||||
baseGeometry.setAttribute('position', new THREE.BufferAttribute(
|
||||
positions, 3));
|
||||
|
||||
// EdgeSplitModifier is used to combine verts so that smoothing normals can be generated WITHOUT removing the hard edges of the design
|
||||
// REF: https://threejs.org/examples/?q=edge#webgl_modifier_edgesplit - https://github.com/mrdoob/three.js/pull/20535
|
||||
var edgeSplit = new EdgeSplitModifier();
|
||||
var cutOffAngle = 20;
|
||||
geometry = edgeSplit.modify( geometry, cutOffAngle * Math.PI / 180 );
|
||||
const edgeSplit = new EdgeSplitModifier();
|
||||
const cutOffAngle = 20;
|
||||
const geometry = edgeSplit.modify(
|
||||
baseGeometry, cutOffAngle * Math.PI / 180);
|
||||
geometry.computeVertexNormals();
|
||||
geometry.computeBoundingSphere();
|
||||
|
||||
var transparent = false;
|
||||
if (opacity != 1.0) { transparent = true; }
|
||||
var material = new THREE.MeshLambertMaterial({
|
||||
|
||||
const material = new THREE.MeshLambertMaterial({
|
||||
color: color,
|
||||
side: THREE.DoubleSide,
|
||||
vertexColors: false,
|
||||
flatShading: false,
|
||||
opacity: opacity,
|
||||
transparent: transparent,
|
||||
transparent: opacity != 1.0,
|
||||
fog: false
|
||||
});
|
||||
|
||||
var meshobj = new THREE.Mesh( geometry, material );
|
||||
|
||||
const meshobj = new THREE.Mesh(geometry, material);
|
||||
meshobj.name = meshobj.uuid;
|
||||
faces.push( meshobj.uuid );
|
||||
scene.add( meshobj );
|
||||
faces.push(meshobj.uuid);
|
||||
scene.add(meshobj);
|
||||
raycasterObj.push(meshobj);
|
||||
}
|
||||
|
||||
var objects = [];
|
||||
var positions;
|
||||
for ( var obj=0; obj<data.objects.length; obj++ ) { // Loop Objects
|
||||
|
||||
var faces = []; // Each face gets its own material because they each can have different colors
|
||||
if (data.objects[obj].facesToFacets.length > 0) {
|
||||
for ( var f=0; f<data.objects[obj].facesToFacets.length; f++ ) {
|
||||
var facecolor = data.objects[obj].color;
|
||||
if (data.objects[obj].faceColors.length > 0) {
|
||||
facecolor = data.objects[obj].faceColors[f];
|
||||
}
|
||||
positions = new Float32Array( data.objects[obj].facesToFacets[f].length * 9 );
|
||||
for ( var a=0; a<data.objects[obj].facesToFacets[f].length; a++ ) {
|
||||
for ( var b=0; b<3; b++ ) {
|
||||
for ( var c=0; c<3; c++ ) {
|
||||
positions[ 9*a + 3*b + c ] = data.objects[obj].verts[ 3*data.objects[obj].facets[ 3*data.objects[obj].facesToFacets[f][a] + b ] + c ];
|
||||
|
||||
const objects = [];
|
||||
for (const obj of data.objects) {
|
||||
// Each face gets its own material because they each can
|
||||
// have different colors
|
||||
const faces = [];
|
||||
if (obj.facesToFacets.length > 0) {
|
||||
for (let f=0; f < obj.facesToFacets.length; f++) {
|
||||
const facecolor = obj.faceColors.length > 0 ? obj.faceColors[f] : obj.color;
|
||||
const positions = new Float32Array(obj.facesToFacets[f].length * 9);
|
||||
for (let a=0; a < obj.facesToFacets[f].length; a++) {
|
||||
for (let b=0; b < 3; b++) {
|
||||
for (let c=0; c < 3; c++) {
|
||||
positions[9 * a + 3 * b + c] = obj.verts[3 * obj.facets[3 * obj.facesToFacets[f][a] + b ] + c ];
|
||||
}
|
||||
}
|
||||
}
|
||||
assignMesh( positions, facecolor, data.objects[obj].opacity );
|
||||
assignMesh(positions, facecolor, obj.opacity, faces);
|
||||
}
|
||||
} else { // No facesToFacets means that there was a tessellate() mismatch inside FreeCAD. Use all facets in object create this mesh
|
||||
positions = new Float32Array( data.objects[obj].facets.length * 3 );
|
||||
for ( var a=0; a<data.objects[obj].facets.length; a++ ) {
|
||||
for ( var b=0; b<3; b++ ) {
|
||||
positions[ 3*a + b ] = data.objects[obj].verts[ 3*data.objects[obj].facets[a] + b ];
|
||||
} else {
|
||||
// No facesToFacets means that there was a tessellate()
|
||||
// mismatch inside FreeCAD. Use all facets in object to
|
||||
// create this mesh
|
||||
const positions = new Float32Array(obj.facets.length * 3);
|
||||
for (let a=0; a < obj.facets.length; a++) {
|
||||
for (let b=0; b < 3; b++) {
|
||||
positions[3 * a + b] = obj.verts[3 * obj.facets[a] + b];
|
||||
}
|
||||
}
|
||||
assignMesh( positions, data.objects[obj].color, data.objects[obj].opacity );
|
||||
assignMesh(positions, obj.color, obj.opacity, faces);
|
||||
}
|
||||
|
||||
|
||||
// Wires
|
||||
// cannot have lines in WebGL that are wider than 1px due to browser limitations so Line2 workaround lib is used
|
||||
// REF: https://threejs.org/examples/?q=fat#webgl_lines_fat - https://jsfiddle.net/brLk6aud/1/
|
||||
var wirematerial = new LineMaterial( {
|
||||
color: new THREE.Color('rgb(0,0,0)'),
|
||||
linewidth: 2, // in pixels
|
||||
// This material is shared by all wires in this object
|
||||
const wirematerial = new LineMaterial( {
|
||||
color: defaultWireColor,
|
||||
linewidth: defaultWireLineWidth,
|
||||
dashed: false, dashSize: 1, gapSize: 1, dashScale: 3
|
||||
} );
|
||||
wirematerial.resolution.set( window.innerWidth, window.innerHeight );
|
||||
|
||||
var wires = [];
|
||||
for ( var w=0; w<data.objects[obj].wires.length; w++ ) {
|
||||
var wiregeometry = new LineGeometry();
|
||||
wiregeometry.setPositions( data.objects[obj].wires[w] );
|
||||
var wire = new Line2( wiregeometry, wirematerial );
|
||||
wirematerial.resolution.set(
|
||||
canvas.clientWidth * window.devicePixelRatio,
|
||||
canvas.clientHeight * window.devicePixelRatio);
|
||||
|
||||
const wires = [];
|
||||
for (const w of obj.wires) {
|
||||
const wiregeometry = new LineGeometry();
|
||||
wiregeometry.setPositions(w);
|
||||
const wire = new Line2(wiregeometry, wirematerial);
|
||||
wire.computeLineDistances();
|
||||
wire.scale.set( 1, 1, 1 );
|
||||
wire.scale.set(1, 1, 1);
|
||||
wire.name = wire.uuid;
|
||||
scene.add( wire );
|
||||
wires.push( wire.name );
|
||||
scene.add(wire);
|
||||
wires.push(wire.name);
|
||||
}
|
||||
objects.push( { name: data.objects[obj].name, faces: faces, wires: wires } );
|
||||
objects.push({
|
||||
data: obj,
|
||||
faces: faces,
|
||||
wires: wires,
|
||||
wirematerial: wirematerial
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ---- GUI Init ----
|
||||
const gui = new GUI( { width: 300 } );
|
||||
var guiparams = {
|
||||
const gui = new GUI({ width: 300 });
|
||||
const guiparams = {
|
||||
wiretype: 'Normal',
|
||||
wirewidth: wirematerial.linewidth,
|
||||
wirecolor: '#'+wirematerial.color.getHexString(),
|
||||
wirewidth: defaultWireLineWidth,
|
||||
wirecolor: '#' + defaultWireColor.getHexString(),
|
||||
clippingx: 100,
|
||||
clippingy: 100,
|
||||
clippingz: 100,
|
||||
cameraType: cameraType,
|
||||
navright: function() { navChange( [1,0,0] ); },
|
||||
navtop: function() { navChange( [0,0,1] ); },
|
||||
navfront: function() { navChange( [0,-1,0] ); }
|
||||
navright: function() { navChange([1, 0, 0]); },
|
||||
navtop: function() { navChange([0, 0, 1]); },
|
||||
navfront: function() { navChange([0, -1, 0]); }
|
||||
};
|
||||
|
||||
|
||||
// ---- Wires ----
|
||||
if (!wirematerial.visible) { guiparams.wiretype = 'None'; }
|
||||
if (wirematerial.dashed) { guiparams.wiretype = 'Dashed'; }
|
||||
const wiretypes = { Normal: 'Normal', Dashed: 'Dashed', None: 'None' };
|
||||
|
||||
var wireFolder = gui.addFolder( 'Wire' );
|
||||
wireFolder.add( guiparams, 'wiretype', wiretypes ).name('Wire Display').onChange( wireChange );
|
||||
wireFolder.add( guiparams, 'wirewidth').min(1).max(5).step(1).name('Wire Width').onChange( wireChange );
|
||||
wireFolder.addColor( guiparams, 'wirecolor' ).name('Wire Color').onChange( wireChange );
|
||||
wireFolder.close();
|
||||
|
||||
|
||||
const wireFolder = gui.addFolder('Wire');
|
||||
wireFolder.add(guiparams, 'wiretype', wiretypes).name('Wire Display').onChange(wireChange);
|
||||
wireFolder.add(guiparams, 'wirewidth').min(1).max(5).step(1).name('Wire Width').onChange(wireChange);
|
||||
wireFolder.addColor(guiparams, 'wirecolor').name('Wire Color').onChange(wireChange);
|
||||
|
||||
function wireChange() {
|
||||
for ( var obj=0; obj<objects.length; obj++ ) {
|
||||
if ( objects[obj].wires.length == 0 ) { continue; }
|
||||
var m = scene.getObjectByName( objects[obj].wires[0] ).material; // all wires in obj share mat
|
||||
for (const obj of objects) {
|
||||
const m = obj.wirematerial;
|
||||
if (m.dashed) {
|
||||
if (guiparams.wiretype != 'Dashed') {
|
||||
m.dashed = false;
|
||||
@@ -366,201 +400,253 @@ def getHTMLTemplate():
|
||||
m.color = new THREE.Color(guiparams.wirecolor);
|
||||
m.needsUpdate = true;
|
||||
}
|
||||
requestRender();
|
||||
}
|
||||
wireChange();
|
||||
|
||||
|
||||
// ---- Clipping ----
|
||||
var clippingFolder = gui.addFolder( 'Clipping' );
|
||||
clippingFolder.add( guiparams, 'clippingx').min(0).max(100).step(1).name('X-Axis Clipping').onChange( clippingChange );
|
||||
clippingFolder.add( guiparams, 'clippingy').min(0).max(100).step(1).name('Y-Axis Clipping').onChange( clippingChange );
|
||||
clippingFolder.add( guiparams, 'clippingz').min(0).max(100).step(1).name('Z-Axis Clipping').onChange( clippingChange );
|
||||
clippingFolder.close();
|
||||
|
||||
var clipPlaneX = new THREE.Plane( new THREE.Vector3( -1, 0, 0 ), 0 );
|
||||
var clipPlaneY = new THREE.Plane( new THREE.Vector3( 0, -1, 0 ), 0 );
|
||||
var clipPlaneZ = new THREE.Plane( new THREE.Vector3( 0, 0, -1 ), 0 );
|
||||
const clippingFolder = gui.addFolder('Clipping');
|
||||
clippingFolder.add(guiparams, 'clippingx').min(0).max(100).step(1).name('X-Axis Clipping').onChange(clippingChange);
|
||||
clippingFolder.add(guiparams, 'clippingy').min(0).max(100).step(1).name('Y-Axis Clipping').onChange(clippingChange);
|
||||
clippingFolder.add(guiparams, 'clippingz').min(0).max(100).step(1).name('Z-Axis Clipping').onChange(clippingChange);
|
||||
|
||||
const clipPlaneX = new THREE.Plane(new THREE.Vector3( -1, 0, 0 ), 0);
|
||||
const clipPlaneY = new THREE.Plane(new THREE.Vector3( 0, -1, 0 ), 0);
|
||||
const clipPlaneZ = new THREE.Plane(new THREE.Vector3( 0, 0, -1 ), 0);
|
||||
|
||||
function clippingChange() {
|
||||
if( guiparams.clippingx < 100 || guiparams.clippingy < 100 || guiparams.clippingz < 100 ) {
|
||||
if (guiparams.clippingx < 100 || guiparams.clippingy < 100 || guiparams.clippingz < 100) {
|
||||
if (renderer.clippingPlanes.length == 0) {
|
||||
renderer.clippingPlanes.push( clipPlaneX, clipPlaneY, clipPlaneZ );
|
||||
renderer.clippingPlanes.push(clipPlaneX, clipPlaneY, clipPlaneZ);
|
||||
}
|
||||
}
|
||||
clipPlaneX.constant = (globalMaxMin[0].max - globalMaxMin[0].min) * guiparams.clippingx / 100.0 + globalMaxMin[0].min;
|
||||
clipPlaneY.constant = (globalMaxMin[1].max - globalMaxMin[1].min) * guiparams.clippingy / 100.0 + globalMaxMin[1].min;
|
||||
clipPlaneZ.constant = (globalMaxMin[2].max - globalMaxMin[2].min) * guiparams.clippingz / 100.0 + globalMaxMin[2].min;
|
||||
requestRender();
|
||||
}
|
||||
|
||||
|
||||
// ---- Camera & Navigation ----
|
||||
var camFolder = gui.addFolder( 'Camera' );
|
||||
const camFolder = gui.addFolder('Camera');
|
||||
const cameraTypes = { Perspective: 'Perspective', Orthographic: 'Orthographic' };
|
||||
camFolder.add( guiparams, 'cameraType', cameraTypes ).name('Wire Display').onChange( cameraChange );
|
||||
camFolder.add( guiparams, 'navright' ).name('View Right');
|
||||
camFolder.add( guiparams, 'navtop' ).name('View Top');
|
||||
camFolder.add( guiparams, 'navfront' ).name('View Front');
|
||||
function navChange( v ) {
|
||||
var t = new THREE.Vector3();
|
||||
new THREE.Box3().setFromObject(scene).getSize( t );
|
||||
persControls.object.position.set( v[0]*t.x + camCenter.x, v[1]*t.y + camCenter.y, v[2]*t.z + camCenter.z);
|
||||
camFolder.add(guiparams, 'cameraType', cameraTypes).name('Camera type').onChange(cameraChange);
|
||||
camFolder.add(guiparams, 'navright').name('View Right');
|
||||
camFolder.add(guiparams, 'navtop').name('View Top');
|
||||
camFolder.add(guiparams, 'navfront').name('View Front');
|
||||
|
||||
function navChange(v) {
|
||||
const t = new THREE.Vector3();
|
||||
new THREE.Box3().setFromObject(scene).getSize(t);
|
||||
persControls.object.position.set(
|
||||
v[0] * t.x * 2 + camCenter.x,
|
||||
v[1] * t.y * 2 + camCenter.y,
|
||||
v[2] * t.z * 2 + camCenter.z);
|
||||
persControls.target = camCenter;
|
||||
persControls.update();
|
||||
orthControls.object.position.set( v[0]*t.x + camCenter.x, v[1]*t.y + camCenter.y, v[2]*t.z + camCenter.z);
|
||||
orthControls.object.position.set(
|
||||
v[0] * t.x + camCenter.x,
|
||||
v[1] * t.y + camCenter.y,
|
||||
v[2] * t.z + camCenter.z);
|
||||
orthControls.target = camCenter;
|
||||
orthControls.update();
|
||||
// controls.update() implicitly calls requestRender()
|
||||
}
|
||||
function cameraChange( v ) {
|
||||
|
||||
function cameraChange(v) {
|
||||
cameraType = v;
|
||||
requestRender();
|
||||
}
|
||||
|
||||
var guiObjData = [];
|
||||
var guiObjects = gui.addFolder( 'Objects' );
|
||||
for ( var obj=0; obj<data.objects.length; obj++ ) {
|
||||
guiObjData.push( {index: obj, color: data.objects[obj].color, opacity:data.objects[obj].opacity } );
|
||||
var guiObject = guiObjects.addFolder( objects[obj].name );
|
||||
guiObject.addColor( guiObjData[ guiObjData.length-1 ], 'color' ).name('Color').onChange( GUIObjectChange );
|
||||
guiObject.add( guiObjData[ guiObjData.length-1 ], 'opacity' ).min(0.0).max(1.0).step(0.05).name('Opacity').onChange( GUIObjectChange );
|
||||
|
||||
const guiObjects = gui.addFolder('Objects');
|
||||
for (const obj of objects) {
|
||||
// Ignore objects with no vertices
|
||||
if (obj.data.verts.length > 0) {
|
||||
const guiObjData = {
|
||||
obj: obj, color: obj.data.color, opacity: obj.data.opacity };
|
||||
const guiObject = guiObjects.addFolder(obj.data.name);
|
||||
guiObject.addColor(guiObjData, 'color').name('Color').onChange(GUIObjectChange);
|
||||
guiObject.add(guiObjData, 'opacity').min(0.0).max(1.0).step(0.05).name('Opacity').onChange(GUIObjectChange);
|
||||
}
|
||||
}
|
||||
function GUIObjectChange( v ) {
|
||||
for ( var f=0; f<objects[this.object.index].faces.length; f++ ) {
|
||||
var m = scene.getObjectByName( objects[this.object.index].faces[f] ).material;
|
||||
if (this.property == 'color') { m.color.setStyle( v ); }
|
||||
|
||||
function GUIObjectChange(v) {
|
||||
for (const f of this.object.obj.faces) {
|
||||
const m = scene.getObjectByName(f).material;
|
||||
if (this.property == 'color') {
|
||||
m.color.setStyle(v);
|
||||
}
|
||||
if (this.property == 'opacity') {
|
||||
m.opacity = v;
|
||||
if ( v == 1.0 ) { m.transparent = false; } else { m.transparent = true; }
|
||||
m.transparent = (v != 1.0);
|
||||
}
|
||||
}
|
||||
if (this.property == 'opacity' && objects[ this.object.index ].wires.length > 0) {
|
||||
var w = scene.getObjectByName( objects[ this.object.index ].wires[0] ).material; // all wires in obj share mat
|
||||
w.opacity = v;
|
||||
if ( v == 1.0 ) { w.transparent = false; } else { w.transparent = true; }
|
||||
if (this.property == 'opacity') {
|
||||
const m = this.object.obj.wirematerial;
|
||||
m.opacity = v;
|
||||
m.transparent = (v != 1.0);
|
||||
}
|
||||
requestRender();
|
||||
}
|
||||
|
||||
|
||||
// Make simple orientation arrows and box - REF: http://jsfiddle.net/b97zd1a3/16/
|
||||
var arrowCanvasSize = { x: 150, y: 150 }; // in pixels on the lower left
|
||||
var arrowRenderer = new THREE.WebGLRenderer( { alpha: true } ); // clear
|
||||
arrowRenderer.setClearColor( 0x000000, 0 );
|
||||
arrowRenderer.setSize( arrowCanvasSize.x, arrowCanvasSize.y );
|
||||
|
||||
var arrowCanvas = document.body.appendChild( arrowRenderer.domElement );
|
||||
arrowCanvas.setAttribute('id', 'arrowCanvas');
|
||||
arrowCanvas.style.width = arrowCanvasSize.x;
|
||||
arrowCanvas.style.height = arrowCanvasSize.y;
|
||||
|
||||
var arrowScene = new THREE.Scene();
|
||||
|
||||
var arrowCamera = new THREE.PerspectiveCamera( 50, arrowCanvasSize.x / arrowCanvasSize.y, 1, 500 );
|
||||
const arrowCanvas = document.querySelector('#arrowCanvas');
|
||||
const arrowRenderer = new THREE.WebGLRenderer({
|
||||
alpha: true,
|
||||
canvas: arrowCanvas
|
||||
}); // clear
|
||||
arrowRenderer.setClearColor(0x000000, 0);
|
||||
arrowRenderer.setSize(arrowCanvas.clientWidth * window.devicePixelRatio,
|
||||
arrowCanvas.clientHeight * window.devicePixelRatio,
|
||||
false);
|
||||
|
||||
const arrowScene = new THREE.Scene();
|
||||
|
||||
const arrowCamera = new THREE.PerspectiveCamera(
|
||||
50, arrowCanvas.clientWidth / arrowCanvas.clientHeight, 1, 500 );
|
||||
arrowCamera.up = persCamera.up; // important!
|
||||
|
||||
var arrowPos = new THREE.Vector3( 0,0,0 );
|
||||
arrowScene.add( new THREE.ArrowHelper( new THREE.Vector3( 1,0,0 ), arrowPos, 60, 0x7F2020, 20, 10 ) );
|
||||
arrowScene.add( new THREE.ArrowHelper( new THREE.Vector3( 0,1,0 ), arrowPos, 60, 0x207F20, 20, 10 ) );
|
||||
arrowScene.add( new THREE.ArrowHelper( new THREE.Vector3( 0,0,1 ), arrowPos, 60, 0x20207F, 20, 10 ) );
|
||||
|
||||
const arrowPos = new THREE.Vector3(0, 0, 0);
|
||||
arrowScene.add(new THREE.ArrowHelper(
|
||||
new THREE.Vector3(1, 0, 0), arrowPos, 60, 0x7F2020, 20, 10));
|
||||
arrowScene.add(new THREE.ArrowHelper(
|
||||
new THREE.Vector3(0, 1, 0), arrowPos, 60, 0x207F20, 20, 10));
|
||||
arrowScene.add(new THREE.ArrowHelper(
|
||||
new THREE.Vector3(0, 0, 1), arrowPos, 60, 0x20207F, 20, 10));
|
||||
arrowScene.add(new THREE.Mesh(
|
||||
new THREE.BoxGeometry( 40, 40, 40 ),
|
||||
new THREE.MeshLambertMaterial( { color: 0xaaaaaa, flatShading: false } )
|
||||
new THREE.BoxGeometry(40, 40, 40),
|
||||
new THREE.MeshLambertMaterial(
|
||||
{ color: 0xaaaaaa, flatShading: false })
|
||||
));
|
||||
arrowScene.add(new THREE.HemisphereLight(0xC7E8FF, 0xFFE3B3, 1.2));
|
||||
|
||||
|
||||
// Controls
|
||||
var persControls = new OrbitControls( persCamera, renderer.domElement );
|
||||
const persControls = new OrbitControls(persCamera, renderer.domElement);
|
||||
persControls.target = camCenter; // rotate around center of parts
|
||||
// persControls.enablePan = false;
|
||||
// persControls.enableDamping = true;
|
||||
persControls.update();
|
||||
var orthControls = new OrbitControls( orthCamera, renderer.domElement );
|
||||
const orthControls = new OrbitControls(orthCamera, renderer.domElement);
|
||||
orthControls.target = camCenter; // rotate around center of parts
|
||||
// orthControls.enablePan = false;
|
||||
// orthControls.enableDamping = true;
|
||||
orthControls.update();
|
||||
|
||||
window.addEventListener( 'resize', onWindowResize, false );
|
||||
onWindowResize();
|
||||
|
||||
var stats = new Stats();
|
||||
document.body.appendChild( stats.dom );
|
||||
|
||||
renderer.domElement.addEventListener( 'mousemove', onMouseMove );
|
||||
|
||||
var animate = function () {
|
||||
requestAnimationFrame( animate );
|
||||
|
||||
|
||||
function render() {
|
||||
renderRequested = false;
|
||||
persControls.update();
|
||||
if (cameraType == 'Perspective') {
|
||||
arrowCamera.position.copy( persCamera.position );
|
||||
arrowCamera.position.sub( persControls.target );
|
||||
arrowCamera.position.copy(persCamera.position);
|
||||
arrowCamera.position.sub(persControls.target);
|
||||
}
|
||||
orthControls.update();
|
||||
if (cameraType == 'Orthographic') {
|
||||
arrowCamera.position.copy( orthCamera.position );
|
||||
arrowCamera.position.sub( orthControls.target );
|
||||
arrowCamera.position.copy(orthCamera.position);
|
||||
arrowCamera.position.sub(orthControls.target);
|
||||
}
|
||||
arrowCamera.lookAt( arrowScene.position );
|
||||
arrowCamera.position.setLength( 200 );
|
||||
|
||||
stats.begin();
|
||||
if (cameraType == 'Perspective') { renderer.render( scene, persCamera ); }
|
||||
if (cameraType == 'Orthographic') { renderer.render( scene, orthCamera ); }
|
||||
arrowRenderer.render( arrowScene, arrowCamera );
|
||||
stats.end();
|
||||
arrowCamera.lookAt(arrowScene.position);
|
||||
arrowCamera.position.setLength(200);
|
||||
|
||||
if (cameraType == 'Perspective') {
|
||||
renderer.render(scene, persCamera);
|
||||
}
|
||||
if (cameraType == 'Orthographic') {
|
||||
renderer.render(scene, orthCamera);
|
||||
}
|
||||
arrowRenderer.render(arrowScene, arrowCamera);
|
||||
};
|
||||
animate();
|
||||
|
||||
function onWindowResize() {
|
||||
for ( var obj=0; obj<objects.length; obj++ ) {
|
||||
if (objects[obj].wires.length > 0) {
|
||||
var w = scene.getObjectByName( objects[obj].wires[0] ).material.resolution.set( window.innerWidth, window.innerHeight ); // all wires in obj share mat
|
||||
}
|
||||
|
||||
function requestRender() {
|
||||
if (!renderRequested) {
|
||||
renderRequested = true;
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
// Ortho camera needs updating. REF: https://stackoverflow.com/questions/39373113/three-js-resize-window-not-scaling-properly
|
||||
var aspect = window.innerWidth / window.innerHeight;
|
||||
var change = originalAspect / aspect;
|
||||
var newSize = viewSize * change;
|
||||
orthCamera.left = -aspect * newSize / 2;
|
||||
orthCamera.right = aspect * newSize / 2;
|
||||
orthCamera.top = newSize / 2;
|
||||
orthCamera.bottom = -newSize / 2;
|
||||
orthCamera.updateProjectionMatrix();
|
||||
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
function onMouseMove( e ) {
|
||||
var c = false;
|
||||
if (cameraType == 'Orthographic') { c = orthCamera;}
|
||||
if (cameraType == 'Perspective') { c = persCamera;}
|
||||
if (!c) { return; }
|
||||
|
||||
var raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera( new THREE.Vector2(
|
||||
( e.clientX / window.innerWidth ) * 2 - 1,
|
||||
-( e.clientY / window.innerHeight ) * 2 + 1
|
||||
), c );
|
||||
var intersects = raycaster.intersectObjects( raycasterObj );
|
||||
|
||||
var chosen = '';
|
||||
if ( intersects.length > 0 ) {
|
||||
for ( var i=0; i<intersects.length; i++ ) {
|
||||
var m = intersects[i].object.material;
|
||||
if (m.opacity > 0) {
|
||||
if (m.emissive.getHex() == 0x000000) {
|
||||
m.emissive.setHex( 0x777777 );
|
||||
m.needsUpdate = true;
|
||||
}
|
||||
chosen = intersects[i].object.name;
|
||||
break;
|
||||
|
||||
persControls.addEventListener('change', requestRender);
|
||||
orthControls.addEventListener('change', requestRender);
|
||||
renderer.domElement.addEventListener('mousemove', onMouseMove);
|
||||
window.addEventListener('resize', onMainCanvasResize, false);
|
||||
|
||||
onMainCanvasResize();
|
||||
requestRender();
|
||||
|
||||
function onMainCanvasResize() {
|
||||
const pixelRatio = window.devicePixelRatio;
|
||||
const width = canvas.clientWidth * pixelRatio | 0;
|
||||
const height = canvas.clientHeight * pixelRatio | 0;
|
||||
const needResize = canvas.width !== width || canvas.height !== height;
|
||||
const aspect = canvas.clientWidth / canvas.clientHeight;
|
||||
if (needResize) {
|
||||
renderer.setSize(width, height, false);
|
||||
|
||||
// See https://stackoverflow.com/questions/39373113/three-js-resize-window-not-scaling-properly
|
||||
const change = originalAspect / aspect;
|
||||
const newSize = viewSize * change;
|
||||
orthCamera.left = -aspect * newSize / 2;
|
||||
orthCamera.right = aspect * newSize / 2;
|
||||
orthCamera.top = newSize / 2;
|
||||
orthCamera.bottom = -newSize / 2;
|
||||
orthCamera.updateProjectionMatrix();
|
||||
|
||||
persCamera.aspect = canvas.clientWidth / canvas.clientHeight;
|
||||
persCamera.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
for (const obj of objects) {
|
||||
obj.wirematerial.resolution.set(width, height);
|
||||
}
|
||||
requestRender();
|
||||
}
|
||||
|
||||
// XXX use mouse click to toggle the gui for the selected object?
|
||||
|
||||
function onMouseMove(e) {
|
||||
let c = false;
|
||||
if (cameraType == 'Orthographic') {
|
||||
c = orthCamera;
|
||||
}
|
||||
if (cameraType == 'Perspective') {
|
||||
c = persCamera;
|
||||
}
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(new THREE.Vector2(
|
||||
(e.clientX / canvas.clientWidth) * 2 - 1,
|
||||
-(e.clientY / canvas.clientHeight) * 2 + 1),
|
||||
c);
|
||||
const intersects = raycaster.intersectObjects(raycasterObj);
|
||||
|
||||
let chosen = '';
|
||||
for (const i of intersects) {
|
||||
const m = i.object.material;
|
||||
if (m.opacity > 0) {
|
||||
if (m.emissive.getHex() == 0x000000) {
|
||||
m.emissive.setHex( 0x777777 );
|
||||
m.needsUpdate = true;
|
||||
requestRender();
|
||||
}
|
||||
chosen = i.object.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for ( var r=0; r<raycasterObj.length; r++ ) {
|
||||
if (raycasterObj[r].name == chosen) { continue; }
|
||||
if (raycasterObj[r].material.emissive.getHex() != 0x000000) {
|
||||
raycasterObj[r].material.emissive.setHex( 0x000000 );
|
||||
raycasterObj[r].material.needsUpdate = true;
|
||||
for (const r of raycasterObj) {
|
||||
if (r.name == chosen) {
|
||||
continue;
|
||||
}
|
||||
if (r.material.emissive.getHex() != 0x000000) {
|
||||
r.material.emissive.setHex(0x000000);
|
||||
r.material.needsUpdate = true;
|
||||
requestRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
data = false; // free up some ram
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
""")
|
||||
|
||||
def export( exportList, filename, colors = None, camera = None ):
|
||||
"""Exports objects to an html file"""
|
||||
@@ -772,11 +858,10 @@ def export( exportList, filename, colors = None, camera = None ):
|
||||
html = html.replace('$version',version[0] + '.' + version[1] + '.' + version[2])
|
||||
|
||||
# Remove data compression in JS
|
||||
if disableCompression: html = html.replace('$disableCompression','true')
|
||||
else: html = html.replace('$disableCompression','false')
|
||||
data['compressed'] = not disableCompression
|
||||
data['base'] = base
|
||||
data['baseFloat'] = baseFloat
|
||||
|
||||
html = html.replace('$base', base)
|
||||
html = html.replace('$float', baseFloat)
|
||||
html = html.replace('$data', json.dumps(data, separators=(',', ':')) ) # Shape Data
|
||||
|
||||
if six.PY2:
|
||||
|
||||
Reference in New Issue
Block a user