mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-16 17:31:24 +01:00
3D Scene system upgrade. (#956)
* 3d view system upgrade. * version fix * Versioning fixed. * Subdivision Added * Subdivision added. Removed toggle wireframe as an option. Reverted to previous rendering method. * Update obj export because new threejs version. * Clean up of unrequired code. * Multiple fixes to 3D view upgrade PR. * Remove unused code.(for3DRender)
This commit is contained in:
parent
c398bc64d6
commit
3d8aa7c3ca
9 changed files with 348 additions and 1042 deletions
35
index.html
35
index.html
|
|
@ -5666,6 +5666,19 @@
|
|||
<input id="options3dLightnessNumber" type="number" min="0" max="500" style="width: 4em" />
|
||||
</div>
|
||||
|
||||
<div data-tip="Set mesh texture resolution">
|
||||
<div>Texture resolution:</div>
|
||||
<select id="options3dMeshSkinResolution" style="width: 10em">
|
||||
<option value="512">512x512px</option>
|
||||
<option value="1024">1024x1024px</option>
|
||||
<option value="2048">2048x2048px</option>
|
||||
<option value="4096">4096x4096px</option>
|
||||
<option value="8192">8192x8192px</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div data-tip="Set sun position (x, y, z) to define shadows">
|
||||
<div>Sun position:</div>
|
||||
<input id="options3dSunX" type="number" min="-2500" max="2500" step="100" style="width: 4.7em" />
|
||||
|
|
@ -5673,6 +5686,8 @@
|
|||
<input id="options3dSunZ" type="number" min="-1500" max="1500" step="100" style="width: 4.7em" />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div data-tip="Toggle 3d labels" style="margin: 0.6em 0 0.3em -0.2em">
|
||||
<input id="options3dMeshLabels3d" class="checkbox" type="checkbox" />
|
||||
<label for="options3dMeshLabels3d" class="checkbox-label"><i>Show 3D labels</i></label>
|
||||
|
|
@ -5683,6 +5698,20 @@
|
|||
<label for="options3dMeshSkyMode" class="checkbox-label"><i>Show sky and extend water</i></label>
|
||||
</div>
|
||||
|
||||
<div data-tip="Toggle 3d subdivision. Increases the polygon count. Opening this will take some time. WARNING: Can smooth the sharp points in progress." style="margin: 0.6em 0 0.3em -0.2em">
|
||||
<input id="options3dSubdivide" class="checkbox" type="checkbox" />
|
||||
<label for="options3dSubdivide" class="checkbox-label"><i>Smooth Geometry.</i></label>
|
||||
</div>
|
||||
|
||||
<div data-tip="Set Sun Color" id="options3dSunColorSection">
|
||||
<span>Sun Color:</span
|
||||
><input
|
||||
id="options3dSunColor"
|
||||
type="color"
|
||||
style="width: 4.4em; height: 1em; border: 0; padding: 0; margin: 0 0.2em"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-tip="Set sky and water color" id="options3dColorSection" style="display: none">
|
||||
<span>Sky:</span
|
||||
><input
|
||||
|
|
@ -7899,7 +7928,7 @@
|
|||
<script src="modules/ui/stylePresets.js?v=1.89.11"></script>
|
||||
|
||||
<script src="modules/ui/general.js?v=1.87.03"></script>
|
||||
<script src="modules/ui/options.js?v=1.89.19"></script>
|
||||
<script src="modules/ui/options.js?v=1.89.36"></script>
|
||||
<script src="main.js?v=1.89.32"></script>
|
||||
|
||||
<script defer src="modules/relief-icons.js"></script>
|
||||
|
|
@ -7935,7 +7964,7 @@
|
|||
<script defer src="modules/ui/battle-screen.js"></script>
|
||||
<script defer src="modules/ui/emblems-editor.js?v=1.89.21"></script>
|
||||
<script defer src="modules/ui/markers-editor.js"></script>
|
||||
<script defer src="modules/ui/3d.js"></script>
|
||||
<script defer src="modules/ui/3d.js?v=1.89.36"></script>
|
||||
<script defer src="modules/ui/submap.js"></script>
|
||||
<script defer src="modules/ui/hotkeys.js?v=1.88.00"></script>
|
||||
<script defer src="modules/coa-renderer.js?v=1.87.08"></script>
|
||||
|
|
@ -7945,7 +7974,7 @@
|
|||
<script defer src="modules/io/save.js?v=1.89.29"></script>
|
||||
<script defer src="modules/io/load.js?v=1.89.30"></script>
|
||||
<script defer src="modules/io/cloud.js"></script>
|
||||
<script defer src="modules/io/export.js?v=1.89.17"></script>
|
||||
<script defer src="modules/io/export.js?v=1.89.36"></script>
|
||||
<script defer src="modules/io/formats.js"></script>
|
||||
|
||||
<!-- Web Components -->
|
||||
|
|
|
|||
132
libs/loopsubdivison.min.js
vendored
Normal file
132
libs/loopsubdivison.min.js
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* @description Loop Subdivision Surface
|
||||
* @about Smooth subdivision surface modifier for use with three.js BufferGeometry.
|
||||
* @author Stephens Nunnally <@stevinz>
|
||||
* @license MIT - Copyright (c) 2022 Stephens Nunnally
|
||||
* @source https://github.com/stevinz/three-subdivide
|
||||
*/
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Functions
|
||||
// modify Applies Loop subdivision to BufferGeometry, returns new BufferGeometry
|
||||
// edgeSplit Splits all triangles at edges shared by coplanar triangles
|
||||
// flat One iteration of Loop subdivision, without point averaging
|
||||
// smooth One iteration of Loop subdivision, with point averaging
|
||||
//
|
||||
// Info
|
||||
// This modifier uses the Loop (Charles Loop, 1987) subdivision surface algorithm to smooth
|
||||
// modern three.js BufferGeometry.
|
||||
//
|
||||
// At one point, three.js included a subdivision surface modifier in the extended examples (see bottom
|
||||
// of file for links), it was removed in r125. The modifier was originally based on the Catmull-Clark
|
||||
// algorithm, which works best for geometry with convex coplanar n-gon faces. In three.js r60 the modifier
|
||||
// was changed to utilize the Loop algorithm. The Loop algorithm was designed to work better with triangle
|
||||
// based meshes.
|
||||
//
|
||||
// The Loop algorithm, however, doesn't always provide uniform results as the vertices are
|
||||
// skewed toward the most used vertex positions. A triangle based box (e.g. BoxGeometry for example) will
|
||||
// tend to favor the corners. To alleviate this issue, this implementation includes an initial pass to split
|
||||
// coplanar faces at their shared edges. It starts by splitting along the longest shared edge first, and then
|
||||
// from that midpoint it splits to any remaining coplanar shared edges.
|
||||
//
|
||||
// Also by default, this implementation inserts new uv coordinates, but does not average them using the Loop
|
||||
// algorithm. In some cases (often in flat geometries) this will produce undesired results, a
|
||||
// noticeable tearing will occur. In such cases, try passing 'uvSmooth' as true to enable uv averaging.
|
||||
//
|
||||
// Note(s)
|
||||
// - This modifier returns a new BufferGeometry instance, it does not dispose() of the old geometry.
|
||||
//
|
||||
// - This modifier returns a NonIndexed geometry. An Indexed geometry can be created by using the
|
||||
// BufferGeometryUtils.mergeVertices() function, see:
|
||||
// https://threejs.org/docs/?q=buffer#examples/en/utils/BufferGeometryUtils.mergeVertices
|
||||
//
|
||||
// - This modifier works best with geometry whose triangles share edges AND edge vertices. See diagram below.
|
||||
//
|
||||
// OKAY NOT OKAY
|
||||
// O O
|
||||
// /|\ / \
|
||||
// / | \ / \
|
||||
// / | \ / \
|
||||
// O---O---O O---O---O
|
||||
// \ | / \ | /
|
||||
// \ | / \ | /
|
||||
// \|/ \|/
|
||||
// O O
|
||||
//
|
||||
// Reference(s)
|
||||
// - Subdivision Surfaces
|
||||
// https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/thesis-10.pdf
|
||||
// https://en.wikipedia.org/wiki/Loop_subdivision_surface
|
||||
// https://cseweb.ucsd.edu/~alchern/teaching/cse167_fa21/6-3Surfaces.pdf
|
||||
//
|
||||
// - Original three.js SubdivisionModifier, r124 (Loop)
|
||||
// https://github.com/mrdoob/three.js/blob/r124/examples/jsm/modifiers/SubdivisionModifier.js
|
||||
//
|
||||
// - Original three.js SubdivisionModifier, r59 (Catmull-Clark)
|
||||
// https://github.com/mrdoob/three.js/blob/r59/examples/js/modifiers/SubdivisionModifier.js
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @description Loop Subdivision Surface
|
||||
* @about Smooth subdivision surface modifier for use with three.js BufferGeometry.
|
||||
* @author Stephens Nunnally <@stevinz>
|
||||
* @license MIT - Copyright (c) 2022 Stephens Nunnally
|
||||
* @source https://github.com/stevinz/three-subdivide
|
||||
*/
|
||||
|
||||
window.loopSubdivision={};(()=>{const POSITION_DECIMALS=2;const _average=new THREE.Vector3();const _center=new THREE.Vector3();const _midpoint=new THREE.Vector3();const _normal=new THREE.Vector3();const _temp=new THREE.Vector3();const _vector0=new THREE.Vector3();const _vector1=new THREE.Vector3();const _vector2=new THREE.Vector3();const _vec0to1=new THREE.Vector3();const _vec1to2=new THREE.Vector3();const _vec2to0=new THREE.Vector3();const _position=[new THREE.Vector3(),new THREE.Vector3(),new THREE.Vector3(),];const _vertex=[new THREE.Vector3(),new THREE.Vector3(),new THREE.Vector3(),];const _triangle=new THREE.Triangle();function modify(bufferGeometry,iterations=1,params={}){if(arguments.length>3)console.warn(`modify() now uses a parameter object. See readme for more info!`);if(typeof params!=='object')params={};if(params.split===undefined)params.split=!0;if(params.uvSmooth===undefined)params.uvSmooth=!1;if(params.preserveEdges===undefined)params.preserveEdges=!1;if(params.flatOnly===undefined)params.flatOnly=!1;if(params.maxTriangles===undefined)params.maxTriangles=Infinity;if(!verifyGeometry(bufferGeometry))return bufferGeometry;let modifiedGeometry=bufferGeometry.clone();if(params.split){const splitGeometry=edgeSplit(modifiedGeometry)
|
||||
modifiedGeometry.dispose();modifiedGeometry=splitGeometry}
|
||||
for(let i=0;i<iterations;i++){let currentTriangles=modifiedGeometry.attributes.position.count/3;if(currentTriangles<params.maxTriangles){let subdividedGeometry;if(params.flatOnly){subdividedGeometry=flat(modifiedGeometry)}else{subdividedGeometry=smooth(modifiedGeometry,params)}
|
||||
modifiedGeometry.groups.forEach((group)=>{subdividedGeometry.addGroup(group.start*4,group.count*4,group.materialIndex)});modifiedGeometry.dispose();modifiedGeometry=subdividedGeometry}}
|
||||
return modifiedGeometry}
|
||||
window.loopSubdivision.modify=modify;function edgeSplit(geometry){if(!verifyGeometry(geometry))return geometry;const existing=(geometry.index!==null)?geometry.toNonIndexed():geometry.clone();const split=new THREE.BufferGeometry();const attributeList=gatherAttributes(existing);const vertexCount=existing.attributes.position.count;const posAttribute=existing.getAttribute('position');const norAttribute=existing.getAttribute('normal');const edgeHashToTriangle={};const triangleEdgeHashes=[];const edgeLength={};const triangleExist=[];for(let i=0;i<vertexCount;i+=3){_vector0.fromBufferAttribute(posAttribute,i+0);_vector1.fromBufferAttribute(posAttribute,i+1);_vector2.fromBufferAttribute(posAttribute,i+2);_normal.fromBufferAttribute(norAttribute,i);const vecHash0=hashFromVector(_vector0);const vecHash1=hashFromVector(_vector1);const vecHash2=hashFromVector(_vector2);const triangleSize=_triangle.set(_vector0,_vector1,_vector2).getArea();triangleExist.push(!fuzzy(triangleSize,0));if(!triangleExist[i/3]){triangleEdgeHashes.push([]);continue}
|
||||
calcNormal(_normal,_vector0,_vector1,_vector2);const normalHash=hashFromVector(_normal);const hashes=[`${vecHash0}_${vecHash1}_${normalHash}`,`${vecHash1}_${vecHash0}_${normalHash}`,`${vecHash1}_${vecHash2}_${normalHash}`,`${vecHash2}_${vecHash1}_${normalHash}`,`${vecHash2}_${vecHash0}_${normalHash}`,`${vecHash0}_${vecHash2}_${normalHash}`,];const index=i/3;for(let j=0;j<hashes.length;j++){if(!edgeHashToTriangle[hashes[j]])edgeHashToTriangle[hashes[j]]=[];edgeHashToTriangle[hashes[j]].push(index);if(!edgeLength[hashes[j]]){if(j===0||j===1)edgeLength[hashes[j]]=_vector0.distanceTo(_vector1);if(j===2||j===3)edgeLength[hashes[j]]=_vector1.distanceTo(_vector2);if(j===4||j===5)edgeLength[hashes[j]]=_vector2.distanceTo(_vector0)}}
|
||||
triangleEdgeHashes.push([hashes[0],hashes[2],hashes[4]])}
|
||||
attributeList.forEach((attributeName)=>{const attribute=existing.getAttribute(attributeName);if(!attribute)return;const floatArray=splitAttribute(attribute,attributeName);split.setAttribute(attributeName,new THREE.BufferAttribute(floatArray,attribute.itemSize))});const morphAttributes=existing.morphAttributes;for(const attributeName in morphAttributes){const array=[];const morphAttribute=morphAttributes[attributeName];for(let i=0,l=morphAttribute.length;i<l;i++){if(morphAttribute[i].count!==vertexCount)continue;const floatArray=splitAttribute(morphAttribute[i],attributeName,!0);array.push(new THREE.BufferAttribute(floatArray,morphAttribute[i].itemSize))}
|
||||
split.morphAttributes[attributeName]=array}
|
||||
split.morphTargetsRelative=existing.morphTargetsRelative;existing.dispose();return split;function splitAttribute(attribute,attributeName,morph=!1){const newTriangles=4;const arrayLength=(vertexCount*attribute.itemSize)*newTriangles;const floatArray=new attribute.array.constructor(arrayLength);const processGroups=(attributeName==='position'&&!morph&&existing.groups.length>0);let groupStart=undefined,groupMaterial=undefined;let index=0;let skipped=0;let step=attribute.itemSize;for(let i=0;i<vertexCount;i+=3){if(!triangleExist[i/3]){skipped+=3;continue}
|
||||
_vector0.fromBufferAttribute(attribute,i+0);_vector1.fromBufferAttribute(attribute,i+1);_vector2.fromBufferAttribute(attribute,i+2);const existingIndex=i/3;const edgeHash0to1=triangleEdgeHashes[existingIndex][0];const edgeHash1to2=triangleEdgeHashes[existingIndex][1];const edgeHash2to0=triangleEdgeHashes[existingIndex][2];const edgeCount0to1=edgeHashToTriangle[edgeHash0to1].length;const edgeCount1to2=edgeHashToTriangle[edgeHash1to2].length;const edgeCount2to0=edgeHashToTriangle[edgeHash2to0].length;const sharedCount=(edgeCount0to1+edgeCount1to2+edgeCount2to0)-3;const loopStartIndex=((index*3)/step)/3;if(sharedCount===0){setTriangle(floatArray,index,step,_vector0,_vector1,_vector2);index+=(step*3)}else{const length0to1=edgeLength[edgeHash0to1];const length1to2=edgeLength[edgeHash1to2];const length2to0=edgeLength[edgeHash2to0];if((length0to1>length1to2||edgeCount1to2<=1)&&(length0to1>length2to0||edgeCount2to0<=1)&&edgeCount0to1>1){_center.copy(_vector0).add(_vector1).divideScalar(2.0);if(edgeCount2to0>1){_midpoint.copy(_vector2).add(_vector0).divideScalar(2.0);setTriangle(floatArray,index,step,_vector0,_center,_midpoint);index+=(step*3);setTriangle(floatArray,index,step,_center,_vector2,_midpoint);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector0,_center,_vector2);index+=(step*3)}
|
||||
if(edgeCount1to2>1){_midpoint.copy(_vector1).add(_vector2).divideScalar(2.0);setTriangle(floatArray,index,step,_center,_vector1,_midpoint);index+=(step*3);setTriangle(floatArray,index,step,_midpoint,_vector2,_center);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector1,_vector2,_center);index+=(step*3)}}else if((length1to2>length2to0||edgeCount2to0<=1)&&edgeCount1to2>1){_center.copy(_vector1).add(_vector2).divideScalar(2.0);if(edgeCount0to1>1){_midpoint.copy(_vector0).add(_vector1).divideScalar(2.0);setTriangle(floatArray,index,step,_center,_midpoint,_vector1);index+=(step*3);setTriangle(floatArray,index,step,_midpoint,_center,_vector0);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector1,_center,_vector0);index+=(step*3)}
|
||||
if(edgeCount2to0>1){_midpoint.copy(_vector2).add(_vector0).divideScalar(2.0);setTriangle(floatArray,index,step,_center,_vector2,_midpoint);index+=(step*3);setTriangle(floatArray,index,step,_midpoint,_vector0,_center);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector2,_vector0,_center);index+=(step*3)}}else if(edgeCount2to0>1){_center.copy(_vector2).add(_vector0).divideScalar(2.0);if(edgeCount1to2>1){_midpoint.copy(_vector1).add(_vector2).divideScalar(2.0);setTriangle(floatArray,index,step,_vector2,_center,_midpoint);index+=(step*3);setTriangle(floatArray,index,step,_center,_vector1,_midpoint);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector2,_center,_vector1);index+=(step*3)}
|
||||
if(edgeCount0to1>1){_midpoint.copy(_vector0).add(_vector1).divideScalar(2.0);setTriangle(floatArray,index,step,_vector0,_midpoint,_center);index+=(step*3);setTriangle(floatArray,index,step,_midpoint,_vector1,_center);index+=(step*3)}else{setTriangle(floatArray,index,step,_vector0,_vector1,_center);index+=(step*3)}}else{setTriangle(floatArray,index,step,_vector0,_vector1,_vector2);index+=(step*3)}}
|
||||
if(processGroups){existing.groups.forEach((group)=>{if(group.start===(i-skipped)){if(groupStart!==undefined&&groupMaterial!==undefined){split.addGroup(groupStart,loopStartIndex-groupStart,groupMaterial)}
|
||||
groupStart=loopStartIndex;groupMaterial=group.materialIndex}})}
|
||||
skipped=0}
|
||||
const reducedCount=(index*3)/step;const reducedArray=new attribute.array.constructor(reducedCount);for(let i=0;i<reducedCount;i++){reducedArray[i]=floatArray[i]}
|
||||
if(processGroups&&groupStart!==undefined&&groupMaterial!==undefined){split.addGroup(groupStart,(((index*3)/step)/3)-groupStart,groupMaterial)}
|
||||
return reducedArray}}
|
||||
function flat(geometry){if(!verifyGeometry(geometry))return geometry;const existing=(geometry.index!==null)?geometry.toNonIndexed():geometry.clone();const loop=new THREE.BufferGeometry();const attributeList=gatherAttributes(existing);const vertexCount=existing.attributes.position.count;attributeList.forEach((attributeName)=>{const attribute=existing.getAttribute(attributeName);if(!attribute)return;loop.setAttribute(attributeName,flatAttribute(attribute,vertexCount))});const morphAttributes=existing.morphAttributes;for(const attributeName in morphAttributes){const array=[];const morphAttribute=morphAttributes[attributeName];for(let i=0,l=morphAttribute.length;i<l;i++){if(morphAttribute[i].count!==vertexCount)continue;array.push(flatAttribute(morphAttribute[i],vertexCount))}
|
||||
loop.morphAttributes[attributeName]=array}
|
||||
loop.morphTargetsRelative=existing.morphTargetsRelative;existing.dispose();return loop}
|
||||
function flatAttribute(attribute,vertexCount){const newTriangles=4;const arrayLength=(vertexCount*attribute.itemSize)*newTriangles;const floatArray=new attribute.array.constructor(arrayLength);let index=0;let step=attribute.itemSize;for(let i=0;i<vertexCount;i+=3){_vector0.fromBufferAttribute(attribute,i+0);_vector1.fromBufferAttribute(attribute,i+1);_vector2.fromBufferAttribute(attribute,i+2);_vec0to1.copy(_vector0).add(_vector1).divideScalar(2.0);_vec1to2.copy(_vector1).add(_vector2).divideScalar(2.0);_vec2to0.copy(_vector2).add(_vector0).divideScalar(2.0);setTriangle(floatArray,index,step,_vector0,_vec0to1,_vec2to0);index+=(step*3);setTriangle(floatArray,index,step,_vector1,_vec1to2,_vec0to1);index+=(step*3);setTriangle(floatArray,index,step,_vector2,_vec2to0,_vec1to2);index+=(step*3);setTriangle(floatArray,index,step,_vec0to1,_vec1to2,_vec2to0);index+=(step*3)}
|
||||
return new THREE.BufferAttribute(floatArray,attribute.itemSize)}
|
||||
function smooth(geometry,params={}){if(typeof params!=='object')params={};if(params.uvSmooth===undefined)params.uvSmooth=!1;if(params.preserveEdges===undefined)params.preserveEdges=!1;if(!verifyGeometry(geometry))return geometry;const existing=(geometry.index!==null)?geometry.toNonIndexed():geometry.clone();const flatGeometry=flat(existing);const loop=new THREE.BufferGeometry();const attributeList=gatherAttributes(existing);const vertexCount=existing.attributes.position.count;const posAttribute=existing.getAttribute('position');const flatPosition=flatGeometry.getAttribute('position');const hashToIndex={};const existingNeighbors={};const flatOpposites={};const existingEdges={};function addNeighbor(posHash,neighborHash,index){if(!existingNeighbors[posHash])existingNeighbors[posHash]={};if(!existingNeighbors[posHash][neighborHash])existingNeighbors[posHash][neighborHash]=[];existingNeighbors[posHash][neighborHash].push(index)}
|
||||
function addOpposite(posHash,index){if(!flatOpposites[posHash])flatOpposites[posHash]=[];flatOpposites[posHash].push(index)}
|
||||
function addEdgePoint(posHash,edgeHash){if(!existingEdges[posHash])existingEdges[posHash]=new Set();existingEdges[posHash].add(edgeHash)}
|
||||
for(let i=0;i<vertexCount;i+=3){const posHash0=hashFromVector(_vertex[0].fromBufferAttribute(posAttribute,i+0));const posHash1=hashFromVector(_vertex[1].fromBufferAttribute(posAttribute,i+1));const posHash2=hashFromVector(_vertex[2].fromBufferAttribute(posAttribute,i+2));addNeighbor(posHash0,posHash1,i+1);addNeighbor(posHash0,posHash2,i+2);addNeighbor(posHash1,posHash0,i+0);addNeighbor(posHash1,posHash2,i+2);addNeighbor(posHash2,posHash0,i+0);addNeighbor(posHash2,posHash1,i+1);_vec0to1.copy(_vertex[0]).add(_vertex[1]).divideScalar(2.0);_vec1to2.copy(_vertex[1]).add(_vertex[2]).divideScalar(2.0);_vec2to0.copy(_vertex[2]).add(_vertex[0]).divideScalar(2.0);const hash0to1=hashFromVector(_vec0to1);const hash1to2=hashFromVector(_vec1to2);const hash2to0=hashFromVector(_vec2to0);addOpposite(hash0to1,i+2);addOpposite(hash1to2,i+0);addOpposite(hash2to0,i+1);addEdgePoint(posHash0,hash0to1);addEdgePoint(posHash0,hash2to0);addEdgePoint(posHash1,hash0to1);addEdgePoint(posHash1,hash1to2);addEdgePoint(posHash2,hash1to2);addEdgePoint(posHash2,hash2to0)}
|
||||
for(let i=0;i<flatGeometry.attributes.position.count;i++){const posHash=hashFromVector(_temp.fromBufferAttribute(flatPosition,i));if(!hashToIndex[posHash])hashToIndex[posHash]=[];hashToIndex[posHash].push(i)}
|
||||
attributeList.forEach((attributeName)=>{const existingAttribute=existing.getAttribute(attributeName);const flatAttribute=flatGeometry.getAttribute(attributeName);if(existingAttribute===undefined||flatAttribute===undefined)return;const floatArray=subdivideAttribute(attributeName,existingAttribute,flatAttribute);loop.setAttribute(attributeName,new THREE.BufferAttribute(floatArray,flatAttribute.itemSize))});const morphAttributes=existing.morphAttributes;for(const attributeName in morphAttributes){const array=[];const morphAttribute=morphAttributes[attributeName];for(let i=0,l=morphAttribute.length;i<l;i++){if(morphAttribute[i].count!==vertexCount)continue;const existingAttribute=morphAttribute[i];const flatAttribute=flatAttribute(morphAttribute[i],morphAttribute[i].count)
|
||||
const floatArray=subdivideAttribute(attributeName,existingAttribute,flatAttribute);array.push(new THREE.BufferAttribute(floatArray,flatAttribute.itemSize))}
|
||||
loop.morphAttributes[attributeName]=array}
|
||||
loop.morphTargetsRelative=existing.morphTargetsRelative;flatGeometry.dispose();existing.dispose();return loop;function subdivideAttribute(attributeName,existingAttribute,flatAttribute){const arrayLength=(flatGeometry.attributes.position.count*flatAttribute.itemSize);const floatArray=new existingAttribute.array.constructor(arrayLength);let index=0;for(let i=0;i<flatGeometry.attributes.position.count;i+=3){for(let v=0;v<3;v++){if(attributeName==='uv'&&!params.uvSmooth){_vertex[v].fromBufferAttribute(flatAttribute,i+v)}else if(attributeName==='normal'){_position[v].fromBufferAttribute(flatPosition,i+v);const positionHash=hashFromVector(_position[v]);const positions=hashToIndex[positionHash];const k=Object.keys(positions).length;const beta=0.75/k;const startWeight=1.0-(beta*k);_vertex[v].fromBufferAttribute(flatAttribute,i+v);_vertex[v].multiplyScalar(startWeight);positions.forEach(positionIndex=>{_average.fromBufferAttribute(flatAttribute,positionIndex);_average.multiplyScalar(beta);_vertex[v].add(_average)})}else{_vertex[v].fromBufferAttribute(flatAttribute,i+v);_position[v].fromBufferAttribute(flatPosition,i+v);const positionHash=hashFromVector(_position[v]);const neighbors=existingNeighbors[positionHash];const opposites=flatOpposites[positionHash];if(neighbors){if(params.preserveEdges){const edgeSet=existingEdges[positionHash];let hasPair=!0;for(const edgeHash of edgeSet){if(flatOpposites[edgeHash].length%2!==0)hasPair=!1}
|
||||
if(!hasPair)continue}
|
||||
const k=Object.keys(neighbors).length;const beta=1/k*((5/8)-Math.pow((3/8)+(1/4)*Math.cos(2*Math.PI/k),2));const startWeight=1.0-(beta*k);_vertex[v].multiplyScalar(startWeight);for(let neighborHash in neighbors){const neighborIndices=neighbors[neighborHash];_average.set(0,0,0);for(let j=0;j<neighborIndices.length;j++){_average.add(_temp.fromBufferAttribute(existingAttribute,neighborIndices[j]))}
|
||||
_average.divideScalar(neighborIndices.length);_average.multiplyScalar(beta);_vertex[v].add(_average)}}else if(opposites&&opposites.length===2){const k=opposites.length;const beta=0.125;const startWeight=1.0-(beta*k);_vertex[v].multiplyScalar(startWeight);opposites.forEach(oppositeIndex=>{_average.fromBufferAttribute(existingAttribute,oppositeIndex);_average.multiplyScalar(beta);_vertex[v].add(_average)})}}}
|
||||
setTriangle(floatArray,index,flatAttribute.itemSize,_vertex[0],_vertex[1],_vertex[2]);index+=(flatAttribute.itemSize*3)}
|
||||
return floatArray}}
|
||||
const _positionShift=Math.pow(10,POSITION_DECIMALS);function fuzzy(a,b,tolerance=0.00001){return((a<(b+tolerance))&&(a>(b-tolerance)))}
|
||||
function hashFromNumber(num,shift=_positionShift){let roundedNumber=round(num*shift);if(roundedNumber==0)roundedNumber=0;return `${roundedNumber}`}
|
||||
function hashFromVector(vector,shift=_positionShift){return `${hashFromNumber(vector.x, shift)},${hashFromNumber(vector.y, shift)},${hashFromNumber(vector.z, shift)}`}
|
||||
function round(x){return(x+((x>0)?0.5:-0.5))<<0}
|
||||
function calcNormal(target,vec1,vec2,vec3){_temp.subVectors(vec1,vec2);target.subVectors(vec2,vec3);target.cross(_temp).normalize()}
|
||||
function gatherAttributes(geometry){const desired=['position','normal','uv'];const contains=Object.keys(geometry.attributes);const attributeList=Array.from(new Set(desired.concat(contains)));return attributeList}
|
||||
function setTriangle(positions,index,step,vec0,vec1,vec2){if(step>=1){positions[index+0+(step*0)]=vec0.x;positions[index+0+(step*1)]=vec1.x;positions[index+0+(step*2)]=vec2.x}
|
||||
if(step>=2){positions[index+1+(step*0)]=vec0.y;positions[index+1+(step*1)]=vec1.y;positions[index+1+(step*2)]=vec2.y}
|
||||
if(step>=3){positions[index+2+(step*0)]=vec0.z;positions[index+2+(step*1)]=vec1.z;positions[index+2+(step*2)]=vec2.z}
|
||||
if(step>=4){positions[index+3+(step*0)]=vec0.w;positions[index+3+(step*1)]=vec1.w;positions[index+3+(step*2)]=vec2.w}}
|
||||
function verifyGeometry(geometry){if(geometry===undefined){console.warn(`window.loopSubdivision: Geometry provided is undefined`);return!1}
|
||||
if(!geometry.isBufferGeometry){console.warn(`window.loopSubdivision: Geometry provided is not 'BufferGeometry' type`);return!1}
|
||||
if(geometry.attributes.position===undefined){console.warn(`window.loopSubdivision: Geometry provided missing required 'position' attribute`);return!1}
|
||||
if(geometry.attributes.normal===undefined){geometry.computeVertexNormals()}
|
||||
return!0}})()
|
||||
30
libs/objexporter.min.js
vendored
30
libs/objexporter.min.js
vendored
|
|
@ -1,5 +1,25 @@
|
|||
THREE.OBJExporter=function(){};
|
||||
THREE.OBJExporter.prototype={constructor:THREE.OBJExporter,parse:function(A){var f="",n=0,w=0,x=0,g=new THREE.Vector3,p=new THREE.Vector3,u=new THREE.Vector2,a,b,y,c,k,v=[];A.traverse(function(e){if(e instanceof THREE.Mesh){var r=0,t=0,d=0,h=e.geometry,z=new THREE.Matrix3;h instanceof THREE.Geometry&&(h=(new THREE.BufferGeometry).setFromObject(e));if(h instanceof THREE.BufferGeometry){var q=h.getAttribute("position"),l=h.getAttribute("normal"),m=h.getAttribute("uv");h=h.getIndex();f+="o "+e.name+
|
||||
"\n";e.material&&e.material.name&&(f+="usemtl "+e.material.name+"\n");if(void 0!==q)for(a=0,c=q.count;a<c;a++,r++)g.x=q.getX(a),g.y=q.getY(a),g.z=q.getZ(a),g.applyMatrix4(e.matrixWorld),f+="v "+g.x+" "+g.y+" "+g.z+"\n";if(void 0!==m)for(a=0,c=m.count;a<c;a++,d++)u.x=m.getX(a),u.y=m.getY(a),f+="vt "+u.x+" "+u.y+"\n";if(void 0!==l)for(z.getNormalMatrix(e.matrixWorld),a=0,c=l.count;a<c;a++,t++)p.x=l.getX(a),p.y=l.getY(a),p.z=l.getZ(a),p.applyMatrix3(z).normalize(),f+="vn "+p.x+" "+p.y+" "+p.z+"\n";if(null!==
|
||||
h)for(a=0,c=h.count;a<c;a+=3){for(k=0;3>k;k++)b=h.getX(a+k)+1,v[k]=n+b+(l||m?"/"+(m?w+b:"")+(l?"/"+(x+b):""):"");f+="f "+v.join(" ")+"\n"}else for(a=0,c=q.count;a<c;a+=3){for(k=0;3>k;k++)b=a+k+1,v[k]=n+b+(l||m?"/"+(m?w+b:"")+(l?"/"+(x+b):""):"");f+="f "+v.join(" ")+"\n"}}else console.warn("THREE.OBJExporter.parseMesh(): geometry type unsupported",h);n+=r;w+=d;x+=t}if(e instanceof THREE.Line){r=0;d=e.geometry;t=e.type;d instanceof THREE.Geometry&&(d=(new THREE.BufferGeometry).setFromObject(e));if(d instanceof
|
||||
THREE.BufferGeometry){d=d.getAttribute("position");f+="o "+e.name+"\n";if(void 0!==d)for(a=0,c=d.count;a<c;a++,r++)g.x=d.getX(a),g.y=d.getY(a),g.z=d.getZ(a),g.applyMatrix4(e.matrixWorld),f+="v "+g.x+" "+g.y+" "+g.z+"\n";if("Line"===t){f+="l ";b=1;for(c=d.count;b<=c;b++)f+=n+b+" ";f+="\n"}if("LineSegments"===t)for(b=1,y=b+1,c=d.count;b<c;b+=2,y=b+1)f+="l "+(n+b)+" "+(n+y)+"\n"}else console.warn("THREE.OBJExporter.parseLine(): geometry type unsupported",d);n+=r}});return f}};
|
||||
(function(){class OBJExporter{parse(object){let output='';let indexVertex=0;let indexVertexUvs=0;let indexNormals=0;const vertex=new THREE.Vector3();const color=new THREE.Color();const normal=new THREE.Vector3();const uv=new THREE.Vector2();const face=[];function parseMesh(mesh){let nbVertex=0;let nbNormals=0;let nbVertexUvs=0;const geometry=mesh.geometry;const normalMatrixWorld=new THREE.Matrix3();if(geometry.isBufferGeometry!==!0){throw new Error('THREE.OBJExporter: Geometry is not of type THREE.BufferGeometry.')}
|
||||
const vertices=geometry.getAttribute('position');const normals=geometry.getAttribute('normal');const uvs=geometry.getAttribute('uv');const indices=geometry.getIndex();output+='o '+mesh.name+'\n';if(mesh.material&&mesh.material.name){output+='usemtl '+mesh.material.name+'\n'}
|
||||
if(vertices!==undefined){for(let i=0,l=vertices.count;i<l;i ++,nbVertex ++){vertex.fromBufferAttribute(vertices,i);vertex.applyMatrix4(mesh.matrixWorld);output+='v '+vertex.x+' '+vertex.y+' '+vertex.z+'\n'}}
|
||||
if(uvs!==undefined){for(let i=0,l=uvs.count;i<l;i ++,nbVertexUvs ++){uv.fromBufferAttribute(uvs,i);output+='vt '+uv.x+' '+uv.y+'\n'}}
|
||||
if(normals!==undefined){normalMatrixWorld.getNormalMatrix(mesh.matrixWorld);for(let i=0,l=normals.count;i<l;i ++,nbNormals ++){normal.fromBufferAttribute(normals,i);normal.applyMatrix3(normalMatrixWorld).normalize();output+='vn '+normal.x+' '+normal.y+' '+normal.z+'\n'}}
|
||||
if(indices!==null){for(let i=0,l=indices.count;i<l;i+=3){for(let m=0;m<3;m ++){const j=indices.getX(i+m)+1;face[m]=indexVertex+j+(normals||uvs?'/'+(uvs?indexVertexUvs+j:'')+(normals?'/'+(indexNormals+j):''):'')}
|
||||
output+='f '+face.join(' ')+'\n'}}else{for(let i=0,l=vertices.count;i<l;i+=3){for(let m=0;m<3;m ++){const j=i+m+1;face[m]=indexVertex+j+(normals||uvs?'/'+(uvs?indexVertexUvs+j:'')+(normals?'/'+(indexNormals+j):''):'')}
|
||||
output+='f '+face.join(' ')+'\n'}}
|
||||
indexVertex+=nbVertex;indexVertexUvs+=nbVertexUvs;indexNormals+=nbNormals}
|
||||
function parseLine(line){let nbVertex=0;const geometry=line.geometry;const type=line.type;if(geometry.isBufferGeometry!==!0){throw new Error('THREE.OBJExporter: Geometry is not of type THREE.BufferGeometry.')}
|
||||
const vertices=geometry.getAttribute('position');output+='o '+line.name+'\n';if(vertices!==undefined){for(let i=0,l=vertices.count;i<l;i ++,nbVertex ++){vertex.fromBufferAttribute(vertices,i);vertex.applyMatrix4(line.matrixWorld);output+='v '+vertex.x+' '+vertex.y+' '+vertex.z+'\n'}}
|
||||
if(type==='Line'){output+='l ';for(let j=1,l=vertices.count;j<=l;j ++){output+=indexVertex+j+' '}
|
||||
output+='\n'}
|
||||
if(type==='LineSegments'){for(let j=1,k=j+1,l=vertices.count;j<l;j+=2,k=j+1){output+='l '+(indexVertex+j)+' '+(indexVertex+k)+'\n'}}
|
||||
indexVertex+=nbVertex}
|
||||
function parsePoints(points){let nbVertex=0;const geometry=points.geometry;if(geometry.isBufferGeometry!==!0){throw new Error('THREE.OBJExporter: Geometry is not of type THREE.BufferGeometry.')}
|
||||
const vertices=geometry.getAttribute('position');const colors=geometry.getAttribute('color');output+='o '+points.name+'\n';if(vertices!==undefined){for(let i=0,l=vertices.count;i<l;i ++,nbVertex ++){vertex.fromBufferAttribute(vertices,i);vertex.applyMatrix4(points.matrixWorld);output+='v '+vertex.x+' '+vertex.y+' '+vertex.z;if(colors!==undefined){color.fromBufferAttribute(colors,i).convertLinearToSRGB();output+=' '+color.r+' '+color.g+' '+color.b}
|
||||
output+='\n'}
|
||||
output+='p ';for(let j=1,l=vertices.count;j<=l;j ++){output+=indexVertex+j+' '}
|
||||
output+='\n'}
|
||||
indexVertex+=nbVertex}
|
||||
object.traverse(function(child){if(child.isMesh===!0){parseMesh(child)}
|
||||
if(child.isLine===!0){parseLine(child)}
|
||||
if(child.isPoints===!0){parsePoints(child)}});return output}}
|
||||
THREE.OBJExporter=OBJExporter})()
|
||||
2
libs/orbitControls.min.js
vendored
2
libs/orbitControls.min.js
vendored
File diff suppressed because one or more lines are too long
1016
libs/three.min.js
vendored
1016
libs/three.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -11,7 +11,7 @@ async function saveSVG() {
|
|||
link.click();
|
||||
|
||||
tip(
|
||||
`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`,
|
||||
`${link.download} is saved. Open "Downloads" screen (ctrl + J) to check. You can set image scale in options`,
|
||||
true,
|
||||
"success",
|
||||
5000
|
||||
|
|
|
|||
141
modules/ui/3d.js
141
modules/ui/3d.js
|
|
@ -12,7 +12,11 @@ window.ThreeD = (function () {
|
|||
waterColor: "#466eab",
|
||||
extendedWater: 0,
|
||||
labels3d: 0,
|
||||
resolution: 2
|
||||
wireframe: 0,
|
||||
resolution: 2,
|
||||
resolutionScale: 2048,
|
||||
sunColor: "#cccccc",
|
||||
subdivide: 0
|
||||
};
|
||||
|
||||
// set variables
|
||||
|
|
@ -92,7 +96,11 @@ window.ThreeD = (function () {
|
|||
|
||||
const setScale = function (scale) {
|
||||
options.scale = scale;
|
||||
geometry.vertices.forEach((v, i) => (v.z = getMeshHeight(i)));
|
||||
let vertices = geometry.getAttribute('position');
|
||||
for(let i = 0; i < vertices.count; i++){
|
||||
vertices.setZ(i,getMeshHeight(i));
|
||||
}
|
||||
geometry.setAttribute('position',vertices);
|
||||
geometry.verticesNeedUpdate = true;
|
||||
geometry.computeVertexNormals();
|
||||
geometry.verticesNeedUpdate = false;
|
||||
|
|
@ -100,6 +108,17 @@ window.ThreeD = (function () {
|
|||
redraw();
|
||||
};
|
||||
|
||||
const setSunColor = function(color){
|
||||
options.sunColor = color;
|
||||
spotLight.color = new THREE.Color(color);
|
||||
render();
|
||||
}
|
||||
|
||||
const setResolutionScale = function (scale) {
|
||||
options.resolutionScale = scale;
|
||||
redraw();
|
||||
};
|
||||
|
||||
const setLightness = function (intensity) {
|
||||
options.lightness = intensity;
|
||||
ambientLight.intensity = intensity;
|
||||
|
|
@ -148,6 +167,16 @@ window.ThreeD = (function () {
|
|||
}
|
||||
};
|
||||
|
||||
const toggle3dSubdivision = function(){
|
||||
options.subdivide = !options.subdivide;
|
||||
redraw();
|
||||
}
|
||||
|
||||
const toggleWireframe = function () {
|
||||
options.wireframe = !options.wireframe;
|
||||
redraw();
|
||||
};
|
||||
|
||||
const setColors = function (sky, water) {
|
||||
options.skyColor = sky;
|
||||
scene.background = scene.fog.color = new THREE.Color(sky);
|
||||
|
|
@ -189,16 +218,20 @@ window.ThreeD = (function () {
|
|||
// light
|
||||
ambientLight = new THREE.AmbientLight(0xcccccc, options.lightness);
|
||||
scene.add(ambientLight);
|
||||
spotLight = new THREE.SpotLight(0xcccccc, 0.8, 2000, 0.8, 0, 0);
|
||||
spotLight = new THREE.SpotLight(options.sunColor, 0.8, 2000, 0.8, 0, 0);
|
||||
spotLight.position.set(options.sun.x, options.sun.y, options.sun.z);
|
||||
spotLight.castShadow = true;
|
||||
//maybe add a option for this. But changing the option will require to reinstance the spotLight.
|
||||
spotLight.shadow.mapSize.width = 2048;
|
||||
spotLight.shadow.mapSize.height = 2048;
|
||||
scene.add(spotLight);
|
||||
//scene.add(new THREE.SpotLightHelper(spotLight));
|
||||
|
||||
// Rendered
|
||||
// Renderer
|
||||
Renderer = new THREE.WebGLRenderer({canvas, antialias: true, preserveDrawingBuffer: true});
|
||||
Renderer.setSize(canvas.width, canvas.height);
|
||||
Renderer.shadowMap.enabled = true;
|
||||
// Renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
if (options.extendedWater) extendWater(graphWidth, graphHeight);
|
||||
createMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
||||
|
||||
|
|
@ -223,7 +256,7 @@ window.ThreeD = (function () {
|
|||
|
||||
function textureToSprite(texture, width, height) {
|
||||
const map = new THREE.TextureLoader().load(texture);
|
||||
map.anisotropy = Renderer.getMaxAnisotropy();
|
||||
map.anisotropy = Renderer.capabilities.getMaxAnisotropy();
|
||||
const material = new THREE.SpriteMaterial({map});
|
||||
|
||||
const sprite = new THREE.Sprite(material);
|
||||
|
|
@ -296,7 +329,9 @@ window.ThreeD = (function () {
|
|||
};
|
||||
|
||||
const city_icon_material = new THREE.MeshPhongMaterial({color: cityOptions.iconColor});
|
||||
city_icon_material.wireframe = options.wireframe;
|
||||
const town_icon_material = new THREE.MeshPhongMaterial({color: townOptions.iconColor});
|
||||
town_icon_material.wireframe = options.wireframe;
|
||||
const city_icon_geometry = new THREE.CylinderGeometry(cityOptions.iconSize * 2, cityOptions.iconSize * 2, cityOptions.iconSize, 16, 1);
|
||||
const town_icon_geometry = new THREE.CylinderGeometry(townOptions.iconSize * 2, townOptions.iconSize * 2, townOptions.iconSize, 16, 1);
|
||||
const line_material = new THREE.LineBasicMaterial({color: cityOptions.iconColor});
|
||||
|
|
@ -387,32 +422,81 @@ window.ThreeD = (function () {
|
|||
lines = [];
|
||||
}
|
||||
|
||||
async function createMeshTextureUrl(){
|
||||
return new Promise(async (resolve, reject)=>{
|
||||
const mapOptions = {
|
||||
noLabels: options.labels3d,
|
||||
noWater: options.extendedWater,
|
||||
fullMap: true
|
||||
};
|
||||
const url = await getMapURL("mesh",mapOptions);
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
canvas.width = options.resolutionScale;
|
||||
canvas.height = options.resolutionScale;
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
|
||||
img.onload = function(){
|
||||
ctx.drawImage(img,0,0,canvas.width,canvas.height);
|
||||
canvas.toBlob((blob)=>{
|
||||
const blobObj = window.URL.createObjectURL(blob)
|
||||
window.setTimeout(()=>{
|
||||
canvas.remove();
|
||||
window.URL.revokeObjectURL(blobObj);
|
||||
}, 100);
|
||||
resolve(blobObj);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
// create a mesh from pixel data
|
||||
async function createMesh(width, height, segmentsX, segmentsY) {
|
||||
const mapOptions = {
|
||||
noLabels: options.labels3d,
|
||||
noWater: options.extendedWater,
|
||||
fullMap: true
|
||||
};
|
||||
const url = await getMapURL("mesh", mapOptions);
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(url), 5000);
|
||||
|
||||
if (texture) texture.dispose();
|
||||
texture = new THREE.TextureLoader().load(url, render);
|
||||
if(!options.wireframe){
|
||||
//Try loading skin texture.
|
||||
texture = new THREE.TextureLoader().load(await createMeshTextureUrl(), render);
|
||||
texture.needsUpdate = true;
|
||||
texture.anisotropy = Renderer.capabilities.getMaxAnisotropy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (material) material.dispose();
|
||||
material = new THREE.MeshLambertMaterial();
|
||||
material.map = texture;
|
||||
material.transparent = true;
|
||||
if(options.wireframe){
|
||||
material = new THREE.MeshLambertMaterial();
|
||||
material.wireframe = true;
|
||||
}else{
|
||||
material = new THREE.MeshLambertMaterial();
|
||||
material.map = texture;
|
||||
material.transparent = true;
|
||||
}
|
||||
|
||||
|
||||
if (geometry) geometry.dispose();
|
||||
geometry = new THREE.PlaneGeometry(width, height, segmentsX - 1, segmentsY - 1);
|
||||
geometry.vertices.forEach((v, i) => (v.z = getMeshHeight(i)));
|
||||
let vertices = geometry.getAttribute('position');
|
||||
for(let i = 0; i < vertices.count; i++){
|
||||
vertices.setZ(i,getMeshHeight(i));
|
||||
}
|
||||
geometry.setAttribute('position',vertices);
|
||||
geometry.computeVertexNormals();
|
||||
|
||||
if (mesh) scene.remove(mesh);
|
||||
mesh = new THREE.Mesh(geometry, material);
|
||||
if(options.subdivide){
|
||||
await loadLoopSubdivision();
|
||||
const subdivideParams = {
|
||||
split: true,
|
||||
uvSmooth: false,
|
||||
preserveEdges: true,
|
||||
flatOnly: false,
|
||||
maxTriangles: Infinity
|
||||
};
|
||||
const smoothGeometry = loopSubdivision.modify(geometry,1,subdivideParams);
|
||||
mesh = new THREE.Mesh(smoothGeometry, material);
|
||||
}else{
|
||||
mesh = new THREE.Mesh(geometry, material);
|
||||
}
|
||||
mesh.rotation.x = -Math.PI / 2;
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
|
|
@ -449,7 +533,7 @@ window.ThreeD = (function () {
|
|||
noWater: options.extendedWater,
|
||||
fullMap: true
|
||||
};
|
||||
const url = await getMapURL("mesh", mapOptions);
|
||||
const url = await createMeshTextureUrl();
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(url), 4000);
|
||||
texture = new THREE.TextureLoader().load(url, render);
|
||||
material.map = texture;
|
||||
|
|
@ -579,6 +663,17 @@ window.ThreeD = (function () {
|
|||
});
|
||||
}
|
||||
|
||||
function loadLoopSubdivision(){
|
||||
if (window.loopSubdivision) return Promise.resolve(true);
|
||||
|
||||
return new Promise(resolve => {
|
||||
const script = document.createElement("script");
|
||||
script.src = "libs/loopsubdivison.min.js";
|
||||
document.head.append(script);
|
||||
script.onload = () => resolve(true);
|
||||
script.onerror = () => resolve(false);
|
||||
});
|
||||
}
|
||||
function OrbitControls(camera, domElement) {
|
||||
if (THREE.OrbitControls) return new THREE.OrbitControls(camera, domElement);
|
||||
|
||||
|
|
@ -596,7 +691,7 @@ window.ThreeD = (function () {
|
|||
|
||||
return new Promise(resolve => {
|
||||
const script = document.createElement("script");
|
||||
script.src = "libs/objexporter.min.js";
|
||||
script.src = "libs/objexporter.min.js?v=1.89.35";
|
||||
document.head.append(script);
|
||||
script.onload = () => resolve(new THREE.OBJExporter());
|
||||
script.onerror = () => resolve(false);
|
||||
|
|
@ -609,11 +704,15 @@ window.ThreeD = (function () {
|
|||
update,
|
||||
stop,
|
||||
options,
|
||||
setSunColor,
|
||||
setScale,
|
||||
setResolutionScale,
|
||||
setLightness,
|
||||
setSun,
|
||||
setRotation,
|
||||
toggleLabels,
|
||||
toggle3dSubdivision,
|
||||
toggleWireframe,
|
||||
toggleSky,
|
||||
setResolution,
|
||||
setColors,
|
||||
|
|
|
|||
|
|
@ -1060,6 +1060,7 @@ function toggle3dOptions() {
|
|||
document.getElementById("options3dSunX").addEventListener("change", changeSunPosition);
|
||||
document.getElementById("options3dSunY").addEventListener("change", changeSunPosition);
|
||||
document.getElementById("options3dSunZ").addEventListener("change", changeSunPosition);
|
||||
document.getElementById("options3dMeshSkinResolution").addEventListener("change", changeResolutionScale);
|
||||
document.getElementById("options3dMeshRotationRange").addEventListener("input", changeRotation);
|
||||
document.getElementById("options3dMeshRotationNumber").addEventListener("change", changeRotation);
|
||||
document.getElementById("options3dGlobeRotationRange").addEventListener("input", changeRotation);
|
||||
|
|
@ -1069,6 +1070,10 @@ function toggle3dOptions() {
|
|||
document.getElementById("options3dMeshSky").addEventListener("input", changeColors);
|
||||
document.getElementById("options3dMeshWater").addEventListener("input", changeColors);
|
||||
document.getElementById("options3dGlobeResolution").addEventListener("change", changeResolution);
|
||||
// document.getElementById("options3dMeshWireframeMode").addEventListener("change",toggleWireframe3d);
|
||||
document.getElementById("options3dSunColor").addEventListener("input", changeSunColor);
|
||||
document.getElementById("options3dSubdivide").addEventListener("change",toggle3dSubdivision);
|
||||
|
||||
|
||||
function updateValues() {
|
||||
const globe = document.getElementById("canvas3d").dataset.type === "viewGlobe";
|
||||
|
|
@ -1081,6 +1086,7 @@ function toggle3dOptions() {
|
|||
options3dSunY.value = ThreeD.options.sun.y;
|
||||
options3dSunZ.value = ThreeD.options.sun.z;
|
||||
options3dMeshRotationRange.value = options3dMeshRotationNumber.value = ThreeD.options.rotateMesh;
|
||||
options3dMeshSkinResolution.value = ThreeD.options.resolutionScale;
|
||||
options3dGlobeRotationRange.value = options3dGlobeRotationNumber.value = ThreeD.options.rotateGlobe;
|
||||
options3dMeshLabels3d.value = ThreeD.options.labels3d;
|
||||
options3dMeshSkyMode.value = ThreeD.options.extendedWater;
|
||||
|
|
@ -1088,6 +1094,8 @@ function toggle3dOptions() {
|
|||
options3dMeshSky.value = ThreeD.options.skyColor;
|
||||
options3dMeshWater.value = ThreeD.options.waterColor;
|
||||
options3dGlobeResolution.value = ThreeD.options.resolution;
|
||||
options3dSunColor.value = ThreeD.options.sunColor;
|
||||
options3dSubdivide.value = ThreeD.options.subdivide;
|
||||
}
|
||||
|
||||
function changeHeightScale() {
|
||||
|
|
@ -1095,11 +1103,20 @@ function toggle3dOptions() {
|
|||
ThreeD.setScale(+this.value);
|
||||
}
|
||||
|
||||
function changeResolutionScale() {
|
||||
options3dMeshSkinResolution.value = this.value;
|
||||
ThreeD.setResolutionScale(+this.value);
|
||||
}
|
||||
|
||||
function changeLightness() {
|
||||
options3dLightnessRange.value = options3dLightnessNumber.value = this.value;
|
||||
ThreeD.setLightness(this.value / 100);
|
||||
}
|
||||
|
||||
function changeSunColor(){
|
||||
ThreeD.setSunColor(options3dSunColor.value);
|
||||
}
|
||||
|
||||
function changeSunPosition() {
|
||||
const x = +options3dSunX.value;
|
||||
const y = +options3dSunY.value;
|
||||
|
|
@ -1117,6 +1134,14 @@ function toggle3dOptions() {
|
|||
ThreeD.toggleLabels();
|
||||
}
|
||||
|
||||
function toggle3dSubdivision(){
|
||||
ThreeD.toggle3dSubdivision();
|
||||
}
|
||||
|
||||
// function toggleWireframe3d() {
|
||||
// ThreeD.toggleWireframe();
|
||||
// }
|
||||
|
||||
function toggleSkyMode() {
|
||||
const hide = ThreeD.options.extendedWater;
|
||||
options3dColorSection.style.display = hide ? "none" : "block";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
// version and caching control
|
||||
const version = "1.89.38"; // generator version, update each time
|
||||
|
||||
|
||||
|
||||
const version = "1.89.39"; // generator version, update each time
|
||||
|
||||
|
||||
{
|
||||
document.title += " v" + version;
|
||||
|
|
@ -28,6 +32,7 @@ const version = "1.89.38"; // generator version, update each time
|
|||
|
||||
<ul>
|
||||
<strong>Latest changes:</strong>
|
||||
<li>New 3D Scene options and quality improvements.</li>
|
||||
<li>Autosave feature (in Options)</li>
|
||||
<li>Google translation support (in Options)</li>
|
||||
<li>Religions can be edited and redrawn like cultures</li>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue