Files
isdefoobaropen/src/open.js

264 lines
7.4 KiB
JavaScript

import * as THREE from "three";
const STAR_POINTS = 5;
let scene, camera, renderer;
let coreStarGroup;
let lights = {};
let animation = {
steps: 0,
x: new Float32Array(4),
y: new Float32Array(4),
z: new Float32Array(4),
};
function interpolate(range, factor) {
for (let i = 1; i < range.length; ++i) {
range[i] += (range[i - 1] - range[i]) * factor;
}
}
/**
* Create a BufferGeometry from vertices and faces.
*
* @param {Float32Array} vertices
* @param {number[]} faces
* @returns {BufferGeometry}
*/
THREE.BufferGeometry.fromVertices = function (vertices, faces) {
let geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(faces);
geometry.computeVertexNormals();
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
return geometry;
};
Float32Array.prototype.last = function () {
return this[this.length - 1];
};
function addStar() {
let frontGeometry = THREE.BufferGeometry.fromVertices(starCoordinates(2, 3, -0.1), filledStarIndices());
let backGeometry = THREE.BufferGeometry.fromVertices(starCoordinates(2, 3, 0.1), filledStarIndices());
let material = new THREE.MeshStandardMaterial({
side: THREE.FrontSide,
color: 0x00ff00,
flatShading: true,
});
let backMaterial = material.clone();
backMaterial.side = THREE.BackSide;
let star = new THREE.Mesh(frontGeometry, material);
star.receiveShadow = true;
coreStarGroup.add(star);
let backStar = new THREE.Mesh(backGeometry, backMaterial);
backStar.receiveShadow = true;
coreStarGroup.add(backStar);
let ringGeometry = generateStarRing(2, 3, 0.4);
let ring = new THREE.Mesh(ringGeometry, material);
ring.receiveShadow = ring.castShadow = true;
coreStarGroup.add(ring);
}
/**
* Concatenate a series of Float32 arrays.
*
* @returns {Float32Array}
*/
function concatFloat32Array() {
let length = 0;
for (let cur of arguments) {
length += cur.length;
}
const buf = new Float32Array(length);
let offset = 0;
for (let cur of arguments) {
buf.set(cur, offset);
offset += cur.length;
}
return buf;
}
function generateStarRing(innerRadius, outerRadius, size) {
let vertexCandidates = [
starCoordinates(innerRadius + 0.5 * size, outerRadius + 0.5 * size, -0.5 * size),
starCoordinates(innerRadius + 0.5 * size, outerRadius + 0.5 * size, 0.5 * size),
starCoordinates(innerRadius - 0.5 * size, outerRadius - 0.5 * size, 0.5 * size),
starCoordinates(innerRadius - 0.5 * size, outerRadius - 0.5 * size, -0.5 * size),
];
let vertices = concatFloat32Array(
vertexCandidates[0],
vertexCandidates[1],
vertexCandidates[2],
vertexCandidates[3],
vertexCandidates[1],
vertexCandidates[2],
vertexCandidates[3],
vertexCandidates[0],
);
console.log(vertices);
const faces = new Array(2 * STAR_POINTS * 3 * 4);
for (let i = 0; i < 4; ++i) {
const offset = 2 * 2 * STAR_POINTS * i;
for (let j = 0; j < 2 * STAR_POINTS; ++j) {
const nextRow = (j + 1) % (2 * STAR_POINTS);
faces.push(offset + j, offset + j + 2 * STAR_POINTS, offset + nextRow);
faces.push(offset + j + 2 * STAR_POINTS, offset + nextRow + 2 * STAR_POINTS, offset + nextRow);
}
}
return THREE.BufferGeometry.fromVertices(vertices, faces);
}
function startOpenAnimation() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.autoUpdate = true;
document.body.appendChild(renderer.domElement);
coreStarGroup = new THREE.Group();
scene.add(coreStarGroup);
initLighting();
addStar();
loadFont();
requestAnimationFrame(animate)
}
function initLighting() {
let spotLight = new THREE.PointLight(0xffffff);
spotLight.position.set(10, 10, 10);
spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 1024; // default
spotLight.shadow.mapSize.height = 1024; // default
scene.add(spotLight);
let ambient = new THREE.AmbientLight(0x404040);
scene.add(ambient);
lights.spotLight = spotLight;
lights.ambient = ambient;
}
/**
* Load the font data and render the message.
*/
function loadFont() {
import('three/examples/fonts/helvetiker_bold.typeface').then(function (data) {
let font = new THREE.Font(data);
let geometry = new THREE.TextGeometry('Ja!', {
font: font,
size: 1.2,
height: 0.5,
curveSegments: 12,
bevelEnabled: true,
bevelSize: 0.05,
bevelThickness: 0.05
}).center();
geometry.computeFaceNormals();
let textMaterial = new THREE.MeshStandardMaterial({
color: 0xff00ff
});
let mesh = new THREE.Mesh(geometry, textMaterial);
mesh.castShadow = true;
mesh.receiveShadow = true;
coreStarGroup.add(mesh);
});
}
/**
* Construct the vertex indices needed to create a filled star.
* @returns {Number[]}
*/
function filledStarIndices() {
const buf = Array(2 * STAR_POINTS * 3);
const TOTAL_VERTICES = 2 * STAR_POINTS;
// First, the "points" of the star.
for (let i = 0; i < STAR_POINTS; ++i) {
const offset = 3 * i;
buf[offset] = 2 * i;
buf[offset + 1] = 2 * i + 1;
buf[offset + 2] = (2 * i + TOTAL_VERTICES - 1) % (TOTAL_VERTICES);
}
// Then, the inner part, constructed from opposing triangles.
for (let i = 0; i < STAR_POINTS; ++i) {
const offset = 3 * (STAR_POINTS + i);
buf[offset] = 2 * i + 1;
buf[offset + 1] = (2 * i + 3) % TOTAL_VERTICES;
buf[offset + 2] = (2 * i + 3 + 2 * Math.floor(STAR_POINTS / 2)) % (TOTAL_VERTICES);
}
return buf;
}
/**
* Generate the outer points of an N pointed star.
*
* @param innerRadius
* @param outerRadius
* @param z
* @returns {Float32Array}
*/
function starCoordinates(innerRadius, outerRadius, z) {
const buf = new Float32Array(3 * STAR_POINTS * 2);
const STAR_SLICE = 2 * Math.PI / STAR_POINTS;
for (let i = 0; i < STAR_POINTS; ++i) {
const offset = 3 * 2 * i;
buf[offset] = Math.sin(STAR_SLICE * i) * outerRadius;
buf[offset + 1] = Math.cos(STAR_SLICE * i) * outerRadius;
buf[offset + 3] = Math.sin(STAR_SLICE * (i + 0.5)) * innerRadius;
buf[offset + 4] = Math.cos(STAR_SLICE * (i + 0.5)) * innerRadius;
buf[offset + 5] = buf[offset + 2] = z;
}
return buf;
}
function animate() {
requestAnimationFrame(animate);
if (animation.steps === 0) {
animation.steps = Math.floor(Math.random() * 60) + 30;
animation.x[0] = 4 * Math.random() * Math.PI;
animation.y[0] = 4 * Math.random() * Math.PI;
animation.z[0] = 4 * Math.random() * Math.PI;
}
animation.steps--;
interpolate(animation.x, 0.05);
interpolate(animation.y, 0.05);
interpolate(animation.z, 0.05);
coreStarGroup.rotation.x = animation.x.last();
coreStarGroup.rotation.y = animation.y.last();
coreStarGroup.rotation.z = animation.z.last();
renderer.render(scene, camera);
}
export {startOpenAnimation};