This repository has been archived on 2025-07-31. You can view files and clone it, but cannot push or open issues or pull requests.
Files
studenthack2024-project/js/constructors.js
Nikolaos Karaolidis aa376ecfd6 Fix public directory
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2024-04-14 11:10:38 +01:00

249 lines
7.8 KiB
JavaScript

import * as THREE from "three";
export const createCelestialBody = function (data, parent, depth = 0, isLast = false) {
let geometry, material;
const detail = 12;
const loader = new THREE.TextureLoader();
if (data.detail === 2) {
geometry = new THREE.IcosahedronGeometry(data.radius, detail);
material = new THREE.MeshPhongMaterial({
map: loader.load("/celestials/" + data.name + "/map.jpg"),
bumpMap: loader.load("/celestials/" + data.name + "/bump.jpg"),
bumpScale: 10,
});
} else if (data.detail === 1 && data.emissive) {
geometry = new THREE.IcosahedronGeometry(data.radius, detail);
material = new THREE.MeshPhongMaterial({
map: loader.load("/celestials/" + data.name + "/map.jpg"),
emissiveMap: loader.load("/celestials/" + data.name + "/map.jpg"),
emissive: 0xffffff,
});
} else if (data.detail === 1) {
geometry = new THREE.IcosahedronGeometry(data.radius, detail);
material = new THREE.MeshPhongMaterial({
map: loader.load("/celestials/" + data.name + "/map.jpg"),
});
} else {
geometry = new THREE.SphereGeometry(data.radius, 64, 64);
material = new THREE.MeshPhysicalMaterial({
color: data.color,
emissive: data.emissive ? data.emissive.color : null,
});
}
const body = new THREE.Mesh(geometry, material);
// Stars
if (data.emissive) {
const light = new THREE.PointLight(data.emissive.color, data.emissive.intensity, data.emissive.distance, data.emissive.decay);
light.position.set(0, 0, 0);
light.castShadow = true;
body.add(light);
}
body.receiveShadow = true;
if (!data.emissive) {
body.castShadow = true;
}
// Flags
body.isCelestialBody = true;
body.position.set(data.orbitRadius || 0, 0, 0);
body.rotation.x = data.inclination || 0;
body.name = data.name;
body.content = data.content;
body.rotationSpeed = data.rotationSpeed;
body.orbitRadius = data.orbitRadius;
body.orbitSpeed = data.orbitSpeed;
body.orbitInclination = data.orbitInclination;
body.orbitInclinationAngle = data.orbitInclinationAngle;
// Parenting
if (parent) {
body.parent = parent;
parent.add(body);
// Orbit Line
if (data.orbitRadius) {
const points = [];
const colors = [];
const color1 = new THREE.Color(data.color);
const color2 = new THREE.Color(0x000000);
for (let i = 0; i <= 360; i++) {
const radians = (i * Math.PI) / 180;
points.push(new THREE.Vector3(data.orbitRadius * Math.cos(radians), 0, data.orbitRadius * -Math.sin(radians)));
const progress = i / 360;
const interpolation = progress < 0.8 ? progress / 0.8 : 1;
const color = color1.clone().lerp(color2, interpolation);
colors.push(color.r, color.g, color.b);
}
const orbitGeometry = new THREE.BufferGeometry().setFromPoints(points);
orbitGeometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
const orbitMaterial = new THREE.LineBasicMaterial({
vertexColors: true,
linewidth: 2,
transparent: true,
opacity: 0.75,
});
const line = new THREE.LineLoop(orbitGeometry, orbitMaterial);
// Flags
line.isOrbit = true;
if (data.orbitInclination) line.rotation.x = data.orbitInclination;
parent.add(line);
body.orbit = line;
}
}
// Dropdown
const dropdown = document.getElementById("dropdown");
const option = document.createElement("option");
option.value = body.uuid;
let prefix = "";
if (depth > 0) {
prefix = "│ ".repeat(depth - 1) + (isLast ? "└─ " : "├─ ");
}
option.textContent = prefix + data.name;
dropdown.appendChild(option);
// Recursion
if (data.children) {
length = data.children.length;
data.children.forEach((data, index) => {
if (data.type && data.type === "particles") return createCelestialParticles(data, body);
else return createCelestialBody(data, body, depth + 1, index === length - 1);
});
}
return body;
};
export const createCelestialParticles = function (data, parent) {
const colors = [];
for (let i = 0; i < 10; i++) {
const color = new THREE.Color(data.color);
color.offsetHSL(Math.random() * 0.2 - 0.1, Math.random() * 0.2 - 0.1, Math.random() * 0.2 - 0.1);
colors.push(color);
}
const sizeWeights = [0.8, 0.9, 1, 1.1, 1.2];
const particleGroups = Array.from({ length: 5 }, () => ({
points: [],
colors: [],
}));
for (let i = 0; i < data.ringVolume; i++) {
const radius = Math.random() * (data.orbitOuterRadius - data.orbitInnerRadius) + data.orbitInnerRadius;
const angle = Math.random() * Math.PI * 2;
const x = Math.cos(angle) * radius;
const y = Math.random() * data.orbitThickness - data.orbitThickness / 2;
const z = Math.sin(angle) * radius;
const groupIndex = Math.floor(Math.random() * 5);
particleGroups[groupIndex].points.push(new THREE.Vector3(x, y, z));
const color = colors[Math.floor(Math.random() * 10)];
particleGroups[groupIndex].colors.push(color.r, color.g, color.b);
}
const particleSystem = new THREE.Group();
particleGroups.forEach((group, index) => {
const geometry = new THREE.BufferGeometry().setFromPoints(group.points);
geometry.setAttribute("color", new THREE.Float32BufferAttribute(group.colors, 3));
const material = new THREE.PointsMaterial({
size: sizeWeights[index] * data.radius,
sizeAttenuation: true,
vertexColors: true,
transparent: true,
opacity: 0.75,
});
const particles = new THREE.Points(geometry, material);
particleSystem.add(particles);
});
// Flags
particleSystem.isCelestialParticles = true;
if (data.orbitInclination) particleSystem.rotation.x = data.orbitInclination;
if (data.orbitInclinationAngle) particleSystem.rotation.y = data.orbitInclinationAngle;
particleSystem.rotationSpeed = data.orbitSpeed;
// Parenting
if (parent) parent.add(particleSystem);
return particleSystem;
};
export const createStarField = function (
count = 10000,
groups = 5,
innerRadius = 20000,
outerRadius = 25000,
colors = [
{ color: 0xfff8e7, weight: 5 },
{ color: 0xffffff, weight: 3 },
{ color: 0xa5d6ff, weight: 2 },
{ color: 0xffffbf, weight: 2 },
{ color: 0xffd6a5, weight: 2 },
{ color: 0xaaaaff, weight: 1 },
{ color: 0xffa5a5, weight: 1 },
{ color: 0xffa5d6, weight: 1 },
{ color: 0xa5a5ff, weight: 1 },
]
) {
const starGroups = Array.from({ length: groups }, () => ({
points: [],
colors: [],
}));
const totalWeight = colors.reduce((sum, color) => sum + color.weight, 0);
const weightedColors = colors.flatMap((color) => Array(color.weight).fill(color.color));
for (let i = 0; i < count; i++) {
const radius = Math.random() * (outerRadius - innerRadius) + innerRadius;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
const x = radius * Math.sin(phi) * Math.cos(theta);
const y = radius * Math.sin(phi) * Math.sin(theta);
const z = radius * Math.cos(phi);
const groupIndex = Math.floor(Math.random() * groups);
starGroups[groupIndex].points.push(new THREE.Vector3(x, y, z));
const color = new THREE.Color(weightedColors[Math.floor(Math.random() * totalWeight)]);
starGroups[groupIndex].colors.push(color.r, color.g, color.b);
}
const starField = new THREE.Group();
starGroups.forEach((group, index) => {
const geometry = new THREE.BufferGeometry().setFromPoints(group.points);
geometry.setAttribute("color", new THREE.Float32BufferAttribute(group.colors, 3));
const material = new THREE.PointsMaterial({
size: 6 * index,
sizeAttenuation: true,
vertexColors: true,
transparent: true,
opacity: 0.8,
});
const stars = new THREE.Points(geometry, material);
starField.add(stars);
});
return starField;
};