aether-shards/src/game-viewport.js

126 lines
3.3 KiB
JavaScript
Raw Normal View History

2025-12-16 23:52:58 +00:00
import { LitElement, html, css } from "lit";
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { VoxelGrid } from "./grid/VoxelGrid.js";
import { VoxelManager } from "./grid/VoxelManager.js";
export class GameViewport extends LitElement {
2025-12-16 23:14:39 +00:00
static styles = css`
:host {
display: block;
width: 100%;
height: 100%;
overflow: hidden;
}
2025-12-16 23:52:58 +00:00
#canvas-container {
2025-12-16 23:14:39 +00:00
width: 100%;
height: 100%;
}
`;
2025-12-16 23:52:58 +00:00
constructor() {
super();
this.scene = null;
this.camera = null;
this.renderer = null;
this.voxelGrid = null;
this.voxelManager = null;
}
2025-12-16 23:14:39 +00:00
firstUpdated() {
this.initThreeJS();
2025-12-16 23:52:58 +00:00
this.initGameWorld();
this.animate();
2025-12-16 23:14:39 +00:00
}
initThreeJS() {
const container = this.shadowRoot.getElementById("canvas-container");
2025-12-16 23:52:58 +00:00
// Scene Setup
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x0a0b10);
// Lighting (Essential for LambertMaterial)
const ambientLight = new THREE.AmbientLight(0x404040, 1.5); // Soft white light
this.scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(10, 20, 10);
this.scene.add(dirLight);
// Camera
this.camera = new THREE.PerspectiveCamera(
45,
2025-12-16 23:14:39 +00:00
window.innerWidth / window.innerHeight,
0.1,
1000
);
2025-12-16 23:52:58 +00:00
this.camera.position.set(20, 20, 20);
this.camera.lookAt(0, 0, 0);
// Renderer
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(this.renderer.domElement);
// Controls
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
2025-12-16 23:14:39 +00:00
// Handle Resize
2025-12-16 23:52:58 +00:00
window.addEventListener("resize", this.onWindowResize.bind(this));
}
initGameWorld() {
// 1. Create Data Grid
this.voxelGrid = new VoxelGrid(16, 8, 16);
// 2. Generate Test Terrain (Simple Flat Floor + Random Pillars)
for (let x = 0; x < 16; x++) {
for (let z = 0; z < 16; z++) {
// Base Floor (Stone)
this.voxelGrid.setVoxel(x, 0, z, 1);
// Random Details (Dirt/Grass)
if (Math.random() > 0.8) {
this.voxelGrid.setVoxel(x, 1, z, 2); // Dirt Mound
}
// Aether Crystal Pillar
if (x === 8 && z === 8) {
this.voxelGrid.setVoxel(x, 1, z, 4);
this.voxelGrid.setVoxel(x, 2, z, 4);
this.voxelGrid.setVoxel(x, 3, z, 4);
}
}
}
// 3. Initialize Visual Manager
this.voxelManager = new VoxelManager(this.voxelGrid, this.scene);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
this.controls.update();
// Update Voxels if dirty
if (this.voxelManager) this.voxelManager.update();
this.renderer.render(this.scene, this.camera);
}
onWindowResize() {
if (!this.camera || !this.renderer) return;
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
2025-12-16 23:14:39 +00:00
}
render() {
2025-12-16 23:52:58 +00:00
return html`<div id="canvas-container"></div>`;
2025-12-16 23:14:39 +00:00
}
}
customElements.define("game-viewport", GameViewport);