Fix level double initialization and refactor ship physics
All checks were successful
Build / build (push) Successful in 1m20s
All checks were successful
Build / build (push) Successful in 1m20s
Major Changes: - Fix Level1 double initialization by deferring initialize() call - Removed initialize() from Level1 constructor - Main.ts now explicitly calls initialize() after registering ready observable - Added error logging for double initialization detection - Refactor Ship to use loadAsset utility and new GLB structure - Changed from SceneLoader.ImportMeshAsync to loadAsset pattern - Ship constructor no longer calls initialize() - must be called explicitly - Updated physics to use transformNode from GLB container - Adjusted control mappings for yaw/pitch/roll (inverted signs) - Reduced force multipliers for better control feel - Remove MaterialFactory pattern - Deleted src/materialFactory.ts - LevelDeserializer now creates PBRMaterial directly for planets/sun - Removed texture quality settings from gameConfig - Cleaned up settings UI to remove texture quality controls - Fix UI element hiding when entering VR - Editor and Settings links now properly hidden on level start - Update GLB models for new asset structure - Updated ship.glb and base.glb models - Modified loadAsset to work with container transformNodes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
146ffccd3d
commit
20dfc238f8
41
index.html
41
index.html
@ -301,47 +301,6 @@
|
|||||||
<p class="subtitle">Configure graphics quality and physics settings</p>
|
<p class="subtitle">Configure graphics quality and physics settings</p>
|
||||||
|
|
||||||
<div class="settings-grid">
|
<div class="settings-grid">
|
||||||
<!-- Graphics Settings -->
|
|
||||||
<div class="section">
|
|
||||||
<h2>🎨 Graphics Quality</h2>
|
|
||||||
<p style="color: #aaa; font-size: 0.9em; margin-bottom: 20px;">
|
|
||||||
Higher quality settings may impact performance on lower-end devices.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="planetTextureLevel">Planet Quality</label>
|
|
||||||
<select id="planetTextureLevel">
|
|
||||||
<option value="WIREFRAME">Wireframe (Lowest)</option>
|
|
||||||
<option value="SIMPLE_MATERIAL">Simple Material</option>
|
|
||||||
<option value="FULL_TEXTURE">Full Texture (Recommended)</option>
|
|
||||||
<option value="PBR_TEXTURE">PBR Texture (Highest)</option>
|
|
||||||
</select>
|
|
||||||
<div class="help-text">Controls planet rendering quality and detail</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="asteroidTextureLevel">Asteroid Quality</label>
|
|
||||||
<select id="asteroidTextureLevel">
|
|
||||||
<option value="WIREFRAME">Wireframe (Lowest)</option>
|
|
||||||
<option value="SIMPLE_MATERIAL">Simple Material</option>
|
|
||||||
<option value="FULL_TEXTURE">Full Texture (Recommended)</option>
|
|
||||||
<option value="PBR_TEXTURE">PBR Texture (Highest)</option>
|
|
||||||
</select>
|
|
||||||
<div class="help-text">Controls asteroid rendering quality and detail</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="sunTextureLevel">Sun Quality</label>
|
|
||||||
<select id="sunTextureLevel">
|
|
||||||
<option value="WIREFRAME">Wireframe (Lowest)</option>
|
|
||||||
<option value="SIMPLE_MATERIAL">Simple Material</option>
|
|
||||||
<option value="FULL_TEXTURE">Full Texture (Recommended)</option>
|
|
||||||
<option value="PBR_TEXTURE">PBR Texture (Highest)</option>
|
|
||||||
</select>
|
|
||||||
<div class="help-text">Controls sun rendering quality</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Physics Settings -->
|
<!-- Physics Settings -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>⚛️ Physics</h2>
|
<h2>⚛️ Physics</h2>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
public/ship1.stl
BIN
public/ship1.stl
Binary file not shown.
@ -1,13 +1,3 @@
|
|||||||
/**
|
|
||||||
* Texture detail levels for game objects
|
|
||||||
*/
|
|
||||||
export enum TextureLevel {
|
|
||||||
WIREFRAME = 'WIREFRAME',
|
|
||||||
SIMPLE_MATERIAL = 'SIMPLE_MATERIAL',
|
|
||||||
FULL_TEXTURE = 'FULL_TEXTURE',
|
|
||||||
PBR_TEXTURE = 'PBR_TEXTURE'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global game configuration settings
|
* Global game configuration settings
|
||||||
* Singleton class for managing game-wide settings
|
* Singleton class for managing game-wide settings
|
||||||
@ -15,10 +5,6 @@ export enum TextureLevel {
|
|||||||
export class GameConfig {
|
export class GameConfig {
|
||||||
private static _instance: GameConfig;
|
private static _instance: GameConfig;
|
||||||
|
|
||||||
// Texture detail settings
|
|
||||||
public planetTextureLevel: TextureLevel = TextureLevel.FULL_TEXTURE;
|
|
||||||
public asteroidTextureLevel: TextureLevel = TextureLevel.FULL_TEXTURE;
|
|
||||||
public sunTextureLevel: TextureLevel = TextureLevel.FULL_TEXTURE;
|
|
||||||
public debug: boolean = false;
|
public debug: boolean = false;
|
||||||
// Physics settings
|
// Physics settings
|
||||||
public physicsEnabled: boolean = true;
|
public physicsEnabled: boolean = true;
|
||||||
@ -46,9 +32,6 @@ export class GameConfig {
|
|||||||
*/
|
*/
|
||||||
public save(): void {
|
public save(): void {
|
||||||
const config = {
|
const config = {
|
||||||
planetTextureLevel: this.planetTextureLevel,
|
|
||||||
asteroidTextureLevel: this.asteroidTextureLevel,
|
|
||||||
sunTextureLevel: this.sunTextureLevel,
|
|
||||||
physicsEnabled: this.physicsEnabled,
|
physicsEnabled: this.physicsEnabled,
|
||||||
debug: this.debug
|
debug: this.debug
|
||||||
};
|
};
|
||||||
@ -63,9 +46,6 @@ export class GameConfig {
|
|||||||
const stored = localStorage.getItem('game-config');
|
const stored = localStorage.getItem('game-config');
|
||||||
if (stored) {
|
if (stored) {
|
||||||
const config = JSON.parse(stored);
|
const config = JSON.parse(stored);
|
||||||
this.planetTextureLevel = config.planetTextureLevel ?? TextureLevel.FULL_TEXTURE;
|
|
||||||
this.asteroidTextureLevel = config.asteroidTextureLevel ?? TextureLevel.FULL_TEXTURE;
|
|
||||||
this.sunTextureLevel = config.sunTextureLevel ?? TextureLevel.FULL_TEXTURE;
|
|
||||||
this.physicsEnabled = config.physicsEnabled ?? true;
|
this.physicsEnabled = config.physicsEnabled ?? true;
|
||||||
this.debug = config.debug ?? false;
|
this.debug = config.debug ?? false;
|
||||||
} else {
|
} else {
|
||||||
@ -80,9 +60,6 @@ export class GameConfig {
|
|||||||
* Reset to default settings
|
* Reset to default settings
|
||||||
*/
|
*/
|
||||||
public reset(): void {
|
public reset(): void {
|
||||||
this.planetTextureLevel = TextureLevel.FULL_TEXTURE;
|
|
||||||
this.asteroidTextureLevel = TextureLevel.FULL_TEXTURE;
|
|
||||||
this.sunTextureLevel = TextureLevel.FULL_TEXTURE;
|
|
||||||
this.physicsEnabled = true;
|
this.physicsEnabled = true;
|
||||||
this.debug = false;
|
this.debug = false;
|
||||||
this.save();
|
this.save();
|
||||||
|
|||||||
@ -30,6 +30,7 @@ export class Level1 implements Level {
|
|||||||
this._deserializer = new LevelDeserializer(levelConfig);
|
this._deserializer = new LevelDeserializer(levelConfig);
|
||||||
this._ship = new Ship(audioEngine);
|
this._ship = new Ship(audioEngine);
|
||||||
|
|
||||||
|
|
||||||
const xr = DefaultScene.XR;
|
const xr = DefaultScene.XR;
|
||||||
|
|
||||||
debugLog('Level1 constructor - Setting up XR observables');
|
debugLog('Level1 constructor - Setting up XR observables');
|
||||||
@ -45,7 +46,7 @@ export class Level1 implements Level {
|
|||||||
this._ship.addController(controller);
|
this._ship.addController(controller);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.initialize();
|
// Don't call initialize here - let Main call it after registering the observable
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadyObservable(): Observable<Level> {
|
getReadyObservable(): Observable<Level> {
|
||||||
@ -92,9 +93,10 @@ export class Level1 implements Level {
|
|||||||
public async initialize() {
|
public async initialize() {
|
||||||
debugLog('Initializing level from config:', this._levelConfig.difficulty);
|
debugLog('Initializing level from config:', this._levelConfig.difficulty);
|
||||||
if (this._initialized) {
|
if (this._initialized) {
|
||||||
|
console.error('Initialize called twice');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await this._ship.initialize();
|
||||||
setLoadingMessage("Loading level from configuration...");
|
setLoadingMessage("Loading level from configuration...");
|
||||||
|
|
||||||
// Use deserializer to create all entities from config
|
// Use deserializer to create all entities from config
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import {
|
|||||||
AbstractMesh,
|
AbstractMesh,
|
||||||
MeshBuilder,
|
MeshBuilder,
|
||||||
Observable,
|
Observable,
|
||||||
|
PBRMaterial,
|
||||||
|
Texture,
|
||||||
Vector3
|
Vector3
|
||||||
} from "@babylonjs/core";
|
} from "@babylonjs/core";
|
||||||
import { DefaultScene } from "./defaultScene";
|
import { DefaultScene } from "./defaultScene";
|
||||||
@ -14,7 +16,8 @@ import {
|
|||||||
validateLevelConfig
|
validateLevelConfig
|
||||||
} from "./levelConfig";
|
} from "./levelConfig";
|
||||||
import { GameConfig } from "./gameConfig";
|
import { GameConfig } from "./gameConfig";
|
||||||
import { MaterialFactory } from "./materialFactory";
|
import { FireProceduralTexture } from "@babylonjs/procedural-textures";
|
||||||
|
import { createSphereLightmap } from "./sphereLightmap";
|
||||||
import debugLog from './debug';
|
import debugLog from './debug';
|
||||||
import StarBase from "./starBase";
|
import StarBase from "./starBase";
|
||||||
|
|
||||||
@ -71,8 +74,7 @@ export class LevelDeserializer {
|
|||||||
* Create the start base from config
|
* Create the start base from config
|
||||||
*/
|
*/
|
||||||
private async createStartBase(): Promise<AbstractMesh> {
|
private async createStartBase(): Promise<AbstractMesh> {
|
||||||
const position = this?.config?.startBase?.position?this.arrayToVector3(this?.config?.startBase?.position):null;
|
return await StarBase.buildStarBase();
|
||||||
return await StarBase.buildStarBase(position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,18 +87,13 @@ export class LevelDeserializer {
|
|||||||
segments: 32
|
segments: 32
|
||||||
}, this.scene);
|
}, this.scene);
|
||||||
sun.position = this.arrayToVector3(config.position);
|
sun.position = this.arrayToVector3(config.position);
|
||||||
// Create material using GameConfig texture level
|
|
||||||
const gameConfig = GameConfig.getInstance();
|
|
||||||
const material = MaterialFactory.createSunMaterial(
|
|
||||||
"sunMaterial",
|
|
||||||
gameConfig.sunTextureLevel,
|
|
||||||
this.scene
|
|
||||||
);
|
|
||||||
sun.material = material;
|
|
||||||
|
|
||||||
// Create glow layer
|
// Create PBR sun material with fire texture
|
||||||
//const gl = new GlowLayer("glow", this.scene);
|
const material = new PBRMaterial("sunMaterial", this.scene);
|
||||||
//gl.intensity = 1;
|
material.emissiveTexture = new FireProceduralTexture("fire", 1024, this.scene);
|
||||||
|
material.emissiveColor.set(0.5, 0.5, 0.1);
|
||||||
|
material.unlit = true;
|
||||||
|
sun.material = material;
|
||||||
|
|
||||||
return sun;
|
return sun;
|
||||||
}
|
}
|
||||||
@ -122,16 +119,28 @@ export class LevelDeserializer {
|
|||||||
// Calculate direction from planet to sun
|
// Calculate direction from planet to sun
|
||||||
const toSun = sunPosition.subtract(planetPosition).normalize();
|
const toSun = sunPosition.subtract(planetPosition).normalize();
|
||||||
|
|
||||||
// Create material using GameConfig texture level
|
// Create PBR planet material
|
||||||
const config = GameConfig.getInstance();
|
const material = new PBRMaterial(planetConfig.name + "-material", this.scene);
|
||||||
const material = MaterialFactory.createPlanetMaterial(
|
const texture = new Texture(planetConfig.texturePath, this.scene);
|
||||||
planetConfig.name + "-material",
|
|
||||||
planetConfig.texturePath,
|
// Create lightmap with bright light pointing toward sun
|
||||||
config.planetTextureLevel,
|
const lightmap = createSphereLightmap(
|
||||||
|
planetConfig.name + "-lightmap",
|
||||||
|
256,
|
||||||
this.scene,
|
this.scene,
|
||||||
toSun
|
toSun,
|
||||||
|
1,
|
||||||
|
toSun.negate(),
|
||||||
|
0.3,
|
||||||
|
0.3
|
||||||
);
|
);
|
||||||
|
|
||||||
|
material.albedoTexture = texture;
|
||||||
|
material.lightmapTexture = lightmap;
|
||||||
|
material.useLightmapAsShadowmap = true;
|
||||||
|
material.roughness = 0.8;
|
||||||
|
material.metallic = 0;
|
||||||
|
|
||||||
planet.material = material;
|
planet.material = material;
|
||||||
|
|
||||||
planets.push(planet);
|
planets.push(planet);
|
||||||
|
|||||||
37
src/main.ts
37
src/main.ts
@ -46,20 +46,31 @@ export class Main {
|
|||||||
setLoadingMessage("This browser does not support WebXR");
|
setLoadingMessage("This browser does not support WebXR");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.initialize();
|
|
||||||
|
|
||||||
// Listen for level selection event
|
// Listen for level selection event
|
||||||
window.addEventListener('levelSelected', async (e: CustomEvent) => {
|
window.addEventListener('levelSelected', async (e: CustomEvent) => {
|
||||||
|
this._started = true;
|
||||||
|
await this.initialize();
|
||||||
const {levelName, config} = e.detail as {levelName: string, config: LevelConfig};
|
const {levelName, config} = e.detail as {levelName: string, config: LevelConfig};
|
||||||
|
|
||||||
debugLog(`Starting level: ${levelName}`);
|
debugLog(`Starting level: ${levelName}`);
|
||||||
|
|
||||||
// Show loading UI again
|
// Hide all UI elements
|
||||||
const mainDiv = document.querySelector('#mainDiv');
|
const mainDiv = document.querySelector('#mainDiv');
|
||||||
const levelSelect = document.querySelector('#levelSelect') as HTMLElement;
|
const levelSelect = document.querySelector('#levelSelect') as HTMLElement;
|
||||||
|
const editorLink = document.querySelector('.editor-link') as HTMLElement;
|
||||||
|
const settingsLink = document.querySelector('.settings-link') as HTMLElement;
|
||||||
|
|
||||||
if (levelSelect) {
|
if (levelSelect) {
|
||||||
levelSelect.style.display = 'none';
|
levelSelect.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
if (editorLink) {
|
||||||
|
editorLink.style.display = 'none';
|
||||||
|
}
|
||||||
|
if (settingsLink) {
|
||||||
|
settingsLink.style.display = 'none';
|
||||||
|
}
|
||||||
setLoadingMessage("Initializing Level...");
|
setLoadingMessage("Initializing Level...");
|
||||||
|
|
||||||
// Unlock audio engine on user interaction
|
// Unlock audio engine on user interaction
|
||||||
@ -80,10 +91,15 @@ export class Main {
|
|||||||
this.play();
|
this.play();
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Now initialize the level (after observable is registered)
|
||||||
|
await this._currentLevel.initialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for test level button click
|
// Listen for test level button click
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const levelSelect = document.querySelector('#levelSelect');
|
||||||
|
levelSelect.classList.add('ready');
|
||||||
debugLog('[Main] DOMContentLoaded fired, looking for test button...');
|
debugLog('[Main] DOMContentLoaded fired, looking for test button...');
|
||||||
const testLevelBtn = document.querySelector('#testLevelBtn');
|
const testLevelBtn = document.querySelector('#testLevelBtn');
|
||||||
debugLog('[Main] Test button found:', !!testLevelBtn);
|
debugLog('[Main] Test button found:', !!testLevelBtn);
|
||||||
@ -92,9 +108,12 @@ export class Main {
|
|||||||
testLevelBtn.addEventListener('click', async () => {
|
testLevelBtn.addEventListener('click', async () => {
|
||||||
debugLog('[Main] ========== TEST LEVEL BUTTON CLICKED ==========');
|
debugLog('[Main] ========== TEST LEVEL BUTTON CLICKED ==========');
|
||||||
|
|
||||||
// Show loading UI
|
// Hide all UI elements
|
||||||
const mainDiv = document.querySelector('#mainDiv');
|
const mainDiv = document.querySelector('#mainDiv');
|
||||||
const levelSelect = document.querySelector('#levelSelect') as HTMLElement;
|
const levelSelect = document.querySelector('#levelSelect') as HTMLElement;
|
||||||
|
const editorLink = document.querySelector('.editor-link') as HTMLElement;
|
||||||
|
const settingsLink = document.querySelector('.settings-link') as HTMLElement;
|
||||||
|
|
||||||
debugLog('[Main] mainDiv exists:', !!mainDiv);
|
debugLog('[Main] mainDiv exists:', !!mainDiv);
|
||||||
debugLog('[Main] levelSelect exists:', !!levelSelect);
|
debugLog('[Main] levelSelect exists:', !!levelSelect);
|
||||||
|
|
||||||
@ -102,6 +121,12 @@ export class Main {
|
|||||||
levelSelect.style.display = 'none';
|
levelSelect.style.display = 'none';
|
||||||
debugLog('[Main] levelSelect hidden');
|
debugLog('[Main] levelSelect hidden');
|
||||||
}
|
}
|
||||||
|
if (editorLink) {
|
||||||
|
editorLink.style.display = 'none';
|
||||||
|
}
|
||||||
|
if (settingsLink) {
|
||||||
|
settingsLink.style.display = 'none';
|
||||||
|
}
|
||||||
setLoadingMessage("Initializing Test Scene...");
|
setLoadingMessage("Initializing Test Scene...");
|
||||||
|
|
||||||
// Unlock audio engine on user interaction
|
// Unlock audio engine on user interaction
|
||||||
@ -193,7 +218,6 @@ export class Main {
|
|||||||
// photoDome1.position = DefaultScene.MainScene.activeCamera.globalPosition;
|
// photoDome1.position = DefaultScene.MainScene.activeCamera.globalPosition;
|
||||||
// photoDome2.position = DefaultScene.MainScene.activeCamera.globalPosition;
|
// photoDome2.position = DefaultScene.MainScene.activeCamera.globalPosition;
|
||||||
});
|
});
|
||||||
setLoadingMessage("Select a difficulty to begin!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setupScene() {
|
private async setupScene() {
|
||||||
@ -236,12 +260,8 @@ export class Main {
|
|||||||
window.setTimeout(()=>{
|
window.setTimeout(()=>{
|
||||||
if (!this._started) {
|
if (!this._started) {
|
||||||
this._started = true;
|
this._started = true;
|
||||||
const levelSelect = document.querySelector('#levelSelect');
|
|
||||||
if (levelSelect) {
|
|
||||||
levelSelect.classList.add('ready');
|
|
||||||
setLoadingMessage("Ready!");
|
setLoadingMessage("Ready!");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this._engine.runRenderLoop(() => {
|
this._engine.runRenderLoop(() => {
|
||||||
@ -250,6 +270,7 @@ export class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async setupPhysics() {
|
private async setupPhysics() {
|
||||||
|
//DefaultScene.MainScene.useRightHandedSystem = true;
|
||||||
const havok = await HavokPhysics();
|
const havok = await HavokPhysics();
|
||||||
const havokPlugin = new HavokPlugin(true, havok);
|
const havokPlugin = new HavokPlugin(true, havok);
|
||||||
//DefaultScene.MainScene.ambientColor = new Color3(.1, .1, .1);
|
//DefaultScene.MainScene.ambientColor = new Color3(.1, .1, .1);
|
||||||
|
|||||||
@ -1,305 +0,0 @@
|
|||||||
import {
|
|
||||||
Color3,
|
|
||||||
DynamicTexture,
|
|
||||||
NoiseProceduralTexture,
|
|
||||||
PBRMaterial,
|
|
||||||
Scene,
|
|
||||||
StandardMaterial,
|
|
||||||
Texture,
|
|
||||||
Vector3
|
|
||||||
} from "@babylonjs/core";
|
|
||||||
import { TextureLevel } from "./gameConfig";
|
|
||||||
import { FireProceduralTexture } from "@babylonjs/procedural-textures";
|
|
||||||
import { createSphereLightmap } from "./sphereLightmap";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory for creating materials at different quality levels
|
|
||||||
*/
|
|
||||||
export class MaterialFactory {
|
|
||||||
/**
|
|
||||||
* Create a planet material based on texture level
|
|
||||||
*/
|
|
||||||
public static createPlanetMaterial(
|
|
||||||
name: string,
|
|
||||||
texturePath: string,
|
|
||||||
textureLevel: TextureLevel,
|
|
||||||
scene: Scene,
|
|
||||||
sunDirection: Vector3
|
|
||||||
): StandardMaterial | PBRMaterial {
|
|
||||||
switch (textureLevel) {
|
|
||||||
case TextureLevel.WIREFRAME:
|
|
||||||
return this.createWireframeMaterial(name, scene, new Color3(0.5, 0.5, 0.8));
|
|
||||||
|
|
||||||
case TextureLevel.SIMPLE_MATERIAL:
|
|
||||||
return this.createSimplePlanetMaterial(name, scene);
|
|
||||||
|
|
||||||
case TextureLevel.FULL_TEXTURE:
|
|
||||||
return this.createFullTexturePlanetMaterial(name, texturePath, scene, sunDirection);
|
|
||||||
|
|
||||||
case TextureLevel.PBR_TEXTURE:
|
|
||||||
return this.createPBRPlanetMaterial(name, texturePath, scene, sunDirection);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return this.createFullTexturePlanetMaterial(name, texturePath, scene, sunDirection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an asteroid material based on texture level
|
|
||||||
*/
|
|
||||||
public static createAsteroidMaterial(
|
|
||||||
name: string,
|
|
||||||
textureLevel: TextureLevel,
|
|
||||||
scene: Scene,
|
|
||||||
originalMaterial?: PBRMaterial
|
|
||||||
): StandardMaterial | PBRMaterial {
|
|
||||||
switch (textureLevel) {
|
|
||||||
case TextureLevel.WIREFRAME:
|
|
||||||
return this.createWireframeMaterial(name, scene, new Color3(0.5, 0.5, 0.5));
|
|
||||||
|
|
||||||
case TextureLevel.SIMPLE_MATERIAL:
|
|
||||||
return this.createSimpleAsteroidMaterial(name, scene);
|
|
||||||
|
|
||||||
case TextureLevel.FULL_TEXTURE:
|
|
||||||
return this.createFullTextureAsteroidMaterial(name, scene, originalMaterial);
|
|
||||||
|
|
||||||
case TextureLevel.PBR_TEXTURE:
|
|
||||||
return this.createPBRAsteroidMaterial(name, scene, originalMaterial);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return this.createFullTextureAsteroidMaterial(name, scene, originalMaterial);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a sun material based on texture level
|
|
||||||
*/
|
|
||||||
public static createSunMaterial(
|
|
||||||
name: string,
|
|
||||||
textureLevel: TextureLevel,
|
|
||||||
scene: Scene
|
|
||||||
): StandardMaterial | PBRMaterial {
|
|
||||||
switch (textureLevel) {
|
|
||||||
case TextureLevel.WIREFRAME:
|
|
||||||
return this.createWireframeMaterial(name, scene, new Color3(1, 1, 0));
|
|
||||||
|
|
||||||
case TextureLevel.SIMPLE_MATERIAL:
|
|
||||||
return this.createSimpleSunMaterial(name, scene);
|
|
||||||
|
|
||||||
case TextureLevel.FULL_TEXTURE:
|
|
||||||
return this.createFullTextureSunMaterial(name, scene);
|
|
||||||
|
|
||||||
case TextureLevel.PBR_TEXTURE:
|
|
||||||
return this.createPBRSunMaterial(name, scene);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return this.createFullTextureSunMaterial(name, scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== Private helper methods ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create wireframe material
|
|
||||||
*/
|
|
||||||
private static createWireframeMaterial(
|
|
||||||
name: string,
|
|
||||||
scene: Scene,
|
|
||||||
color: Color3
|
|
||||||
): StandardMaterial {
|
|
||||||
const material = new StandardMaterial(name, scene);
|
|
||||||
material.wireframe = true;
|
|
||||||
material.emissiveColor = color;
|
|
||||||
material.disableLighting = true;
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create simple planet material with solid color
|
|
||||||
*/
|
|
||||||
private static createSimplePlanetMaterial(name: string, scene: Scene): StandardMaterial {
|
|
||||||
const material = new StandardMaterial(name, scene);
|
|
||||||
material.diffuseColor = new Color3(0.4, 0.6, 0.8);
|
|
||||||
material.specularColor = Color3.Black();
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create full texture planet material (current implementation)
|
|
||||||
*/
|
|
||||||
private static createFullTexturePlanetMaterial(
|
|
||||||
name: string,
|
|
||||||
texturePath: string,
|
|
||||||
scene: Scene,
|
|
||||||
sunDirection: Vector3
|
|
||||||
): StandardMaterial {
|
|
||||||
const material = new StandardMaterial(name, scene);
|
|
||||||
const texture = new Texture(texturePath, scene);
|
|
||||||
|
|
||||||
// Create lightmap with bright light pointing toward sun
|
|
||||||
const lightmap = createSphereLightmap(
|
|
||||||
name + "-lightmap",
|
|
||||||
256,
|
|
||||||
scene,
|
|
||||||
sunDirection,
|
|
||||||
1,
|
|
||||||
sunDirection.negate(),
|
|
||||||
0.3,
|
|
||||||
0.3
|
|
||||||
);
|
|
||||||
|
|
||||||
material.emissiveTexture = texture;
|
|
||||||
material.lightmapTexture = lightmap;
|
|
||||||
material.useLightmapAsShadowmap = true;
|
|
||||||
material.disableLighting = true;
|
|
||||||
material.roughness = 1;
|
|
||||||
material.specularColor = Color3.Black();
|
|
||||||
material.freeze();
|
|
||||||
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create PBR planet material
|
|
||||||
*/
|
|
||||||
private static createPBRPlanetMaterial(
|
|
||||||
name: string,
|
|
||||||
texturePath: string,
|
|
||||||
scene: Scene,
|
|
||||||
sunDirection: Vector3
|
|
||||||
): PBRMaterial {
|
|
||||||
const material = new PBRMaterial(name, scene);
|
|
||||||
const texture = new Texture(texturePath, scene);
|
|
||||||
|
|
||||||
// Create lightmap with bright light pointing toward sun
|
|
||||||
const lightmap = createSphereLightmap(
|
|
||||||
name + "-lightmap",
|
|
||||||
256,
|
|
||||||
scene,
|
|
||||||
sunDirection,
|
|
||||||
1,
|
|
||||||
sunDirection.negate(),
|
|
||||||
0.3,
|
|
||||||
0.3
|
|
||||||
);
|
|
||||||
|
|
||||||
material.albedoTexture = texture;
|
|
||||||
material.lightmapTexture = lightmap;
|
|
||||||
material.useLightmapAsShadowmap = true;
|
|
||||||
material.roughness = 0.8;
|
|
||||||
material.metallic = 0;
|
|
||||||
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create simple asteroid material with solid color
|
|
||||||
*/
|
|
||||||
private static createSimpleAsteroidMaterial(name: string, scene: Scene): StandardMaterial {
|
|
||||||
const material = new StandardMaterial(name, scene);
|
|
||||||
material.diffuseColor = new Color3(0.4, 0.4, 0.4);
|
|
||||||
material.specularColor = Color3.Black();
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create full texture asteroid material (current implementation)
|
|
||||||
*/
|
|
||||||
private static createFullTextureAsteroidMaterial(name: string, scene: Scene, originalMaterial?: PBRMaterial): StandardMaterial {
|
|
||||||
// If we have the original material from GLB, use it as a base
|
|
||||||
if (originalMaterial) {
|
|
||||||
// Clone the original material to preserve bump texture and other properties
|
|
||||||
const material = originalMaterial.clone(name) as PBRMaterial;
|
|
||||||
|
|
||||||
// Create noise texture for color variation
|
|
||||||
const noiseTexture = new NoiseProceduralTexture(name + "-noise", 256, scene);
|
|
||||||
noiseTexture.brightness = 0.6;
|
|
||||||
noiseTexture.octaves = 4;
|
|
||||||
|
|
||||||
// Replace only the albedo texture, keeping bump and other textures
|
|
||||||
material.albedoTexture = noiseTexture;
|
|
||||||
material.roughness = 1;
|
|
||||||
|
|
||||||
return material as any as StandardMaterial;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback if no original material
|
|
||||||
const material = new StandardMaterial(name, scene);
|
|
||||||
const noiseTexture = new NoiseProceduralTexture(name + "-noise", 256, scene);
|
|
||||||
noiseTexture.brightness = 0.6;
|
|
||||||
noiseTexture.octaves = 4;
|
|
||||||
|
|
||||||
material.ambientTexture = noiseTexture;
|
|
||||||
material.diffuseTexture = noiseTexture;
|
|
||||||
material.roughness = 1;
|
|
||||||
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create PBR asteroid material
|
|
||||||
*/
|
|
||||||
private static createPBRAsteroidMaterial(name: string, scene: Scene, originalMaterial?: PBRMaterial): PBRMaterial {
|
|
||||||
// If we have the original material from GLB, use it as a base
|
|
||||||
if (originalMaterial) {
|
|
||||||
// Clone the original material to preserve bump texture and other properties
|
|
||||||
const material = originalMaterial.clone(name) as PBRMaterial;
|
|
||||||
|
|
||||||
// Create noise texture for color variation
|
|
||||||
const noiseTexture = new NoiseProceduralTexture(name + "-noise", 256, scene);
|
|
||||||
noiseTexture.brightness = 0.6;
|
|
||||||
noiseTexture.octaves = 4;
|
|
||||||
|
|
||||||
// Replace only the albedo texture, keeping bump and other textures
|
|
||||||
material.albedoTexture = noiseTexture;
|
|
||||||
material.roughness = 1;
|
|
||||||
material.metallic = 0;
|
|
||||||
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback if no original material
|
|
||||||
const material = new PBRMaterial(name, scene);
|
|
||||||
const noiseTexture = new NoiseProceduralTexture(name + "-noise", 256, scene);
|
|
||||||
noiseTexture.brightness = 0.6;
|
|
||||||
noiseTexture.octaves = 4;
|
|
||||||
|
|
||||||
material.albedoTexture = noiseTexture;
|
|
||||||
material.roughness = 1;
|
|
||||||
material.metallic = 0;
|
|
||||||
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create simple sun material with solid color
|
|
||||||
*/
|
|
||||||
private static createSimpleSunMaterial(name: string, scene: Scene): StandardMaterial {
|
|
||||||
const material = new StandardMaterial(name, scene);
|
|
||||||
material.emissiveColor = new Color3(1, 0.9, 0.2);
|
|
||||||
material.disableLighting = true;
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create full texture sun material (current implementation)
|
|
||||||
*/
|
|
||||||
private static createFullTextureSunMaterial(name: string, scene: Scene): StandardMaterial {
|
|
||||||
const material = new StandardMaterial(name, scene);
|
|
||||||
material.emissiveTexture = new FireProceduralTexture("fire", 1024, scene);
|
|
||||||
material.emissiveColor = new Color3(0.5, 0.5, 0.1);
|
|
||||||
material.disableLighting = true;
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create PBR sun material
|
|
||||||
*/
|
|
||||||
private static createPBRSunMaterial(name: string, scene: Scene): PBRMaterial {
|
|
||||||
const material = new PBRMaterial(name, scene);
|
|
||||||
material.emissiveTexture = new FireProceduralTexture("fire", 1024, scene);
|
|
||||||
material.emissiveColor = new Color3(0.5, 0.5, 0.1);
|
|
||||||
material.unlit = true;
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -55,7 +55,7 @@ export class RockFactory {
|
|||||||
private static async loadMesh() {
|
private static async loadMesh() {
|
||||||
debugLog('loading mesh');
|
debugLog('loading mesh');
|
||||||
this._asteroidMesh = (await loadAsset("asteroid.glb")).meshes.get('Asteroid');
|
this._asteroidMesh = (await loadAsset("asteroid.glb")).meshes.get('Asteroid');
|
||||||
this._asteroidMesh.setParent(null);
|
//this._asteroidMesh.setParent(null);
|
||||||
this._asteroidMesh.setEnabled(false);
|
this._asteroidMesh.setEnabled(false);
|
||||||
debugLog(this._asteroidMesh);
|
debugLog(this._asteroidMesh);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { GameConfig, TextureLevel } from "./gameConfig";
|
import { GameConfig } from "./gameConfig";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the settings screen
|
* Initialize the settings screen
|
||||||
@ -7,9 +7,6 @@ export function initializeSettingsScreen(): void {
|
|||||||
const config = GameConfig.getInstance();
|
const config = GameConfig.getInstance();
|
||||||
|
|
||||||
// Get form elements
|
// Get form elements
|
||||||
const planetTextureSelect = document.getElementById('planetTextureLevel') as HTMLSelectElement;
|
|
||||||
const asteroidTextureSelect = document.getElementById('asteroidTextureLevel') as HTMLSelectElement;
|
|
||||||
const sunTextureSelect = document.getElementById('sunTextureLevel') as HTMLSelectElement;
|
|
||||||
const physicsEnabledCheckbox = document.getElementById('physicsEnabled') as HTMLInputElement;
|
const physicsEnabledCheckbox = document.getElementById('physicsEnabled') as HTMLInputElement;
|
||||||
const debugEnabledCheckbox = document.getElementById('debugEnabled') as HTMLInputElement;
|
const debugEnabledCheckbox = document.getElementById('debugEnabled') as HTMLInputElement;
|
||||||
|
|
||||||
@ -39,9 +36,6 @@ export function initializeSettingsScreen(): void {
|
|||||||
* Load current settings into form
|
* Load current settings into form
|
||||||
*/
|
*/
|
||||||
function loadSettings(): void {
|
function loadSettings(): void {
|
||||||
if (planetTextureSelect) planetTextureSelect.value = config.planetTextureLevel;
|
|
||||||
if (asteroidTextureSelect) asteroidTextureSelect.value = config.asteroidTextureLevel;
|
|
||||||
if (sunTextureSelect) sunTextureSelect.value = config.sunTextureLevel;
|
|
||||||
if (physicsEnabledCheckbox) physicsEnabledCheckbox.checked = config.physicsEnabled;
|
if (physicsEnabledCheckbox) physicsEnabledCheckbox.checked = config.physicsEnabled;
|
||||||
if (debugEnabledCheckbox) debugEnabledCheckbox.checked = config.debug;
|
if (debugEnabledCheckbox) debugEnabledCheckbox.checked = config.debug;
|
||||||
}
|
}
|
||||||
@ -50,9 +44,6 @@ export function initializeSettingsScreen(): void {
|
|||||||
* Save form settings to GameConfig
|
* Save form settings to GameConfig
|
||||||
*/
|
*/
|
||||||
function saveSettings(): void {
|
function saveSettings(): void {
|
||||||
config.planetTextureLevel = planetTextureSelect.value as TextureLevel;
|
|
||||||
config.asteroidTextureLevel = asteroidTextureSelect.value as TextureLevel;
|
|
||||||
config.sunTextureLevel = sunTextureSelect.value as TextureLevel;
|
|
||||||
config.physicsEnabled = physicsEnabledCheckbox.checked;
|
config.physicsEnabled = physicsEnabledCheckbox.checked;
|
||||||
config.debug = debugEnabledCheckbox.checked;
|
config.debug = debugEnabledCheckbox.checked;
|
||||||
config.save();
|
config.save();
|
||||||
|
|||||||
151
src/ship.ts
151
src/ship.ts
@ -8,7 +8,6 @@ import {
|
|||||||
PhysicsAggregate,
|
PhysicsAggregate,
|
||||||
PhysicsMotionType,
|
PhysicsMotionType,
|
||||||
PhysicsShapeType,
|
PhysicsShapeType,
|
||||||
SceneLoader,
|
|
||||||
StandardMaterial,
|
StandardMaterial,
|
||||||
TransformNode,
|
TransformNode,
|
||||||
Vector2,
|
Vector2,
|
||||||
@ -23,10 +22,12 @@ import { GameConfig } from "./gameConfig";
|
|||||||
import { Sight } from "./sight";
|
import { Sight } from "./sight";
|
||||||
import debugLog from './debug';
|
import debugLog from './debug';
|
||||||
import {Scoreboard} from "./scoreboard";
|
import {Scoreboard} from "./scoreboard";
|
||||||
|
import loadAsset from "./utils/loadAsset";
|
||||||
|
import {Debug} from "@babylonjs/core/Legacy/legacy";
|
||||||
const MAX_LINEAR_VELOCITY = 200;
|
const MAX_LINEAR_VELOCITY = 200;
|
||||||
const MAX_ANGULAR_VELOCITY = 1.8;
|
const MAX_ANGULAR_VELOCITY = 1.4;
|
||||||
const LINEAR_FORCE_MULTIPLIER = 1200;
|
const LINEAR_FORCE_MULTIPLIER = 800;
|
||||||
const ANGULAR_FORCE_MULTIPLIER = 20;
|
const ANGULAR_FORCE_MULTIPLIER = 15;
|
||||||
|
|
||||||
const controllerComponents = [
|
const controllerComponents = [
|
||||||
'a-button',
|
'a-button',
|
||||||
@ -69,8 +70,6 @@ export class Ship {
|
|||||||
|
|
||||||
constructor( audioEngine?: AudioEngineV2) {
|
constructor( audioEngine?: AudioEngineV2) {
|
||||||
this._audioEngine = audioEngine;
|
this._audioEngine = audioEngine;
|
||||||
this.setup();
|
|
||||||
this.initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initializeSounds() {
|
private async initializeSounds() {
|
||||||
@ -102,8 +101,8 @@ export class Ship {
|
|||||||
this._shot?.play();
|
this._shot?.play();
|
||||||
const ammo = new InstancedMesh("ammo", this._ammoBaseMesh as Mesh);
|
const ammo = new InstancedMesh("ammo", this._ammoBaseMesh as Mesh);
|
||||||
ammo.parent = this._ship;
|
ammo.parent = this._ship;
|
||||||
ammo.position.y = .5;
|
ammo.position.y = .1;
|
||||||
ammo.position.z = 7.1;
|
ammo.position.z = 8.4;
|
||||||
//ammo.rotation.x = Math.PI / 2;
|
//ammo.rotation.x = Math.PI / 2;
|
||||||
ammo.setParent(null);
|
ammo.setParent(null);
|
||||||
const ammoAggregate = new PhysicsAggregate(ammo, PhysicsShapeType.SPHERE, {
|
const ammoAggregate = new PhysicsAggregate(ammo, PhysicsShapeType.SPHERE, {
|
||||||
@ -141,13 +140,48 @@ export class Ship {
|
|||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
private setup() {
|
|
||||||
this._ship = new TransformNode("ship", DefaultScene.MainScene);
|
|
||||||
|
|
||||||
|
public async initialize() {
|
||||||
|
this._scoreboard = new Scoreboard();
|
||||||
|
this._ship = new TransformNode("shipBawe", DefaultScene.MainScene);
|
||||||
|
//this._ship.rotation.y = Math.PI;
|
||||||
|
const data = await loadAsset('ship.glb');
|
||||||
|
const axes = new Debug.AxesViewer(DefaultScene.MainScene, 1);
|
||||||
|
//axes.xAxis.parent = data.container.rootNodes[0];
|
||||||
|
//axes.yAxis.parent = data.container.rootNodes[0];
|
||||||
|
axes.zAxis.parent = data.container.transformNodes[0];
|
||||||
|
//data.container.transformNodes[0].parent = this._ship;
|
||||||
|
this._ship = data.container.transformNodes[0];
|
||||||
|
this._ship.position.y = 5;
|
||||||
|
|
||||||
|
const config = GameConfig.getInstance();
|
||||||
|
if (config.physicsEnabled) {
|
||||||
|
console.log('Physics Enabled for Ship');
|
||||||
|
if (this._ship) {
|
||||||
|
const agg = new PhysicsAggregate(
|
||||||
|
this._ship,
|
||||||
|
PhysicsShapeType.MESH,
|
||||||
|
{
|
||||||
|
mass: 10,
|
||||||
|
mesh: data.container.rootNodes[0].getChildMeshes()[0] as Mesh
|
||||||
|
},
|
||||||
|
DefaultScene.MainScene
|
||||||
|
);
|
||||||
|
|
||||||
|
agg.body.setMotionType(PhysicsMotionType.DYNAMIC);
|
||||||
|
agg.body.setLinearDamping(.2);
|
||||||
|
agg.body.setAngularDamping(.4);
|
||||||
|
agg.body.setAngularVelocity(new Vector3(0, 0, 0));
|
||||||
|
agg.body.setCollisionCallbackEnabled(true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.warn("No geometry mesh found, cannot create physics");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//shipMesh.position.z = -1;
|
||||||
|
|
||||||
// Create sounds asynchronously if audio engine is available
|
|
||||||
if (this._audioEngine) {
|
if (this._audioEngine) {
|
||||||
this.initializeSounds();
|
await this.initializeSounds();
|
||||||
}
|
}
|
||||||
this._ammoMaterial = new StandardMaterial("ammoMaterial", DefaultScene.MainScene);
|
this._ammoMaterial = new StandardMaterial("ammoMaterial", DefaultScene.MainScene);
|
||||||
this._ammoMaterial.emissiveColor = new Color3(1, 1, 0);
|
this._ammoMaterial.emissiveColor = new Color3(1, 1, 0);
|
||||||
@ -171,11 +205,11 @@ export class Ship {
|
|||||||
DefaultScene.MainScene);
|
DefaultScene.MainScene);
|
||||||
this._camera.parent = this._ship;
|
this._camera.parent = this._ship;
|
||||||
|
|
||||||
DefaultScene.MainScene.setActiveCameraByName("Flat Camera");
|
//DefaultScene.MainScene.setActiveCameraByName("Flat Camera");
|
||||||
|
|
||||||
// Create sight reticle
|
// Create sight reticle
|
||||||
this._sight = new Sight(DefaultScene.MainScene, this._ship, {
|
this._sight = new Sight(DefaultScene.MainScene, this._ship, {
|
||||||
position: new Vector3(0, .5, 125),
|
position: new Vector3(0, .1, 125),
|
||||||
circleRadius: 2,
|
circleRadius: 2,
|
||||||
crosshairLength: 1.5,
|
crosshairLength: 1.5,
|
||||||
lineThickness: 0.1,
|
lineThickness: 0.1,
|
||||||
@ -183,78 +217,20 @@ export class Ship {
|
|||||||
renderingGroupId: 3,
|
renderingGroupId: 3,
|
||||||
centerGap: 0.5
|
centerGap: 0.5
|
||||||
});
|
});
|
||||||
|
console.log(data.meshes.get('Screen'));
|
||||||
|
const screen = DefaultScene.MainScene.getMaterialById('Screen').getBindedMeshes()[0] as Mesh
|
||||||
|
console.log(screen);
|
||||||
|
const old = screen.parent;
|
||||||
|
screen.setParent(null);
|
||||||
|
screen.setPivotPoint(screen.getBoundingInfo().boundingSphere.center);
|
||||||
|
screen.setParent(old);
|
||||||
|
screen.rotation.y = Math.PI;
|
||||||
|
console.log(screen.rotation);
|
||||||
|
console.log(screen.scaling);
|
||||||
|
|
||||||
}
|
this._scoreboard.initialize(screen);
|
||||||
|
|
||||||
|
|
||||||
private async initialize() {
|
|
||||||
this._scoreboard = new Scoreboard();
|
|
||||||
|
|
||||||
const importMesh = await SceneLoader.ImportMeshAsync(null, "./", "ship2.glb", DefaultScene.MainScene);
|
|
||||||
|
|
||||||
const shipMesh = importMesh.meshes[0];
|
|
||||||
shipMesh.id = "shipMesh";
|
|
||||||
shipMesh.name = "shipMesh";
|
|
||||||
debugLog(shipMesh.position);
|
|
||||||
shipMesh.parent = this._ship;
|
|
||||||
debugLog(shipMesh.position);
|
|
||||||
|
|
||||||
// Create physics aggregate based on the loaded mesh (if physics enabled)
|
|
||||||
const config = GameConfig.getInstance();
|
|
||||||
if (config.physicsEnabled) {
|
|
||||||
// Find the actual geometry mesh (usually meshes[1] or a child)
|
|
||||||
//const geometryMesh = importMesh.meshes.find(m => m instanceof Mesh && m.getTotalVertices() > 0) as Mesh;
|
|
||||||
const geo = shipMesh.getChildMeshes()[0]
|
|
||||||
if (geo) {
|
|
||||||
|
|
||||||
|
|
||||||
// Create physics aggregate on the ship TransformNode using the mesh shape
|
|
||||||
const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.CONVEX_HULL, {
|
|
||||||
mass: 100,
|
|
||||||
mesh: (geo as Mesh) // Use the actual ship geometry
|
|
||||||
}, DefaultScene.MainScene);
|
|
||||||
|
|
||||||
|
|
||||||
agg.body.setMotionType(PhysicsMotionType.DYNAMIC);
|
|
||||||
agg.body.setLinearDamping(.2);
|
|
||||||
agg.body.setAngularDamping(.3);
|
|
||||||
agg.body.setAngularVelocity(new Vector3(0, 0, 0));
|
|
||||||
agg.body.setCollisionCallbackEnabled(true);
|
|
||||||
} else {
|
|
||||||
console.warn("No geometry mesh found in ship1.glb, falling back to box shape");
|
|
||||||
// Fallback to box shape if mesh not found
|
|
||||||
const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.BOX, {
|
|
||||||
mass: 100,
|
|
||||||
extents: new Vector3(4, 4, 7.4),
|
|
||||||
center: new Vector3(0, 1, 1.8)
|
|
||||||
}, DefaultScene.MainScene);
|
|
||||||
|
|
||||||
agg.body.setMotionType(PhysicsMotionType.DYNAMIC);
|
|
||||||
agg.body.setLinearDamping(.1);
|
|
||||||
agg.body.setAngularDamping(.2);
|
|
||||||
agg.body.setAngularVelocity(new Vector3(0, 0, 0));
|
|
||||||
agg.body.setCollisionCallbackEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//shipMesh.rotation.y = Angle.FromDegrees(90).radians();
|
|
||||||
//shipMesh.rotation.y = Math.PI;
|
|
||||||
//shipMesh.position.y = 1;
|
|
||||||
shipMesh.position.z = -1;
|
|
||||||
// shipMesh.renderingGroupId = 3;
|
|
||||||
//const light = new PointLight("ship.light", new Vector3(0, .5, .1), DefaultScene.MainScene);
|
|
||||||
//light.intensity = 4;
|
|
||||||
/*light.includedOnlyMeshes = [shipMesh];
|
|
||||||
for (const mesh of shipMesh.getChildMeshes()) {
|
|
||||||
// mesh.renderingGroupId = 3;
|
|
||||||
if (mesh.material.id.indexOf('glass') === -1) {
|
|
||||||
light.includedOnlyMeshes.push(mesh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
light.parent = this._ship;*/
|
|
||||||
//DefaultScene.MainScene.getMaterialById('glass_mat.002').alpha = .4;
|
|
||||||
const screenMesh = DefaultScene.MainScene.getMaterialById('Screen')?.getBindedMeshes()[0];
|
|
||||||
this._scoreboard.initialize(screenMesh as Mesh);
|
|
||||||
//this._scoreboard.initialize(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -287,7 +263,8 @@ export class Ship {
|
|||||||
// Transform to world space - TransformNode vectors are in local space!
|
// Transform to world space - TransformNode vectors are in local space!
|
||||||
const worldDirection = Vector3.TransformNormal(localDirection, this._ship.getWorldMatrix());
|
const worldDirection = Vector3.TransformNormal(localDirection, this._ship.getWorldMatrix());
|
||||||
const force = worldDirection.scale(LINEAR_FORCE_MULTIPLIER);
|
const force = worldDirection.scale(LINEAR_FORCE_MULTIPLIER);
|
||||||
body.applyForce(force, this._ship.physicsBody.transformNode.absolutePosition);
|
const thrustPoint = Vector3.TransformCoordinates(this._ship.physicsBody.getMassProperties().centerOfMass.add(new Vector3(0,1,0)), this._ship.getWorldMatrix());
|
||||||
|
body.applyForce(force, thrustPoint);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,9 +295,9 @@ export class Ship {
|
|||||||
|
|
||||||
// Only apply torque if we haven't reached max angular velocity
|
// Only apply torque if we haven't reached max angular velocity
|
||||||
if (currentAngularSpeed < MAX_ANGULAR_VELOCITY) {
|
if (currentAngularSpeed < MAX_ANGULAR_VELOCITY) {
|
||||||
const yaw = this._leftStickVector.x;
|
const yaw = -this._leftStickVector.x;
|
||||||
const pitch = -this._rightStickVector.y;
|
const pitch = this._rightStickVector.y;
|
||||||
const roll = -this._rightStickVector.x;
|
const roll = this._rightStickVector.x;
|
||||||
|
|
||||||
// Create torque in local space, then transform to world space
|
// Create torque in local space, then transform to world space
|
||||||
const localTorque = new Vector3(pitch, yaw, roll).scale(ANGULAR_FORCE_MULTIPLIER);
|
const localTorque = new Vector3(pitch, yaw, roll).scale(ANGULAR_FORCE_MULTIPLIER);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
AbstractMesh,
|
AbstractMesh,
|
||||||
HavokPlugin,
|
HavokPlugin, Mesh,
|
||||||
PhysicsAggregate,
|
PhysicsAggregate,
|
||||||
PhysicsMotionType,
|
PhysicsMotionType,
|
||||||
PhysicsShapeType,
|
PhysicsShapeType,
|
||||||
@ -17,13 +17,14 @@ import loadAsset from "./utils/loadAsset";
|
|||||||
* @returns Promise resolving to the loaded star base mesh
|
* @returns Promise resolving to the loaded star base mesh
|
||||||
*/
|
*/
|
||||||
export default class StarBase {
|
export default class StarBase {
|
||||||
public static async buildStarBase(position: Vector3): Promise<AbstractMesh> {
|
public static async buildStarBase(): Promise<AbstractMesh> {
|
||||||
const config = GameConfig.getInstance();
|
const config = GameConfig.getInstance();
|
||||||
const scene = DefaultScene.MainScene;
|
const scene = DefaultScene.MainScene;
|
||||||
const importMeshes = await loadAsset('base.glb');
|
const importMeshes = await loadAsset('base.glb');
|
||||||
|
|
||||||
const baseMesh = importMeshes.meshes.get('Base');
|
const baseMesh = importMeshes.meshes.get('Base');
|
||||||
const landingMesh = importMeshes.meshes.get('BaseLandingZone');
|
const landingMesh = importMeshes.meshes.get('BaseLandingZone');
|
||||||
clearParent(importMeshes.meshes, position);
|
|
||||||
|
|
||||||
|
|
||||||
if (config.physicsEnabled) {
|
if (config.physicsEnabled) {
|
||||||
@ -38,14 +39,14 @@ export default class StarBase {
|
|||||||
|
|
||||||
const landingAgg = new PhysicsAggregate(landingMesh, PhysicsShapeType.MESH);
|
const landingAgg = new PhysicsAggregate(landingMesh, PhysicsShapeType.MESH);
|
||||||
landingAgg.body.setMotionType(PhysicsMotionType.ANIMATED);
|
landingAgg.body.setMotionType(PhysicsMotionType.ANIMATED);
|
||||||
landingAgg.body.getCollisionObservable().add((collidedCollidedBody) => {
|
/*landingAgg.body.getCollisionObservable().add((collidedCollidedBody) => {
|
||||||
console.log(collidedCollidedBody);
|
|
||||||
});
|
});*/
|
||||||
landingAgg.shape.isTrigger = true;
|
landingAgg.shape.isTrigger = true;
|
||||||
(DefaultScene.MainScene.getPhysicsEngine().getPhysicsPlugin() as HavokPlugin).onTriggerCollisionObservable.add((eventdata, eventState) => {
|
/*(DefaultScene.MainScene.getPhysicsEngine().getPhysicsPlugin() as HavokPlugin).onTriggerCollisionObservable.add((eventdata, eventState) => {
|
||||||
console.log(eventState);
|
console.log(eventState);
|
||||||
console.log(eventdata);
|
console.log(eventdata);
|
||||||
})
|
})*/
|
||||||
landingAgg.body.setCollisionCallbackEnabled(true);
|
landingAgg.body.setCollisionCallbackEnabled(true);
|
||||||
}
|
}
|
||||||
//importMesh.rootNodes[0].dispose();
|
//importMesh.rootNodes[0].dispose();
|
||||||
|
|||||||
@ -8,7 +8,12 @@ export type LoadedAsset = {
|
|||||||
export default async function loadAsset(file: string, theme: string = "default"): Promise<LoadedAsset> {
|
export default async function loadAsset(file: string, theme: string = "default"): Promise<LoadedAsset> {
|
||||||
const container = await LoadAssetContainerAsync(`assets/themes/${theme}/models/${file}`, DefaultScene.MainScene);
|
const container = await LoadAssetContainerAsync(`assets/themes/${theme}/models/${file}`, DefaultScene.MainScene);
|
||||||
const map: Map<string, AbstractMesh> = new Map();
|
const map: Map<string, AbstractMesh> = new Map();
|
||||||
|
container.addAllToScene();
|
||||||
for (const mesh of container.rootNodes[0].getChildMeshes(false)) {
|
for (const mesh of container.rootNodes[0].getChildMeshes(false)) {
|
||||||
|
console.log(mesh.id, mesh);
|
||||||
|
//mesh.setParent(null);
|
||||||
|
//mesh.rotation.y = Math.PI /2;
|
||||||
|
//mesh.rotation.z = Math.PI;
|
||||||
map.set(mesh.id, mesh);
|
map.set(mesh.id, mesh);
|
||||||
}
|
}
|
||||||
return {container: container, meshes: map};
|
return {container: container, meshes: map};
|
||||||
|
|||||||
Binary file not shown.
BIN
themes/default/base.blend1
Normal file
BIN
themes/default/base.blend1
Normal file
Binary file not shown.
Binary file not shown.
BIN
themes/default/ship.blend1
Normal file
BIN
themes/default/ship.blend1
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user