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 { static styles = css` :host { display: block; width: 100%; height: 100%; overflow: hidden; } #canvas-container { width: 100%; height: 100%; } `; constructor() { super(); this.scene = null; this.camera = null; this.renderer = null; this.voxelGrid = null; this.voxelManager = null; } async firstUpdated() { this.initThreeJS(); await this.initGameWorld(); this.animate(); } initThreeJS() { const container = this.shadowRoot.getElementById("canvas-container"); // Scene Setup this.scene = new THREE.Scene(); this.scene.background = new THREE.Color(0x0a0b10); // Lighting (Essential for LambertMaterial) const ambientLight = new THREE.AmbientLight(0x909090, 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, window.innerWidth / window.innerHeight, 0.1, 1000 ); 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; // Handle Resize window.addEventListener("resize", this.onWindowResize.bind(this)); } async initGameWorld() { // 1. Create Data Grid this.voxelGrid = new VoxelGrid(20, 8, 20); const { CaveGenerator } = await import("./generation/CaveGenerator.js"); const { RuinGenerator } = await import("./generation/RuinGenerator.js"); const { CrystalSpiresGenerator } = await import( "./generation/CrystalSpiresGenerator.js" ); const crystalSpiresGen = new CrystalSpiresGenerator(this.voxelGrid, 12345); crystalSpiresGen.generate(5, 8); // const ruinGen = new RuinGenerator(this.voxelGrid, 12345); // ruinGen.generate(5, 4, 6); // const caveGen = new CaveGenerator(this.voxelGrid, 12345); // caveGen.generate(0.5, 1); this.voxelManager = new VoxelManager(this.voxelGrid, this.scene); // this.voxelManager.updateMaterials(ruinGen.generatedAssets); // this.voxelManager.updateMaterials(caveGen.generatedAssets); this.voxelManager.update(); this.voxelManager.focusCamera(this.controls); } 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); } render() { return html`
`; } } customElements.define("game-viewport", GameViewport);