Compare commits

..

14 Commits

Author SHA1 Message Date
069f4e1878 Update readme 2020-01-19 01:49:04 -05:00
31cb65caa5 Remove hexsphere 7 option 2020-01-19 01:28:08 -05:00
d94b6b18af Add _config.yml to docs/ 2020-01-19 01:24:04 -05:00
f90d8fce00 Add built files to /docs 2020-01-19 01:18:43 -05:00
856cdafcb4 Add shape bin files, shape selection 2020-01-19 01:17:25 -05:00
1dce74a9ea Clean up code a bit 2019-02-09 19:10:05 -05:00
35bb35415a Add detail 1 screenshot to README 2019-02-09 18:53:30 -05:00
2ddcc0bbd3 Add README with screenshots 2019-02-09 18:40:04 -05:00
69ac45b21c At last! A real hexsphere! 2019-02-09 17:38:42 -05:00
d062708cec Pointy hexagonal faces. Avg adjacent centroid. 2019-02-09 17:28:21 -05:00
1743145944 Detail level 8 crashed my computer. How about 6... 2019-02-04 23:47:45 -05:00
3b75f81d49 Add some timing and hex info logs 2019-02-04 23:45:28 -05:00
ec1a200aaf Create more efficient BufferGeometry
And, cache midpoint vertices.
2019-02-04 23:17:57 -05:00
113de04cbe Finally a hex sphere, albeit pointy
I still need to flatten the hexagons and pentagons to make it a true dual
polyhedron of the icosahedron.

Also needs performance improvements. Like caching the vertices, switching from
fan subdivision to a different method that represents hexagons and pentagons
with less triangles.
2019-02-04 01:43:11 -05:00
44 changed files with 463 additions and 164 deletions

32
README.md Normal file
View File

@ -0,0 +1,32 @@
# Hexsphere and Icosahedron Viewer
View live at: https://www.hallada.net/planet/
Renders shapes generated from my other project
[icosahedron](https://crates.io/crates/icosahedron) ([github
repo](https://github.com/thallada/icosahedron)) using
[regl.js](https://github.com/regl-project/regl).
Since this is hosted on Github, which has a 100 MB file limit. The most detailed
shapes available for rendering are the hexsphere at detail level 6 and the
icosahedron at detail level 7.
## Running
Checkout and then run:
```
npm install
npm start
```
## Building
Compiles files to the `/docs` folder which is the folder set up for Github pages
hosting.
```
npm run build
```
Any output needs to be committed for it to appear on Github pages.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
docs/_config.yml Normal file
View File

@ -0,0 +1 @@
theme: none

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

51
docs/index.html Normal file
View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Icosahedrons and Hexspheres Generated in Rust</title>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
height: 100%;
color: #eee;
background-color: black;
font-family: sans-serif;
}
canvas { width: 100%; height: 100%; }
div.loading-container { display: flex; align-items: center; justify-content: center; height: 100%; }
div.select-container { position: fixed; top: 0px; width: 100%; z-index: 1; margin: 10px; }
p.loading { font-size: 32px; }
</style>
</head>
<body>
<div class="select-container">
<label>
Rendered Shape:&nbsp;
<select id="shape-select">
<option value="hexsphere_0">Hexsphere 0</option>
<option value="hexsphere_1">Hexsphere 1</option>
<option value="hexsphere_2">Hexsphere 2</option>
<option value="hexsphere_3">Hexsphere 3</option>
<option value="hexsphere_4" selected>Hexsphere 4</option>
<option value="hexsphere_5">Hexsphere 5</option>
<option value="hexsphere_6">Hexsphere 6</option>
<option value="hexsphere_7">Hexsphere 7</option>
<option value="icosahedron_0">Icosahedron 0</option>
<option value="icosahedron_1">Icosahedron 1</option>
<option value="icosahedron_2">Icosahedron 2</option>
<option value="icosahedron_3">Icosahedron 3</option>
<option value="icosahedron_4">Icosahedron 4</option>
<option value="icosahedron_5">Icosahedron 5</option>
<option value="icosahedron_6">Icosahedron 6</option>
<option value="icosahedron_7">Icosahedron 7</option>
</select>
</label>
<span id="shape-loading" style="display: none">Loading...</span>
</div>
<div class="loading-container">
<p class="loading">Loading...</p>
</div>
<script type="text/javascript" src="main.js"></script></body>
</html>

2
docs/main.js Normal file

File diff suppressed because one or more lines are too long

1
docs/main.js.map Normal file

File diff suppressed because one or more lines are too long

BIN
img/detail-0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
img/detail-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
img/detail-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -5,19 +5,22 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --mode development --watch-poll --progress --hot", "start": "webpack-dev-server --mode development --watch --progress --hot --host 0.0.0.0 --port 8888 --public mule.hallada.net:8888",
"build": "webpack" "build": "webpack"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"angle-normals": "^1.0.0",
"regl": "^1.3.11",
"regl-camera": "^2.1.1",
"three": "^0.101.1", "three": "^0.101.1",
"three-orbit-controls": "^82.1.0", "three-orbit-controls": "^82.1.0"
"three-subdivision-modifier": "^1.0.5"
}, },
"devDependencies": { "devDependencies": {
"file-loader": "^3.0.1", "file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"url-loader": "^3.0.0",
"webpack": "^4.29.0", "webpack": "^4.29.0",
"webpack-cli": "^3.2.1", "webpack-cli": "^3.2.1",
"webpack-dev-server": "^3.1.14" "webpack-dev-server": "^3.1.14"

View File

@ -2,12 +2,49 @@
<html> <html>
<head> <head>
<meta charset=utf-8 /> <meta charset=utf-8 />
<title>My first three.js app</title> <title>Icosahedrons and Hexspheres Generated in Rust</title>
<style> <style>
html, body { margin: 0; padding: 0; overflow: hidden; } html, body {
margin: 0;
padding: 0;
overflow: hidden;
height: 100%;
color: #eee;
background-color: black;
font-family: sans-serif;
}
canvas { width: 100%; height: 100%; } canvas { width: 100%; height: 100%; }
div.loading-container { display: flex; align-items: center; justify-content: center; height: 100%; }
div.select-container { position: fixed; top: 0px; width: 100%; z-index: 1; margin: 10px; }
p.loading { font-size: 32px; }
</style> </style>
</head> </head>
<body> <body>
<div class="select-container">
<label>
Rendered Shape:&nbsp;
<select id="shape-select">
<option value="hexsphere_0">Hexsphere 0</option>
<option value="hexsphere_1">Hexsphere 1</option>
<option value="hexsphere_2">Hexsphere 2</option>
<option value="hexsphere_3">Hexsphere 3</option>
<option value="hexsphere_4" selected>Hexsphere 4</option>
<option value="hexsphere_5">Hexsphere 5</option>
<option value="hexsphere_6">Hexsphere 6</option>
<option value="icosahedron_0">Icosahedron 0</option>
<option value="icosahedron_1">Icosahedron 1</option>
<option value="icosahedron_2">Icosahedron 2</option>
<option value="icosahedron_3">Icosahedron 3</option>
<option value="icosahedron_4">Icosahedron 4</option>
<option value="icosahedron_5">Icosahedron 5</option>
<option value="icosahedron_6">Icosahedron 6</option>
<option value="icosahedron_7">Icosahedron 7</option>
</select>
</label>
<span id="shape-loading" style="display: none">Loading...</span>
</div>
<div class="loading-container">
<p class="loading">Loading...</p>
</div>
</body> </body>
</html> </html>

View File

@ -1,156 +0,0 @@
import * as THREE from "three";
var OrbitControls = require("three-orbit-controls")(THREE);
var SubdivisionModifier = require("three-subdivision-modifier");
const scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
var geometry = new THREE.IcosahedronBufferGeometry(5, 1);
var position = geometry.getAttribute("position");
var colors = [];
var color = new THREE.Color(Math.random(), Math.random(), Math.random());
for (var i = 0; i < position.count / 3; i += 1) {
color.setRGB(i / (position.count / 3), i / (position.count / 3), i / (position.count / 3));
colors.push(color.r, color.g, color.b);
colors.push(color.r, color.g, color.b);
colors.push(color.r, color.g, color.b);
}
function disposeArray() {
this.array = null;
}
geometry.addAttribute("color", new THREE.Float32BufferAttribute(colors, 3).onUpload(disposeArray));
var material = new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors });
var sphere = new THREE.Mesh(geometry, material);
// scene.add(sphere);
var polyhedronMesh = new THREE.Object3D();
var faces;
function displayPolyhedron(data)
{
scene.remove(polyhedronMesh);
polyhedronMesh = polyhedronDataToMesh(data);
scene.add(polyhedronMesh);
}
function polyhedronDataToMesh(data)
{
var polyhedron = new THREE.Object3D();
// convert vertex data to THREE.js vectors
var vertex = [];
for (var i = 0; i < data.vertex.length; i++)
vertex.push( new THREE.Vector3( data.vertex[i][0], data.vertex[i][1], data.vertex[i][2] ).multiplyScalar(100) );
// var vertexGeometry = new THREE.SphereGeometry( 6, 12, 6 );
// var vertexMaterial = new THREE.MeshLambertMaterial( { color: 0x222244 } );
// var vertexSingleMesh = new THREE.Mesh( vertexGeometry );
// var vertexAmalgam = new THREE.Geometry();
// for (var i = 0; i < data.vertex.length; i++)
// {
// var vMesh = vertexSingleMesh.clone();
// vMesh.position = vertex[i];
// THREE.GeometryUtils.merge( vertexAmalgam, vMesh );
// }
// var vertexMesh = new THREE.Mesh( vertexAmalgam, vertexMaterial );
// polyhedron.add( vertexMesh );
// // convert edge data to cylinders
// var edgeMaterial = new THREE.MeshLambertMaterial( {color: 0x666666} );
// var edgeAmalgam = new THREE.Geometry();
// for (var i = 0; i < data.edge.length; i++)
// {
// var index0 = data.edge[i][0];
// var index1 = data.edge[i][1];
// var eMesh = cylinderMesh( vertex[index0], vertex[index1], edgeMaterial );
// THREE.GeometryUtils.merge( edgeAmalgam, eMesh );
// }
// var edgeMesh = new THREE.Mesh( edgeAmalgam, edgeMaterial );
// polyhedron.add( edgeMesh );
// convert face data to a single (triangulated) geometry
var faceMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors, side: THREE.FrontSide, transparent: false, opacity:0.8 } );
var faceColors =
{
3: new THREE.Color( 0xcc0000 ),
4: new THREE.Color( 0x00cc00 ),
5: new THREE.Color( 0x0000cc ),
6: new THREE.Color( 0xcccc00 ),
7: new THREE.Color( 0x999999 ),
8: new THREE.Color( 0x990099 ),
9: new THREE.Color( 0xff6600 ),
10: new THREE.Color( 0x6666ff )
};
var geometry = new THREE.Geometry();
geometry.vertices = vertex;
var faceIndex = 0;
for (var faceNum = 0; faceNum < data.face.length; faceNum++)
{
for (var i = 0; i < data.face[faceNum].length - 2; i++)
{
geometry.faces[faceIndex] = new THREE.Face3( data.face[faceNum][0], data.face[faceNum][i+1], data.face[faceNum][i+2] );
geometry.faces[faceIndex].color = faceColors[data.face[faceNum].length];
faceIndex++;
}
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
var modifier = new SubdivisionModifier(5);
modifier.modify(geometry);
for ( var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[ i ];
for ( var j = 0; j < 3; j ++ ) {
// var vertexIndex = face[ faceIndices[ j ] ];
// var vertex = geometry.vertices[ vertexIndex ];
var hue = Math.random();
var color = new THREE.Color().setHSL( hue, 1, 0.5 );
face.vertexColors[ j ] = color;
}
}
faces = new THREE.Mesh(geometry, faceMaterial);
faces.scale.multiplyScalar(1.01);
polyhedron.add(faces);
return polyhedron;
}
displayPolyhedron({
"name":"Truncated Icosahedron",
"category":["Archimedean Solid"],
"vertex":[[0,0,1.021],[0.4035482,0,0.9378643],[-0.2274644,0.3333333,0.9378643],[-0.1471226,-0.375774,0.9378643],[0.579632,0.3333333,0.7715933],[0.5058321,-0.375774,0.8033483],[-0.6020514,0.2908927,0.7715933],[-0.05138057,0.6666667,0.7715933],[0.1654988,-0.6080151,0.8033483],[-0.5217096,-0.4182147,0.7715933],[0.8579998,0.2908927,0.4708062],[0.3521676,0.6666667,0.6884578],[0.7841999,-0.4182147,0.5025612],[-0.657475,0.5979962,0.5025612],[-0.749174,-0.08488134,0.6884578],[-0.3171418,0.8302373,0.5025612],[0.1035333,-0.8826969,0.5025612],[-0.5836751,-0.6928964,0.4708062],[0.8025761,0.5979962,0.2017741],[0.9602837,-0.08488134,0.3362902],[0.4899547,0.8302373,0.3362902],[0.7222343,-0.6928964,0.2017741],[-0.8600213,0.5293258,0.1503935],[-0.9517203,-0.1535518,0.3362902],[-0.1793548,0.993808,0.1503935],[0.381901,-0.9251375,0.2017741],[-0.2710537,-0.9251375,0.3362902],[-0.8494363,-0.5293258,0.2017741],[0.8494363,0.5293258,-0.2017741],[1.007144,-0.1535518,-0.06725804],[0.2241935,0.993808,0.06725804],[0.8600213,-0.5293258,-0.1503935],[-0.7222343,0.6928964,-0.2017741],[-1.007144,0.1535518,0.06725804],[-0.381901,0.9251375,-0.2017741],[0.1793548,-0.993808,-0.1503935],[-0.2241935,-0.993808,-0.06725804],[-0.8025761,-0.5979962,-0.2017741],[0.5836751,0.6928964,-0.4708062],[0.9517203,0.1535518,-0.3362902],[0.2710537,0.9251375,-0.3362902],[0.657475,-0.5979962,-0.5025612],[-0.7841999,0.4182147,-0.5025612],[-0.9602837,0.08488134,-0.3362902],[-0.1035333,0.8826969,-0.5025612],[0.3171418,-0.8302373,-0.5025612],[-0.4899547,-0.8302373,-0.3362902],[-0.8579998,-0.2908927,-0.4708062],[0.5217096,0.4182147,-0.7715933],[0.749174,0.08488134,-0.6884578],[0.6020514,-0.2908927,-0.7715933],[-0.5058321,0.375774,-0.8033483],[-0.1654988,0.6080151,-0.8033483],[0.05138057,-0.6666667,-0.7715933],[-0.3521676,-0.6666667,-0.6884578],[-0.579632,-0.3333333,-0.7715933],[0.1471226,0.375774,-0.9378643],[0.2274644,-0.3333333,-0.9378643],[-0.4035482,0,-0.9378643],[0,0,-1.021]],
"edge":[[0,3],[3,8],[8,5],[5,1],[1,0],[2,7],[7,15],[15,13],[13,6],[6,2],[4,10],[10,18],[18,20],[20,11],[11,4],[9,14],[14,23],[23,27],[27,17],[17,9],[12,21],[21,31],[31,29],[29,19],[19,12],[16,26],[26,36],[36,35],[35,25],[25,16],[22,32],[32,42],[42,43],[43,33],[33,22],[24,30],[30,40],[40,44],[44,34],[34,24],[28,39],[39,49],[49,48],[48,38],[38,28],[37,47],[47,55],[55,54],[54,46],[46,37],[41,45],[45,53],[53,57],[57,50],[50,41],[51,52],[52,56],[56,59],[59,58],[58,51],[1,4],[11,7],[2,0],[6,14],[9,3],[5,12],[19,10],[17,26],[16,8],[25,21],[13,22],[33,23],[20,30],[24,15],[29,39],[28,18],[34,32],[27,37],[46,36],[38,40],[35,45],[41,31],[43,47],[50,49],[44,52],[51,42],[54,53],[48,56],[58,55],[57,59]],
"face":[[0,3,8,5,1],[2,7,15,13,6],[4,10,18,20,11],[9,14,23,27,17],[12,21,31,29,19],[16,26,36,35,25],[22,32,42,43,33],[24,30,40,44,34],[28,39,49,48,38],[37,47,55,54,46],[41,45,53,57,50],[51,52,56,59,58],[0,1,4,11,7,2],[0,2,6,14,9,3],[1,5,12,19,10,4],[3,9,17,26,16,8],[5,8,16,25,21,12],[6,13,22,33,23,14],[7,11,20,30,24,15],[10,19,29,39,28,18],[13,15,24,34,32,22],[17,27,37,46,36,26],[18,28,38,40,30,20],[21,25,35,45,41,31],[23,33,43,47,37,27],[29,31,41,50,49,39],[32,34,44,52,51,42],[35,36,46,54,53,45],[38,48,56,52,44,40],[42,51,58,55,47,43],[48,49,50,57,59,56],[53,54,55,58,59,57]]});
var controls = new OrbitControls(camera);
camera.position.z = 15;
controls.update()
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
controls.update();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();

132
src/regl_index.js Normal file
View File

@ -0,0 +1,132 @@
import hexsphere_0 from "./shapes/hexsphere_r1_d0.bin"
import hexsphere_1 from "./shapes/hexsphere_r1_d1.bin"
import hexsphere_2 from "./shapes/hexsphere_r1_d2.bin"
import hexsphere_3 from "./shapes/hexsphere_r1_d3.bin"
import hexsphere_4 from "./shapes/hexsphere_r1_d4.bin"
import hexsphere_5 from "./shapes/hexsphere_r1_d5.bin"
import hexsphere_6 from "./shapes/hexsphere_r1_d6.bin"
import icosahedron_0 from "./shapes/icosahedron_r1_d0.bin"
import icosahedron_1 from "./shapes/icosahedron_r1_d1.bin"
import icosahedron_2 from "./shapes/icosahedron_r1_d2.bin"
import icosahedron_3 from "./shapes/icosahedron_r1_d3.bin"
import icosahedron_4 from "./shapes/icosahedron_r1_d4.bin"
import icosahedron_5 from "./shapes/icosahedron_r1_d5.bin"
import icosahedron_6 from "./shapes/icosahedron_r1_d6.bin"
import icosahedron_7 from "./shapes/icosahedron_r1_d7.bin"
const shapes = {
"hexsphere_0": hexsphere_0,
"hexsphere_1": hexsphere_1,
"hexsphere_2": hexsphere_2,
"hexsphere_3": hexsphere_3,
"hexsphere_4": hexsphere_4,
"hexsphere_5": hexsphere_5,
"hexsphere_6": hexsphere_6,
"icosahedron_0": icosahedron_0,
"icosahedron_1": icosahedron_1,
"icosahedron_2": icosahedron_2,
"icosahedron_3": icosahedron_3,
"icosahedron_4": icosahedron_4,
"icosahedron_5": icosahedron_5,
"icosahedron_6": icosahedron_6,
"icosahedron_7": icosahedron_7,
}
const regl = require("regl")({
extensions: ["OES_element_index_uint"],
})
const camera = require("regl-camera")(regl, {
center: [0, 0, 0],
distance: 3,
})
const drawShape = hexsphere => regl({
vert: `
precision mediump float;
uniform mat4 projection, view;
attribute vec3 position, normal, color;
varying vec3 fragNormal, fragPosition, fragColor;
void main() {
fragNormal = normal;
fragPosition = position;
fragColor = color;
gl_Position = projection * view * vec4(position, 1.0);
}`,
frag: `
precision mediump float;
struct Light {
vec3 color;
vec3 position;
};
uniform Light lights[1];
varying vec3 fragNormal, fragPosition, fragColor;
void main() {
vec3 normal = normalize(fragNormal);
vec3 light = vec3(0.1, 0.1, 0.1);
for (int i = 0; i < 1; i++) {
vec3 lightDir = normalize(lights[i].position - fragPosition);
float diffuse = max(0.0, dot(lightDir, normal));
light += diffuse * lights[i].color;
}
gl_FragColor = vec4(fragColor * light, 1.0);
}`,
attributes: {
position: hexsphere.positions,
normal: hexsphere.normals,
color: hexsphere.colors,
},
elements: hexsphere.cells,
uniforms: {
"lights[0].color": [1, 1, 1],
"lights[0].position": ({ tick }) => {
const t = 0.008 * tick
return [
1000 * Math.cos(t),
1000 * Math.sin(t),
1000 * Math.sin(t)
]
},
},
})
let draw = null
const loadShape = shape => {
fetch(shape)
.then(response => response.arrayBuffer())
.then(buffer => {
let reader = new DataView(buffer);
let numVertices = reader.getUint32(0, true);
let numCells = reader.getUint32(4, true);
const shapeData = {
positions: new Float32Array(buffer, 8, numVertices * 3),
normals: new Float32Array(buffer, numVertices * 12 + 8, numVertices * 3),
colors: new Float32Array(buffer, numVertices * 24 + 8, numVertices * 3),
cells: new Uint32Array(buffer, numVertices * 36 + 8, numCells * 3),
}
draw = drawShape(shapeData)
regl.frame(() => {
regl.clear({
depth: 1,
color: [0, 0, 0, 1]
})
camera(() => {
draw()
})
})
shapeSelectLoading.style.display = "none"
})
}
const shapeSelect = document.querySelector("select#shape-select")
const shapeSelectLoading = document.querySelector("#shape-loading")
shapeSelect.value = "hexsphere_4"
loadShape(hexsphere_4)
shapeSelect.addEventListener("change", event => {
shapeSelectLoading.style.display = "inline"
loadShape(shapes[event.target.value])
})

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

186
src/three_index.js Normal file
View File

@ -0,0 +1,186 @@
import * as THREE from "three";
const OrbitControls = require("three-orbit-controls")(THREE);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
console.time("init geo");
const geometry = new THREE.IcosahedronGeometry(1, 1);
console.timeEnd("init geo");
console.log(geometry);
console.time("Hexsphere");
const vertToFaces = {};
const newVertices = [];
const colors = [];
for (let i = 0; i < geometry.faces.length; i += 1) {
const face = geometry.faces[i];
if (vertToFaces[face.a]) {
vertToFaces[face.a].push(i);
} else {
vertToFaces[face.a] = [i];
}
if (vertToFaces[face.b]) {
vertToFaces[face.b].push(i);
} else {
vertToFaces[face.b] = [i];
}
if (vertToFaces[face.c]) {
vertToFaces[face.c].push(i);
} else {
vertToFaces[face.c] = [i];
}
}
console.log(vertToFaces);
const originalVertCount = geometry.vertices.length;
const calculateCentroid = (pa, pb, pc) => {
const vabHalf = pb.clone().sub(pa).divideScalar(2);
const pabHalf = pa.clone().add(vabHalf);
const centroid = pc.clone().sub(pabHalf).multiplyScalar(1/3).add(pabHalf);
return centroid;
}
const faceCentroids = {};
for (let i = 0; i < geometry.faces.length; i += 1) {
const face = geometry.faces[i];
const vertexA = geometry.vertices[face.a];
const vertexB = geometry.vertices[face.b];
const vertexC = geometry.vertices[face.c];
faceCentroids[i] = calculateCentroid(vertexA, vertexB, vertexC);
}
const findAdjacentFace = (vertexIndex, faces) => {
for (let i = 0; i < faces.length; i += 1) {
const faceIndex = faces[i];
const face = geometry.faces[faceIndex];
if ([face.a, face.b, face.c].includes(vertexIndex)) return faceIndex;
}
}
const findCenterPoint = faces => {
const centerPoint = new THREE.Vector3(0, 0, 0);
for (let i = 0; i < faces.length; i += 1) {
centerPoint.add(faceCentroids[faces[i]]);
}
centerPoint.divideScalar(faces.length);
return centerPoint;
}
const midCentroidCache = {};
const calculateMidCentroid = (vertexIndex, faces, centroid) => {
const adjFaceIndex = findAdjacentFace(vertexIndex, faces);
const adjCentroid = faceCentroids[adjFaceIndex];
const midCentroidKey = vertexIndex + ",F" + adjFaceIndex;
let midCentroid = midCentroidCache[midCentroidKey];
if (!midCentroid) {
midCentroid = centroid.clone().lerp(adjCentroid, 0.5);
midCentroidCache[midCentroidKey] = midCentroid;
}
return midCentroid;
}
console.time("dual");
let hexCount = 0;
let pentCount = 0;
for (let i = 0; i < originalVertCount; i += 1) {
const faces = vertToFaces[i];
if (faces.length === 6) {
hexCount += 1;
} else if (faces.length === 5) {
pentCount += 1;
}
const color = new THREE.Color(Math.random(), Math.random(), Math.random());
const centerPoint = findCenterPoint(faces);
for (let j = 0; j < faces.length; j += 1) {
const faceIndex = faces[j];
const face = geometry.faces[faceIndex];
const sortedVerts = [face.a, face.b, face.c].filter(vert => vert !== i);
sortedVerts.unshift(i)
const centroid = faceCentroids[faceIndex];
const midBCentroid = calculateMidCentroid(sortedVerts[1], faces, centroid);
const midCCentroid = calculateMidCentroid(sortedVerts[2], faces, centroid);
newVertices.push(
centerPoint.x, centerPoint.y, centerPoint.z,
centroid.x, centroid.y, centroid.z,
midBCentroid.x, midBCentroid.y, midBCentroid.z,
centerPoint.x, centerPoint.y, centerPoint.z,
centroid.x, centroid.y, centroid.z,
midCCentroid.x, midCCentroid.y, midCCentroid.z,
);
colors.push(
color.r, color.g, color.b,
color.r, color.g, color.b,
color.r, color.g, color.b,
color.r, color.g, color.b,
color.r, color.g, color.b,
color.r, color.g, color.b,
);
}
}
console.timeEnd("dual");
console.log(`hexagons: ${hexCount}`);
console.log(`pentagons: ${pentCount}`);
function disposeArray() {
this.array = null;
}
console.time("find geo");
const newGeometry = new THREE.BufferGeometry();
newGeometry.addAttribute("position", new THREE.Float32BufferAttribute(newVertices, 3).onUpload(disposeArray));
newGeometry.addAttribute("color", new THREE.Float32BufferAttribute(colors, 3).onUpload(disposeArray));
newGeometry.computeFaceNormals();
newGeometry.computeVertexNormals();
const nonBuffer = new THREE.Geometry().fromBufferGeometry(newGeometry);
console.log(nonBuffer);
console.timeEnd("find geo");
console.time("other render");
const material = new THREE.MeshBasicMaterial({ vertexColors: THREE.VertexColors, side: THREE.DoubleSide });
const sphere = new THREE.Mesh(newGeometry, material);
scene.add(sphere);
const controls = new OrbitControls(camera);
camera.position.z = 15;
controls.update()
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
console.timeEnd("other render");
console.timeEnd("Hexsphere");
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
controls.update();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();

View File

@ -2,15 +2,25 @@ const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = { module.exports = {
entry: "./src/index.js", entry: "./src/regl_index.js",
output: { output: {
filename: "main.js", filename: "main.js",
path: path.resolve(__dirname, "dist") path: path.resolve(__dirname, "docs")
}, },
module: { module: {
rules: [ rules: [
{ {
test: /\.(png|jpg|gif)$/, test: /\.(png|jpg|gif|bin)$/,
use: [
{
loader: 'file-loader',
options: {},
},
],
},
{
test: /\.json$/,
type: 'javascript/auto',
use: [ use: [
{ {
loader: 'file-loader', loader: 'file-loader',