Compare commits

..

17 Commits

Author SHA1 Message Date
96bc3df51e Fix position export and add target physics improvements
All checks were successful
Build / build (push) Successful in 2m0s
- Use getAbsolutePosition() in all config builders for correct world coords
- Add TargetComponent case to meshCollector for target export
- Add shared target body cache in RockFactory (asteroids can share targets)
- Add debug logging throughout export and physics pipelines
- Pass targetId to RockFactory for proper target body sharing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 12:33:31 -06:00
4c9e1f65c0 Add BabylonJS Editor workspace boilerplate
Includes:
- Editor project configuration and cache files
- Sample scene with example meshes and geometries
- Asset files (asteroid, base GLB models, environment map)
- Vite/TypeScript configuration for editor preview
- Editor script components for game objects

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 07:49:00 -06:00
9440be3251 Add level change history system with database trigger
- Create level_history table storing previous and new state snapshots
- Add trigger that automatically logs meaningful changes to levels
- Track change_type: update, submitted, published, rejected
- RLS policies for user and admin access to history
- Add RPC functions for querying history and entries
- Set user context in save_level_by_token for proper attribution

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 07:46:31 -06:00
f73661c23b Add BabylonJS Editor plugin for level editing
Plugin features:
- Token-based authentication (user pastes token from website)
- Browse and load official levels
- Browse, load, and save personal levels
- Export current scene as level config JSON
- Import level config into Editor scene
- Editor script components for game objects (asteroid, ship, planet, etc.)
- Floating UI panel for quick access to tools
- Camera speed controls for editor navigation

Note: Uses public Supabase anon key (same as website client bundle)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 07:11:49 -06:00
fe88c2bf47 Add My Levels tab, profile page, and token auth system
- Add My Levels tab to website level selection for viewing private levels
- Add profile page for generating/managing editor plugin tokens
- Create user_tokens table and RPC functions for token-based auth
- Fix cloudLevelService to use maybeSingle() for admin and level queries
- Fix getLevelById to try authenticated client first for private levels
- Add rotation support to asteroids, base, sun, and planets
- Remove deprecated difficultyConfig from level files
- Add editor script components for BabylonJS Editor integration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 07:06:40 -06:00
496bb50095 Add target positions for asteroid orbit and movement
Adds configurable target positions that asteroids can reference:
- Orbit mode: asteroid maintains fixed distance to target via constraint
- MoveToward mode: asteroid velocity redirected toward target

Includes level editor UI for managing targets and asteroid assignments.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 16:47:58 -06:00
71bb2b25da Add sun and starfield config to level config schema
- Add StarfieldConfig interface (count, radius, brightness, pointSize)
- Add scale property to SunConfig for independent x/y/z scaling
- Update levelDeserializer to apply sun scale and expose starfield config
- Update level1 to pass starfield config to BackgroundStars
- Create SunConfigEditor.svelte for editing sun properties
- Create StarfieldConfigEditor.svelte for editing starfield properties
- Add Sun and Stars tabs to LevelConfigEditor

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 15:41:20 -06:00
29db6ec4b7 Add rendering groups, camera far plane, and planet physics
- Set renderingGroupId=2 for game objects (ship, asteroids, base, sun, planets)
- Set camera maxZ=100000 for FreeCamera and XR camera (distant planets)
- Add static sphere physics bodies to planets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 13:13:16 -06:00
21a6d7c9ec Update rendering and add level editor components
- Enable renderingGroupId=3 for missionBrief and statusScreen (always on top)
- Adjust background stars: increase count to 4500, radius to 50000
- Add level editor Svelte components (AsteroidList, BaseConfig, LevelConfig,
  PlanetList, ShipConfig, Vector3Input editors)
- Add LEVEL_TRANSITION.md documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 12:50:12 -06:00
18a9ae9978 Refactor loading into 3-phase system to mask XR camera repositioning
Phase 1: Prefetch assets (ship.glb, asteroid.glb, base.glb, audio)
Phase 2: Create level meshes hidden (before XR entry)
Phase 3: Enter XR, init physics, show meshes, unlock audio

Key changes:
- Split Ship.initialize() into addToScene() + initializePhysics()
- Split RockFactory.createRock() into createRockMesh() + initPhysics()
- Split StarBase.buildStarBase() into addToScene() + initializePhysics()
- Add deserializeMeshes() + initializePhysics() to LevelDeserializer
- Update Level1 to orchestrate new phased flow
- Fix XR camera parenting (use getTransformNodeByName not getMeshById)
- Fix asteroid visibility (show meshes before clearing _createdRocks map)
- Add audioPrefetch utility for prefetching audio files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 12:43:24 -06:00
ad2656a61f Add level editor for superadmins
- Create LevelEditor.svelte with permission-gated level list
- Create LevelEditForm.svelte for editing level metadata
- Add getAllLevelsForAdmin() and updateLevelAsAdmin() to CloudLevelService
- Add /editor/:levelId route to App.svelte
- Show Level Editor link in header only for admins with canManageOfficial
- Add migration to use internal UUID for admins table
- Fix auth0_sub -> auth0_id column name in supabaseService

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 19:57:48 -06:00
6655abeeec Add non-linear controller input curve and fix auth token error
- Replace linear controller scaling with power curve (exp 2.5) for
  smoother VR controls - less sensitive at low deflections, full
  power at 90% stick travel
- Fix Missing Refresh Token error by clearing stale auth state
  automatically instead of leaving users in broken state
- Fix build errors: use ScoreEvent type in weaponSystem, remove
  unused parTime parameter from buildResult

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 19:24:57 -06:00
749cc18211 Refactor scoring system to additive model starting at 0
- Replace multiplier-based scoring with additive system
- Score builds from asteroid destruction based on size and timing
- Small asteroids (<10 scale): 1000 pts, Medium (10-20): 500 pts, Large (>20): 250 pts
- Timing multiplier: 3x in first 1/3 of par time, 2x in middle, 1x in last third
- End-game bonuses only applied at game end (hull, fuel, accuracy)
- Add scale property to ScoreEvent for point calculation
- Update status screen to show "CURRENT SCORE" during play, "FINAL SCORE" at end
- Refactor star ratings display into individual columns
- Fix button clipping on hover with clipChildren = false
- Add reusable button hover effects utility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 16:39:27 -06:00
64331b4566 Add mission_brief_shown event to hint system
- Add 'mission_brief_shown' event type to HintEntry interface
- Add triggerMissionBriefShown() method to LevelHintSystem
- Call hint trigger when mission brief is displayed in Level1
- Remove old missionBriefAudio playback from MissionBrief class

This enables database-configurable audio hints (like welcome_rookie)
to play when the mission brief is shown.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 12:33:10 -06:00
3104859bb7 Fix ENTER XR button centering and improve preloader styling
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 12:10:12 -06:00
1528f54472 Add ENTER XR button to preloader with level info display
- Replace automatic XR entry with user-triggered ENTER XR button
- Display level name, difficulty, and mission brief during loading
- Add VR availability check with "VR not available" error for desktop
- Add deep link protection - redirect locked levels to level select
- Extract XR entry logic to xrEntryHandler.ts for code organization
- Refactor levelSelectedHandler.ts from 206 to 150 lines

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 11:51:33 -06:00
4ce5f5f2de updated scoreboard. 2025-12-01 10:54:09 -06:00
174 changed files with 674601 additions and 1370 deletions

265
LEVEL_TRANSITION.md Normal file
View File

@ -0,0 +1,265 @@
# Immersive Level Progression Plan
## Overview
Add the ability for players to progress to the next level while staying in VR/immersive mode. This includes a "NEXT LEVEL" button on the status screen, fade-to-black transition, proper cleanup, and mission brief display.
## User Requirements
- Show mission brief for the new level
- Use fade-to-black transition (smoother UX)
- Reset all game statistics for each level
- Maintain deep-link reload capability
---
## Implementation Approach
### Architecture Decision: Create LevelTransitionManager
Create a new singleton class rather than adding to existing code because:
1. Keeps transition logic isolated (~90 lines)
2. Avoids bloating statusScreen.ts (already 700 lines)
3. Single responsibility principle
4. Follows existing patterns (InputControlManager, ProgressionManager)
---
## Phase 1: Create VR Fade Effect
**New file: `src/ui/effects/vrFadeEffect.ts`** (~60 lines)
For VR, use a black sphere that surrounds the XR camera (2D post-process won't work correctly for stereo rendering):
- Create small sphere (0.5m diameter) parented to XR camera
- Material: pure black, `backFaceCulling = false` (see inside)
- `renderingGroupId = 3` (render in front of everything)
- Animate alpha 0→1 for fadeOut, 1→0 for fadeIn
- Use BabylonJS Animation class with 500ms duration
---
## Phase 2: Create Level Transition Manager
**New file: `src/core/levelTransitionManager.ts`** (~90 lines)
Singleton that orchestrates the transition sequence:
```
transitionToLevel(nextLevelSlug):
1. Initialize fade sphere (parent to XR camera)
2. fadeOut(500ms) - screen goes black
3. currentLevel.dispose() - cleanup all entities
4. RockFactory.reset() then init() - reset asteroid factory
5. Get new level config from LevelRegistry
6. Create new Level1(config, audioEngine, false, levelSlug)
7. newLevel.initialize() - creates ship, asteroids, etc.
8. newLevel.setupXRCamera() - re-parent XR camera to new ship
9. fadeIn(500ms) - reveal new scene
10. newLevel.showMissionBrief() - show objectives
11. (Player clicks START on mission brief)
12. newLevel.startGameplay() - timer begins
```
Key considerations:
- Store reference to audioEngine (passed once, reused)
- Store reference to currentLevel (updated on transition)
- XR session stays active throughout
- Physics engine stays active (bodies are disposed, not engine)
---
## Phase 3: Enable NEXT LEVEL Button on StatusScreen
**File: `src/ui/hud/statusScreen.ts`**
### 3a. Uncomment button code (lines 248-262)
Currently commented out NEXT LEVEL button exists. Uncomment and add hover effect:
```typescript
this._nextLevelButton = Button.CreateSimpleButton("nextLevelButton", "NEXT LEVEL");
this._nextLevelButton.width = "300px";
this._nextLevelButton.height = "60px";
this._nextLevelButton.color = "white";
this._nextLevelButton.background = "#0088ff";
this._nextLevelButton.cornerRadius = 10;
this._nextLevelButton.thickness = 0;
this._nextLevelButton.fontSize = "30px";
this._nextLevelButton.fontWeight = "bold";
addButtonHoverEffect(this._nextLevelButton, "#0088ff", "#00aaff"); // ADD THIS
this._nextLevelButton.onPointerClickObservable.add(() => {
if (this._onNextLevelCallback) {
this._onNextLevelCallback();
}
});
buttonBar.addControl(this._nextLevelButton);
```
### 3b. Verify visibility logic (around line 474)
Existing logic should handle button visibility on victory:
```typescript
if (this._nextLevelButton) {
this._nextLevelButton.isVisible = isGameEnded && victory && hasNextLevel;
}
```
Ensure `hasNextLevel` is properly checked using ProgressionManager.getNextLevel().
---
## Phase 4: Modify Ship.handleNextLevel()
**File: `src/ship/ship.ts`** (lines 523-528)
Change from page reload to using LevelTransitionManager:
```typescript
private async handleNextLevel(): Promise<void> {
log.debug('Next Level button clicked - transitioning to next level');
const { ProgressionManager } = await import('../game/progression');
const progression = ProgressionManager.getInstance();
const nextLevel = progression.getNextLevel();
if (nextLevel) {
const { LevelTransitionManager } = await import('../core/levelTransitionManager');
const transitionManager = LevelTransitionManager.getInstance();
await transitionManager.transitionToLevel(nextLevel);
}
}
```
---
## Phase 5: Wire Up LevelTransitionManager
**File: `src/core/handlers/levelSelectedHandler.ts`** or `src/main.ts`
After level is created, register it with the transition manager:
```typescript
import { LevelTransitionManager } from './core/levelTransitionManager';
// After level creation:
const transitionManager = LevelTransitionManager.getInstance();
transitionManager.setAudioEngine(audioEngine);
transitionManager.setCurrentLevel(level);
```
---
## Cleanup Details
### Gap Found: Sun, Planets, Asteroids Not Tracked
**Issue**: In `Level1.initialize()` (line 393), the comment says "sun and planets are already created by deserializer" but they are NOT stored as instance variables. This means they won't be disposed when switching levels.
**Fix Required**: Add new instance variables and dispose them:
```typescript
// Add to Level1 class properties:
private _sun: AbstractMesh | null = null;
private _planets: AbstractMesh[] = [];
private _asteroids: AbstractMesh[] = [];
// In initialize(), store the returned entities:
this._sun = entities.sun;
this._planets = entities.planets;
this._asteroids = entities.asteroids;
// In dispose(), add cleanup:
if (this._sun) this._sun.dispose();
this._planets.forEach(p => p.dispose());
this._asteroids.forEach(a => { if (!a.isDisposed()) a.dispose(); });
```
### What gets disposed (via Level1.dispose()):
- `_startBase` - landing base mesh
- `_endBase` - end base mesh (if exists)
- `_sun` - **NEW** sun mesh
- `_planets` - **NEW** planet meshes array
- `_asteroids` - **NEW** asteroid meshes (may already be destroyed in gameplay)
- `_backgroundStars` - particle system
- `_missionBrief` - UI overlay
- `_hintSystem` - audio hints
- `_ship` - cascades to: physics body, controllers, audio, weapons, statusScreen, scoreboard
- `_backgroundMusic` - audio
### What stays active:
- XR session
- XR camera (re-parented to new ship)
- Audio engine
- Main scene
- Physics engine (bodies disposed, engine stays)
- Render loop
### RockFactory reset:
- `RockFactory.reset()` clears static asteroid mesh references
- `RockFactory.init()` reloads base asteroid models
- Ensures fresh asteroid creation for new level
---
## Critical Files to Modify
| File | Changes |
|------|---------|
| `src/ui/effects/vrFadeEffect.ts` | CREATE - VR fade sphere effect (~60 lines) |
| `src/core/levelTransitionManager.ts` | CREATE - Transition orchestration (~90 lines) |
| `src/ui/hud/statusScreen.ts` | MODIFY - Uncomment NEXT LEVEL button, add hover effect |
| `src/ship/ship.ts` | MODIFY - Update handleNextLevel() to use transition manager |
| `src/levels/level1.ts` | MODIFY - Add _sun, _planets, _asteroids properties and dispose them |
| `src/main.ts` or handler | MODIFY - Wire up transition manager on level creation |
---
## Transition Sequence Diagram
```
[Victory] → StatusScreen shows NEXT LEVEL button
Player clicks NEXT LEVEL
Ship.handleNextLevel()
LevelTransitionManager.transitionToLevel()
VRFadeEffect.fadeOut(500ms) ← Screen goes black
Level1.dispose() ← All entities cleaned up
RockFactory.reset() + init()
new Level1(newConfig) ← New level created
Level1.initialize() ← Asteroids, ship, bases created
Level1.setupXRCamera() ← Camera re-parented
VRFadeEffect.fadeIn(500ms) ← Scene revealed
Level1.showMissionBrief() ← Objectives displayed
Player clicks START
Level1.startGameplay() ← Timer starts, gameplay begins
```
---
## Testing Checklist
1. NEXT LEVEL button appears only on victory when next level exists and is unlocked
2. Clicking button starts fade transition
3. XR session remains active throughout
4. Old level entities fully disposed (no memory leaks)
5. New level loads with correct configuration
6. XR camera re-parents to new ship correctly
7. Mission brief displays for new level
8. GameStats reset (time starts at 0:00)
9. Ship status (fuel/hull/ammo) reset to full
10. Deep-link reload still works (page refresh loads correct level)
11. Scoreboard shows correct asteroid count
12. Physics bodies cleaned up (no orphaned bodies)
13. Audio continues working (background music, effects)
14. Controllers work on new ship

3
bjsEditorPlugin/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules/
dist/
*.log

50
bjsEditorPlugin/README.md Normal file
View File

@ -0,0 +1,50 @@
# Space Game BabylonJS Editor Plugin
Export BabylonJS Editor scenes to Space Game LevelConfig format.
## Installation
1. Build the plugin:
```bash
cd bjsEditorPlugin
npm install
npm run build
```
2. Install in BabylonJS Editor:
- Open BabylonJS Editor
- Edit → Preferences → Plugins
- Click "Add" and select this folder
- Apply and restart Editor
## Usage
1. Create a workspace in BabylonJS Editor
2. Copy the script components from `editorScripts/` to your workspace's `src/scenes/` folder
3. Place meshes in your scene and attach the appropriate scripts:
- `AsteroidComponent` - for asteroids
- `ShipComponent` - for player spawn point
- `SunComponent` - for the sun
- `PlanetComponent` - for planets
- `TargetComponent` - for orbit/movement targets
4. Configure properties in the Inspector panel
5. Space Game → Export Level Config...
## Script Components
The `editorScripts/` folder contains TypeScript components to use in your Editor workspace.
These expose game-specific properties (velocities, targets, etc.) in the Inspector.
## Plugin Menu
- **Export Level Config...** - Downloads level.json file
- **Export to Clipboard** - Copies JSON to clipboard
## Development
```bash
npm run watch # Watch mode for development
npm run build # Production build
```
Debug in Editor: CTRL+ALT+i to open DevTools, F5 to reload plugin.

View File

@ -0,0 +1,30 @@
/**
* BabylonJS Editor script component for asteroids
* Copy this to your Editor workspace: src/scenes/scripts/AsteroidComponent.ts
*
* Attach to asteroid meshes to expose game properties in Inspector.
*/
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import {
visibleAsNumber,
visibleAsString,
visibleAsVector3,
} from "babylonjs-editor-tools";
export default class AsteroidComponent extends Mesh {
@visibleAsVector3("Linear Velocity", { step: 0.1 })
public linearVelocity = { x: 0, y: 0, z: 0 };
@visibleAsVector3("Angular Velocity", { step: 0.01 })
public angularVelocity = { x: 0, y: 0, z: 0 };
@visibleAsNumber("Mass", { min: 1, max: 1000, step: 10 })
public mass: number = 200;
@visibleAsString("Target ID", { description: "Reference to a TargetComponent node" })
public targetId: string = "";
@visibleAsString("Target Mode", { description: "orbit | moveToward | (empty)" })
public targetMode: string = "";
}

View File

@ -0,0 +1,17 @@
/**
* BabylonJS Editor script component for the start base
* Copy this to your Editor workspace: src/scenes/scripts/BaseComponent.ts
*
* Attach to a mesh to mark it as the start base (yellow cylinder constraint zone).
*/
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { visibleAsString } from "babylonjs-editor-tools";
export default class BaseComponent extends Mesh {
@visibleAsString("Base GLB Path", { description: "Path to base GLB model" })
public baseGlbPath: string = "base.glb";
@visibleAsString("Landing GLB Path", { description: "Path to landing zone GLB" })
public landingGlbPath: string = "";
}

View File

@ -0,0 +1,17 @@
/**
* BabylonJS Editor script component for planets
* Copy this to your Editor workspace: src/scenes/scripts/PlanetComponent.ts
*
* Attach to a mesh to configure planet properties.
*/
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { visibleAsNumber, visibleAsString } from "babylonjs-editor-tools";
export default class PlanetComponent extends Mesh {
@visibleAsNumber("Diameter", { min: 10, max: 1000, step: 10 })
public diameter: number = 100;
@visibleAsString("Texture Path", { description: "Path to planet texture" })
public texturePath: string = "";
}

View File

@ -0,0 +1,17 @@
/**
* BabylonJS Editor script component for player ship spawn
* Copy this to your Editor workspace: src/scenes/scripts/ShipComponent.ts
*
* Attach to a mesh/transform node to mark player spawn point.
*/
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { visibleAsVector3 } from "babylonjs-editor-tools";
export default class ShipComponent extends Mesh {
@visibleAsVector3("Start Velocity", { step: 0.1 })
public linearVelocity = { x: 0, y: 0, z: 0 };
@visibleAsVector3("Start Angular Vel", { step: 0.01 })
public angularVelocity = { x: 0, y: 0, z: 0 };
}

View File

@ -0,0 +1,17 @@
/**
* BabylonJS Editor script component for the sun
* Copy this to your Editor workspace: src/scenes/scripts/SunComponent.ts
*
* Attach to a mesh to mark it as the sun. Position from transform.
*/
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { visibleAsNumber } from "babylonjs-editor-tools";
export default class SunComponent extends Mesh {
@visibleAsNumber("Diameter", { min: 10, max: 200, step: 5 })
public diameter: number = 50;
@visibleAsNumber("Intensity", { min: 0, max: 5000000, step: 100000 })
public intensity: number = 1000000;
}

View File

@ -0,0 +1,15 @@
/**
* BabylonJS Editor script component for orbit/movement targets
* Copy this to your Editor workspace: src/scenes/scripts/TargetComponent.ts
*
* Attach to a TransformNode to create an invisible target point.
* Asteroids can reference this by targetId to orbit or move toward.
*/
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { visibleAsString } from "babylonjs-editor-tools";
export default class TargetComponent extends TransformNode {
@visibleAsString("Display Name", { description: "Friendly name for this target" })
public displayName: string = "Target";
}

1272
bjsEditorPlugin/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
{
"name": "babylonjs-editor-space-game-plugin",
"version": "1.0.0",
"description": "Export BabylonJS Editor scenes to Space Game LevelConfig format",
"main": "dist/index.js",
"scripts": {
"build": "tsc && node -e \"const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json'));p.main='index.js';fs.writeFileSync('dist/package.json',JSON.stringify(p,null,2))\"",
"watch": "tsc -w"
},
"dependencies": {
"@auth0/auth0-spa-js": "^2.1.3",
"@supabase/supabase-js": "^2.45.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"@babylonjs/core": "^6.0.0"
},
"peerDependencies": {
"babylonjs-editor": "^4.0.0"
}
}

View File

@ -0,0 +1,37 @@
/**
* Camera speed persistence and application
*/
import { showNotification } from "./utils";
const CAMERA_SPEED_KEY = "space-game-camera-speed";
const DEFAULT_CAMERA_SPEED = 1;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let editorRef: any = null;
export function initCameraSpeed(editor: any): void {
editorRef = editor;
applyCameraSpeed(getSavedCameraSpeed());
}
export function getSavedCameraSpeed(): number {
const saved = localStorage.getItem(CAMERA_SPEED_KEY);
return saved ? parseFloat(saved) : DEFAULT_CAMERA_SPEED;
}
export function saveCameraSpeed(speed: number): void {
localStorage.setItem(CAMERA_SPEED_KEY, String(speed));
}
export function applyCameraSpeed(speed: number): void {
const camera = editorRef?.layout?.preview?.camera;
if (camera) {
camera.speed = speed;
}
}
export function handleCameraSpeedChange(speed: number): void {
saveCameraSpeed(speed);
applyCameraSpeed(speed);
showNotification(`Camera speed set to ${speed}`);
}

View File

@ -0,0 +1,149 @@
/**
* Handlers for cloud level browsing and loading
*/
import { getOfficialLevels, getMyLevels, saveLevel, CloudLevelEntry } from "./services/pluginLevelService";
import { showLevelBrowserModal, closeLevelBrowserModal } from "./levelBrowser/levelBrowserModal";
import { showSaveLevelModal, closeSaveLevelModal } from "./levelBrowser/saveLevelModal";
import { updateAuthSection } from "./levelBrowser/authStatus";
import { importLevelConfig } from "./levelImporter";
import { exportLevelConfig } from "./exporter";
import { showNotification } from "./utils";
import { isAuthenticated } from "./services/pluginAuth";
import { Scene } from "@babylonjs/core/scene";
let sceneGetter: () => Scene | null = () => null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let editorRef: any = null;
export function initCloudHandlers(getScene: () => Scene | null, editor: unknown): void {
sceneGetter = getScene;
editorRef = editor;
}
export async function handleBrowseOfficial(): Promise<void> {
try {
showNotification("Loading levels...");
const levels = await getOfficialLevels();
showLevelBrowserModal(levels, "Official Levels", {
onSelectLevel: handleLoadLevel,
onClose: closeLevelBrowserModal,
});
} catch (err) {
console.error("Browse official error:", err);
showNotification("Failed to fetch levels", true);
}
}
export async function handleBrowseMyLevels(): Promise<void> {
if (!isAuthenticated()) {
showNotification("Sign in to view your levels", true);
return;
}
try {
showNotification("Loading levels...");
const levels = await getMyLevels();
showLevelBrowserModal(levels, "My Levels", {
onSelectLevel: handleLoadLevel,
onClose: closeLevelBrowserModal,
onSaveNew: handleSaveNewLevel,
onSaveExisting: handleSaveExistingLevel,
});
} catch (err) {
console.error("Browse my levels error:", err);
showNotification("Failed to fetch levels", true);
}
}
export function handleAuthChange(): void {
const authSection = document.getElementById("auth-status-section");
if (authSection) {
updateAuthSection(authSection, handleAuthChange);
}
}
function handleLoadLevel(level: CloudLevelEntry): void {
const scene = sceneGetter();
if (!scene) {
showNotification("No scene loaded", true);
return;
}
try {
closeLevelBrowserModal();
showNotification(`Loading: ${level.name}...`);
importLevelConfig(scene, level.config, () => {
refreshEditorGraph();
showNotification(`Loaded: ${level.name}`);
});
} catch (err) {
console.error("Import error:", err);
showNotification("Failed to import level", true);
}
}
function handleSaveNewLevel(): void {
closeLevelBrowserModal();
showSaveLevelModal(
async ({ name, difficulty }) => {
const scene = sceneGetter();
if (!scene) {
showNotification("No scene loaded", true);
closeSaveLevelModal();
return;
}
try {
showNotification("Saving level...");
const configJson = exportLevelConfig(scene);
const config = JSON.parse(configJson);
const levelId = await saveLevel(name, difficulty, config);
closeSaveLevelModal();
if (levelId) {
showNotification(`Saved: ${name}`);
} else {
showNotification("Failed to save level", true);
}
} catch (err) {
console.error("Save error:", err);
showNotification("Failed to save level", true);
closeSaveLevelModal();
}
},
closeSaveLevelModal
);
}
async function handleSaveExistingLevel(level: CloudLevelEntry): Promise<void> {
const scene = sceneGetter();
if (!scene) {
showNotification("No scene loaded", true);
return;
}
try {
showNotification(`Saving ${level.name}...`);
const configJson = exportLevelConfig(scene);
const config = JSON.parse(configJson);
const result = await saveLevel(level.name, level.difficulty, config, level.id);
if (result) {
showNotification(`Saved: ${level.name}`);
} else {
showNotification("Failed to save level", true);
}
} catch (err) {
console.error("Save error:", err);
showNotification("Failed to save level", true);
}
}
function refreshEditorGraph(): void {
try {
editorRef?.layout?.graph?.refresh?.();
editorRef?.layout?.preview?.forceUpdate?.();
} catch (err) {
console.warn("Could not refresh editor graph:", err);
}
}

View File

@ -0,0 +1,10 @@
/**
* Plugin configuration - hardcoded values for editor context
* These are public/client-safe values (appear in browser bundles)
*/
export const PLUGIN_CONFIG = {
SUPABASE_URL: "https://ezipploqzuphwsptvvdv.supabase.co",
SUPABASE_ANON_KEY:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImV6aXBwbG9xenVwaHdzcHR2dmR2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQxMDk2NTYsImV4cCI6MjA3OTY4NTY1Nn0.CjpAh8v0c54KAYCPuLmrgrcHFAOVRxOEQCW8zZ9lwzA",
WEBSITE_URL: "https://www.flatearthdefense.com",
};

View File

@ -0,0 +1,54 @@
/**
* Builds AsteroidConfig[] from meshes with AsteroidComponent
*/
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { AsteroidConfig, Vector3Array } from "../types";
import { getScriptValues } from "../scriptUtils";
export function buildAsteroidConfigs(meshes: AbstractMesh[]): AsteroidConfig[] {
return meshes.map((mesh, index) => buildSingleAsteroid(mesh, index));
}
function buildSingleAsteroid(mesh: AbstractMesh, index: number): AsteroidConfig {
const script = getScriptValues(mesh);
const rotation = toVector3Array(mesh.rotation);
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
// Debug: compare local vs absolute position
const localPos = mesh.position;
const absPos = mesh.getAbsolutePosition();
if (Math.abs(localPos.x - absPos.x) > 1 || Math.abs(localPos.y - absPos.y) > 1 || Math.abs(localPos.z - absPos.z) > 1) {
console.warn(`[AsteroidBuilder] Position mismatch for ${mesh.name}:`,
`local=(${localPos.x.toFixed(1)}, ${localPos.y.toFixed(1)}, ${localPos.z.toFixed(1)})`,
`absolute=(${absPos.x.toFixed(1)}, ${absPos.y.toFixed(1)}, ${absPos.z.toFixed(1)})`,
`parent=${mesh.parent?.name || 'none'}`);
}
return {
id: mesh.name || `asteroid-${index}`,
position: toVector3Array(mesh.getAbsolutePosition()), // Use absolute position
rotation: hasRotation ? rotation : undefined,
scale: mesh.scaling.x,
linearVelocity: extractVector3(script.linearVelocity, [0, 0, 0]),
angularVelocity: extractVector3(script.angularVelocity, [0, 0, 0]),
mass: (script.mass as number) ?? 200,
targetId: (script.targetId as string) || undefined,
targetMode: parseTargetMode(script.targetMode as string)
};
}
function toVector3Array(v: { x: number; y: number; z: number }): Vector3Array {
return [v.x, v.y, v.z];
}
function extractVector3(v: unknown, defaultVal: Vector3Array): Vector3Array {
if (!v) return defaultVal;
if (Array.isArray(v)) return v as Vector3Array;
const vec = v as { x?: number; y?: number; z?: number };
return [vec.x ?? 0, vec.y ?? 0, vec.z ?? 0];
}
function parseTargetMode(mode: string): 'orbit' | 'moveToward' | undefined {
if (mode === 'orbit' || mode === 'moveToward') return mode;
return undefined;
}

View File

@ -0,0 +1,51 @@
/**
* Builds StartBaseConfig from mesh with BaseComponent
*/
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { StartBaseConfig, Vector3Array } from "../types";
import { getScriptValues } from "../scriptUtils";
export function buildBaseConfig(mesh: AbstractMesh | null): StartBaseConfig | undefined {
if (!mesh) {
return undefined;
}
const script = getScriptValues(mesh);
const glbPath = extractGlbPath(mesh, script);
const rotation = toVector3Array(mesh.rotation);
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
return {
position: toVector3Array(mesh.getAbsolutePosition()),
rotation: hasRotation ? rotation : undefined,
baseGlbPath: glbPath || undefined,
landingGlbPath: (script.landingGlbPath as string) || undefined,
};
}
function extractGlbPath(mesh: AbstractMesh, script: Record<string, unknown>): string | null {
// 1. Check script property first (manual override)
if (script.baseGlbPath) return script.baseGlbPath as string;
// 2. Check mesh metadata for source file path
const meta = mesh.metadata as Record<string, any> | undefined;
if (meta?.sourcePath) return extractFilename(meta.sourcePath);
if (meta?.gltf?.sourcePath) return extractFilename(meta.gltf.sourcePath);
// 3. Derive from mesh name if it looks like a GLB reference
const name = mesh.name || "";
if (name.endsWith(".glb") || name.endsWith(".gltf")) return name;
// 4. Check if name contains path separator and extract filename
if (name.includes("/")) return extractFilename(name);
return null;
}
function extractFilename(path: string): string {
return path.split("/").pop() || path;
}
function toVector3Array(v: { x: number; y: number; z: number }): Vector3Array {
return [v.x, v.y, v.z];
}

View File

@ -0,0 +1,31 @@
/**
* Builds PlanetConfig[] from meshes with PlanetComponent
*/
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { PlanetConfig, Vector3Array } from "../types";
import { getScriptValues } from "../scriptUtils";
export function buildPlanetConfigs(meshes: AbstractMesh[]): PlanetConfig[] {
return meshes.map(buildSinglePlanet);
}
function buildSinglePlanet(mesh: AbstractMesh): PlanetConfig {
const script = getScriptValues(mesh);
return {
name: mesh.name || "planet",
position: toVector3Array(mesh.getAbsolutePosition()),
diameter: (script.diameter as number) ?? 100,
texturePath: (script.texturePath as string) || "planet_texture.jpg",
rotation: hasRotation(mesh) ? toVector3Array(mesh.rotation) : undefined
};
}
function toVector3Array(v: { x: number; y: number; z: number }): Vector3Array {
return [v.x, v.y, v.z];
}
function hasRotation(mesh: AbstractMesh): boolean {
const r = mesh.rotation;
return r.x !== 0 || r.y !== 0 || r.z !== 0;
}

View File

@ -0,0 +1,38 @@
/**
* Builds ShipConfig from mesh with ShipComponent
*/
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { ShipConfig, Vector3Array } from "../types";
import { getScriptValues } from "../scriptUtils";
export function buildShipConfig(mesh: AbstractMesh | null): ShipConfig {
if (!mesh) {
return { position: [0, 1, 0] };
}
const script = getScriptValues(mesh);
return {
position: toVector3Array(mesh.getAbsolutePosition()),
rotation: mesh.rotation ? toVector3Array(mesh.rotation) : undefined,
linearVelocity: extractVector3OrUndefined(script.linearVelocity),
angularVelocity: extractVector3OrUndefined(script.angularVelocity)
};
}
function toVector3Array(v: { x: number; y: number; z: number }): Vector3Array {
return [v.x, v.y, v.z];
}
function extractVector3OrUndefined(v: unknown): Vector3Array | undefined {
if (!v) return undefined;
if (Array.isArray(v)) {
const arr = v as number[];
if (arr[0] === 0 && arr[1] === 0 && arr[2] === 0) return undefined;
return arr as Vector3Array;
}
const vec = v as { x?: number; y?: number; z?: number };
const arr: Vector3Array = [vec.x ?? 0, vec.y ?? 0, vec.z ?? 0];
if (arr[0] === 0 && arr[1] === 0 && arr[2] === 0) return undefined;
return arr;
}

View File

@ -0,0 +1,39 @@
/**
* Builds SunConfig from mesh with SunComponent
*/
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { SunConfig, Vector3Array } from "../types";
import { getScriptValues } from "../scriptUtils";
const DEFAULT_SUN: SunConfig = {
position: [0, 0, 400],
diameter: 50
};
export function buildSunConfig(mesh: AbstractMesh | null): SunConfig {
if (!mesh) {
return DEFAULT_SUN;
}
const script = getScriptValues(mesh);
const rotation = toVector3Array(mesh.rotation);
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
return {
position: toVector3Array(mesh.getAbsolutePosition()),
rotation: hasRotation ? rotation : undefined,
diameter: (script.diameter as number) ?? 50,
intensity: (script.intensity as number) ?? 1000000,
scale: hasNonUniformScale(mesh) ? toVector3Array(mesh.scaling) : undefined
};
}
function toVector3Array(v: { x: number; y: number; z: number }): Vector3Array {
return [v.x, v.y, v.z];
}
function hasNonUniformScale(mesh: AbstractMesh): boolean {
const s = mesh.scaling;
return s.x !== s.y || s.y !== s.z;
}

View File

@ -0,0 +1,24 @@
/**
* Builds TargetConfig[] from TransformNodes with TargetComponent
*/
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { TargetConfig, Vector3Array } from "../types";
import { getScriptValues } from "../scriptUtils";
export function buildTargetConfigs(nodes: TransformNode[]): TargetConfig[] {
return nodes.map(buildSingleTarget);
}
function buildSingleTarget(node: TransformNode): TargetConfig {
const script = getScriptValues(node);
return {
id: node.name || node.id,
name: (script.displayName as string) || node.name || "Target",
position: toVector3Array(node.getAbsolutePosition())
};
}
function toVector3Array(v: { x: number; y: number; z: number }): Vector3Array {
return [v.x, v.y, v.z];
}

View File

@ -0,0 +1,40 @@
/**
* Main export orchestrator - builds LevelConfig from scene
*/
import { Scene } from "@babylonjs/core/scene";
import { LevelConfig } from "./types";
import { collectMeshesByComponent } from "./meshCollector";
import { buildAsteroidConfigs } from "./configBuilders/asteroidBuilder";
import { buildBaseConfig } from "./configBuilders/baseBuilder";
import { buildPlanetConfigs } from "./configBuilders/planetBuilder";
import { buildShipConfig } from "./configBuilders/shipBuilder";
import { buildSunConfig } from "./configBuilders/sunBuilder";
import { buildTargetConfigs } from "./configBuilders/targetBuilder";
export function exportLevelConfig(scene: Scene): string {
const meshes = collectMeshesByComponent(scene);
console.log(`[Exporter] Collected: ${meshes.asteroids.length} asteroids, ${meshes.targets.length} targets, ${meshes.planets.length} planets`);
if (meshes.targets.length > 0) {
console.log(`[Exporter] Target IDs: ${meshes.targets.map(t => t.name || t.id).join(', ')}`);
}
const config: LevelConfig = {
version: "1.0",
difficulty: "rookie",
timestamp: new Date().toISOString(),
metadata: {
author: "BabylonJS Editor",
description: "Exported from Editor"
},
ship: buildShipConfig(meshes.ship),
startBase: buildBaseConfig(meshes.base),
sun: buildSunConfig(meshes.sun),
targets: buildTargetConfigs(meshes.targets),
planets: buildPlanetConfigs(meshes.planets),
asteroids: buildAsteroidConfigs(meshes.asteroids),
useOrbitConstraints: true
};
return JSON.stringify(config, null, 2);
}

View File

@ -0,0 +1,93 @@
/**
* Floating UI panel for Space Game plugin
*/
import { createContent } from "./panelSections";
const PANEL_ID = "space-game-plugin-panel";
let panelElement: HTMLDivElement | null = null;
let isDragging = false;
let dragOffset = { x: 0, y: 0 };
export interface FloatingUICallbacks {
onExport: () => void;
onExportClipboard: () => void;
onApplyUniformScale: (scale: number) => void;
onCameraSpeedChange: (multiplier: number) => void;
getCameraSpeed: () => number;
onBrowseOfficial: () => void;
onBrowseMyLevels: () => void;
onAuthChange: () => void;
}
export async function createFloatingUI(callbacks: FloatingUICallbacks): Promise<void> {
if (document.getElementById(PANEL_ID)) return;
panelElement = document.createElement("div");
panelElement.id = PANEL_ID;
applyPanelStyles(panelElement);
const header = createHeader();
const content = await createContent(callbacks);
panelElement.appendChild(header);
panelElement.appendChild(content);
document.body.appendChild(panelElement);
setupDragHandlers(header);
}
export function destroyFloatingUI(): void {
document.getElementById(PANEL_ID)?.remove();
panelElement = null;
}
function applyPanelStyles(panel: HTMLDivElement): void {
Object.assign(panel.style, {
position: "fixed", top: "80px", right: "20px", width: "200px",
backgroundColor: "#1e1e1e", border: "1px solid #3c3c3c", borderRadius: "8px",
boxShadow: "0 4px 12px rgba(0,0,0,0.4)", zIndex: "10000",
fontFamily: "system-ui, -apple-system, sans-serif", fontSize: "13px",
color: "#e0e0e0", overflow: "hidden",
});
}
function createHeader(): HTMLDivElement {
const header = document.createElement("div");
Object.assign(header.style, {
padding: "8px 12px", backgroundColor: "#2d2d2d", borderBottom: "1px solid #3c3c3c",
cursor: "move", userSelect: "none", display: "flex", alignItems: "center", gap: "8px",
});
const icon = document.createElement("span");
icon.textContent = "\u{1F680}";
const title = document.createElement("span");
title.textContent = "Space Game";
title.style.fontWeight = "500";
header.append(icon, title);
return header;
}
function setupDragHandlers(header: HTMLDivElement): void {
header.addEventListener("mousedown", onDragStart);
document.addEventListener("mousemove", onDrag);
document.addEventListener("mouseup", onDragEnd);
}
function onDragStart(e: MouseEvent): void {
if (!panelElement) return;
isDragging = true;
const rect = panelElement.getBoundingClientRect();
dragOffset.x = e.clientX - rect.left;
dragOffset.y = e.clientY - rect.top;
}
function onDrag(e: MouseEvent): void {
if (!isDragging || !panelElement) return;
panelElement.style.left = `${e.clientX - dragOffset.x}px`;
panelElement.style.top = `${e.clientY - dragOffset.y}px`;
panelElement.style.right = "auto";
}
function onDragEnd(): void {
isDragging = false;
}

View File

@ -0,0 +1,99 @@
/**
* BabylonJS Editor plugin entry point
* Uses Editor v5 plugin API: main(), close(), title, description
*/
import { exportLevelConfig } from "./exporter";
import { createFloatingUI, destroyFloatingUI } from "./floatingUI";
import { initCameraSpeed, getSavedCameraSpeed, handleCameraSpeedChange } from "./cameraSpeed";
import { showNotification, copyToClipboard, downloadJson } from "./utils";
import { initAuth } from "./services/pluginAuth";
import { initCloudHandlers, handleBrowseOfficial, handleBrowseMyLevels, handleAuthChange } from "./cloudLevelHandlers";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Editor = any;
let editorInstance: Editor;
export const title = "Space Game";
export const description = "Export/Import Space Game level configurations";
export async function main(editor: Editor): Promise<void> {
editorInstance = editor;
// Expose for debugging in DevTools
(window as any).spaceGameEditor = editor;
(window as any).exportSpaceGameLevel = handleExport;
(window as any).exportSpaceGameLevelToClipboard = handleExportToClipboard;
initCameraSpeed(editor);
initCloudHandlers(getScene, editor);
initAuth().catch((err) => console.warn("Auth init failed:", err));
await createFloatingUI({
onExport: handleExport,
onExportClipboard: handleExportToClipboard,
onApplyUniformScale: handleApplyUniformScale,
onCameraSpeedChange: handleCameraSpeedChange,
getCameraSpeed: getSavedCameraSpeed,
onBrowseOfficial: handleBrowseOfficial,
onBrowseMyLevels: handleBrowseMyLevels,
onAuthChange: handleAuthChange,
});
console.log("Space Game plugin activated");
}
export function close(): void {
destroyFloatingUI();
console.log("Space Game plugin deactivated");
}
async function handleExport(): Promise<void> {
try {
const scene = getScene();
if (!scene) {
showNotification("No scene loaded", true);
return;
}
const json = exportLevelConfig(scene);
downloadJson(json, "level.json");
showNotification("Level exported!");
} catch (err) {
console.error("Export error:", err);
showNotification("Failed to export: " + (err as Error).message, true);
}
}
async function handleExportToClipboard(): Promise<void> {
try {
const scene = getScene();
if (!scene) {
showNotification("No scene loaded", true);
return;
}
const json = exportLevelConfig(scene);
await copyToClipboard(json);
showNotification("Copied to clipboard!");
} catch (err) {
console.error("Clipboard error:", err);
showNotification("Failed to copy: " + (err as Error).message, true);
}
}
function handleApplyUniformScale(scale: number): void {
const selected = editorInstance?.layout?.inspector?.state?.editedObject;
if (!selected?.scaling) {
showNotification("Select a mesh first", true);
return;
}
selected.scaling.x = scale;
selected.scaling.y = scale;
selected.scaling.z = scale;
showNotification(`Scale set to ${scale}`);
}
function getScene() {
const scene = editorInstance?.layout?.preview?.scene ?? editorInstance?.scene;
console.log("getScene called - editor:", editorInstance, "scene:", scene);
return scene;
}

View File

@ -0,0 +1,44 @@
/**
* Auth status display and login/logout buttons
*/
import { isAuthenticated, login, logout } from "../services/pluginAuth";
import { createButton } from "../uiComponents";
export function createAuthSection(onAuthChange: () => void): HTMLDivElement {
const section = document.createElement("div");
section.id = "auth-status-section";
section.style.marginBottom = "8px";
updateAuthSection(section, onAuthChange);
return section;
}
export function updateAuthSection(section: HTMLElement, onAuthChange: () => void): void {
section.innerHTML = "";
if (isAuthenticated()) {
const label = document.createElement("div");
label.textContent = "Signed in";
label.style.cssText = "font-size: 11px; color: #888; margin-bottom: 6px;";
const btn = createButton("Sign Out", async () => {
await logout();
onAuthChange();
});
btn.style.width = "100%";
btn.style.backgroundColor = "#6c757d";
section.appendChild(label);
section.appendChild(btn);
} else {
const btn = createButton("Sign In", async () => {
try {
await login();
onAuthChange();
} catch (err) {
console.error("Login failed:", err);
}
});
btn.style.width = "100%";
section.appendChild(btn);
}
}

View File

@ -0,0 +1,123 @@
/**
* Modal dialog for browsing and selecting levels
*/
import type { CloudLevelEntry } from "../services/pluginLevelService";
const MODAL_ID = "level-browser-modal";
interface ModalCallbacks {
onSelectLevel: (level: CloudLevelEntry) => void;
onClose: () => void;
onSaveNew?: () => void;
onSaveExisting?: (level: CloudLevelEntry) => void;
}
export function showLevelBrowserModal(levels: CloudLevelEntry[], title: string, callbacks: ModalCallbacks): void {
closeLevelBrowserModal();
const overlay = createOverlay(callbacks.onClose);
const modal = createModalContainer();
modal.appendChild(createHeader(title, callbacks.onClose));
modal.appendChild(createLevelList(levels, callbacks));
overlay.appendChild(modal);
document.body.appendChild(overlay);
}
export function closeLevelBrowserModal(): void {
document.getElementById(MODAL_ID)?.remove();
}
function createOverlay(onClose: () => void): HTMLDivElement {
const overlay = document.createElement("div");
overlay.id = MODAL_ID;
overlay.style.cssText =
"position:fixed;inset:0;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:10002";
overlay.addEventListener("click", (e) => { if (e.target === overlay) onClose(); });
return overlay;
}
function createModalContainer(): HTMLDivElement {
const modal = document.createElement("div");
modal.style.cssText =
"background:#1e1e1e;border-radius:8px;width:500px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 8px 32px rgba(0,0,0,0.5)";
return modal;
}
function createHeader(title: string, onClose: () => void): HTMLDivElement {
const header = document.createElement("div");
header.style.cssText = "padding:16px;border-bottom:1px solid #3c3c3c;display:flex;justify-content:space-between;align-items:center";
const titleEl = document.createElement("span");
titleEl.textContent = title;
titleEl.style.cssText = "font-weight:600;font-size:16px;color:#e0e0e0";
const closeBtn = document.createElement("button");
closeBtn.textContent = "\u2715";
closeBtn.style.cssText = "background:none;border:none;color:#888;font-size:18px;cursor:pointer;padding:4px 8px";
closeBtn.addEventListener("click", onClose);
header.append(titleEl, closeBtn);
return header;
}
function createLevelList(levels: CloudLevelEntry[], callbacks: ModalCallbacks): HTMLDivElement {
const container = document.createElement("div");
container.style.cssText = "display:flex;flex-direction:column;flex:1;max-height:450px";
const list = document.createElement("div");
list.style.cssText = "overflow-y:auto;flex:1";
if (levels.length === 0) {
const empty = document.createElement("div");
empty.style.cssText = "padding:32px;text-align:center;color:#888";
empty.textContent = "No levels found";
list.appendChild(empty);
} else {
for (const level of levels) {
list.appendChild(createLevelItem(level, callbacks));
}
}
container.appendChild(list);
if (callbacks.onSaveNew) {
container.appendChild(createFooter(callbacks.onSaveNew));
}
return container;
}
function createFooter(onSaveNew: () => void): HTMLDivElement {
const footer = document.createElement("div");
footer.style.cssText = "padding:12px 16px;border-top:1px solid #3c3c3c;display:flex;justify-content:center";
const saveBtn = document.createElement("button");
saveBtn.textContent = "+ Save Current Scene as New Level";
saveBtn.style.cssText = "padding:8px 16px;background:#4fc3f7;border:none;color:#000;border-radius:4px;cursor:pointer;font-weight:500";
saveBtn.addEventListener("click", onSaveNew);
footer.appendChild(saveBtn);
return footer;
}
function createLevelItem(level: CloudLevelEntry, callbacks: ModalCallbacks): HTMLDivElement {
const item = document.createElement("div");
item.style.cssText = "padding:12px 16px;border-bottom:1px solid #2d2d2d;display:flex;justify-content:space-between;align-items:center";
const info = document.createElement("div");
info.style.cssText = "cursor:pointer;flex:1";
info.innerHTML = `<div style="font-weight:500;color:#e0e0e0">${level.name}</div>
<div style="font-size:11px;color:#888;margin-top:4px">${level.difficulty} | ${level.levelType} | ${new Date(level.updatedAt).toLocaleDateString()}</div>`;
info.addEventListener("click", () => callbacks.onSelectLevel(level));
item.appendChild(info);
if (callbacks.onSaveExisting && level.levelType !== "official") {
const saveBtn = document.createElement("button");
saveBtn.textContent = "Save";
saveBtn.style.cssText = "padding:4px 12px;background:#28a745;border:none;color:#fff;border-radius:4px;cursor:pointer;font-size:12px;margin-left:8px";
saveBtn.addEventListener("click", (e) => {
e.stopPropagation();
callbacks.onSaveExisting!(level);
});
item.appendChild(saveBtn);
}
item.addEventListener("mouseenter", () => (item.style.backgroundColor = "#2d2d2d"));
item.addEventListener("mouseleave", () => (item.style.backgroundColor = "transparent"));
return item;
}

View File

@ -0,0 +1,109 @@
/**
* Modal for saving a level with name and difficulty
*/
const MODAL_ID = "save-level-modal";
interface SaveLevelData {
name: string;
difficulty: string;
}
export function showSaveLevelModal(
onSave: (data: SaveLevelData) => void,
onCancel: () => void
): void {
closeSaveLevelModal();
const overlay = createOverlay(onCancel);
const modal = createModalContainer();
modal.appendChild(createHeader(onCancel));
modal.appendChild(createForm(onSave, onCancel));
overlay.appendChild(modal);
document.body.appendChild(overlay);
}
export function closeSaveLevelModal(): void {
document.getElementById(MODAL_ID)?.remove();
}
function createOverlay(onCancel: () => void): HTMLDivElement {
const overlay = document.createElement("div");
overlay.id = MODAL_ID;
overlay.style.cssText =
"position:fixed;inset:0;background:rgba(0,0,0,0.7);display:flex;align-items:center;justify-content:center;z-index:10004";
overlay.addEventListener("click", (e) => { if (e.target === overlay) onCancel(); });
return overlay;
}
function createModalContainer(): HTMLDivElement {
const modal = document.createElement("div");
modal.style.cssText =
"background:#1e1e1e;border-radius:8px;width:400px;box-shadow:0 8px 32px rgba(0,0,0,0.5)";
return modal;
}
function createHeader(onCancel: () => void): HTMLDivElement {
const header = document.createElement("div");
header.style.cssText = "padding:16px;border-bottom:1px solid #3c3c3c;display:flex;justify-content:space-between";
const title = document.createElement("span");
title.textContent = "Save Level";
title.style.cssText = "font-weight:600;font-size:16px;color:#e0e0e0";
const closeBtn = document.createElement("button");
closeBtn.textContent = "\u2715";
closeBtn.style.cssText = "background:none;border:none;color:#888;font-size:18px;cursor:pointer;padding:0";
closeBtn.addEventListener("click", onCancel);
header.append(title, closeBtn);
return header;
}
function createForm(onSave: (data: SaveLevelData) => void, onCancel: () => void): HTMLDivElement {
const form = document.createElement("div");
form.style.cssText = "padding:20px";
// Name input
const nameLabel = document.createElement("label");
nameLabel.textContent = "Level Name";
nameLabel.style.cssText = "display:block;color:#aaa;font-size:12px;margin-bottom:4px";
const nameInput = document.createElement("input");
nameInput.type = "text";
nameInput.placeholder = "My Awesome Level";
nameInput.style.cssText = inputStyle();
// Difficulty select
const diffLabel = document.createElement("label");
diffLabel.textContent = "Difficulty";
diffLabel.style.cssText = "display:block;color:#aaa;font-size:12px;margin-bottom:4px;margin-top:12px";
const diffSelect = document.createElement("select");
diffSelect.style.cssText = inputStyle();
["recruit", "pilot", "captain", "commander"].forEach((d) => {
const opt = document.createElement("option");
opt.value = d;
opt.textContent = d.charAt(0).toUpperCase() + d.slice(1);
diffSelect.appendChild(opt);
});
// Buttons
const buttons = document.createElement("div");
buttons.style.cssText = "display:flex;gap:8px;margin-top:20px;justify-content:flex-end";
const cancelBtn = document.createElement("button");
cancelBtn.textContent = "Cancel";
cancelBtn.style.cssText = "padding:8px 16px;background:#6c757d;border:none;color:#fff;border-radius:4px;cursor:pointer";
cancelBtn.addEventListener("click", onCancel);
const saveBtn = document.createElement("button");
saveBtn.textContent = "Save Level";
saveBtn.style.cssText = "padding:8px 16px;background:#4fc3f7;border:none;color:#000;border-radius:4px;cursor:pointer;font-weight:500";
saveBtn.addEventListener("click", () => {
const name = nameInput.value.trim();
if (!name) { nameInput.focus(); return; }
onSave({ name, difficulty: diffSelect.value });
});
buttons.append(cancelBtn, saveBtn);
form.append(nameLabel, nameInput, diffLabel, diffSelect, buttons);
return form;
}
function inputStyle(): string {
return "width:100%;padding:8px;border:1px solid #3c3c3c;border-radius:4px;background:#2d2d2d;color:#e0e0e0;font-size:14px;box-sizing:border-box";
}

View File

@ -0,0 +1,95 @@
/**
* Modal for entering API token
* User gets token from website profile page
*/
const MODAL_ID = "token-entry-modal";
export function showTokenEntryModal(
profileUrl: string,
onSubmit: (token: string) => void,
onCancel: () => void
): void {
closeTokenEntryModal();
const overlay = createOverlay(onCancel);
const modal = createModalContainer();
modal.appendChild(createHeader(onCancel));
modal.appendChild(createContent(profileUrl, onSubmit, onCancel));
overlay.appendChild(modal);
document.body.appendChild(overlay);
}
export function closeTokenEntryModal(): void {
document.getElementById(MODAL_ID)?.remove();
}
function createOverlay(onCancel: () => void): HTMLDivElement {
const overlay = document.createElement("div");
overlay.id = MODAL_ID;
overlay.style.cssText =
"position:fixed;inset:0;background:rgba(0,0,0,0.7);display:flex;align-items:center;justify-content:center;z-index:10003";
overlay.addEventListener("click", (e) => { if (e.target === overlay) onCancel(); });
return overlay;
}
function createModalContainer(): HTMLDivElement {
const modal = document.createElement("div");
modal.style.cssText =
"background:#1e1e1e;border-radius:8px;width:420px;box-shadow:0 8px 32px rgba(0,0,0,0.5)";
return modal;
}
function createHeader(onCancel: () => void): HTMLDivElement {
const header = document.createElement("div");
header.style.cssText = "padding:16px;border-bottom:1px solid #3c3c3c;display:flex;justify-content:space-between";
const title = document.createElement("span");
title.textContent = "Sign In with Token";
title.style.cssText = "font-weight:600;font-size:16px;color:#e0e0e0";
const closeBtn = document.createElement("button");
closeBtn.textContent = "\u2715";
closeBtn.style.cssText = "background:none;border:none;color:#888;font-size:18px;cursor:pointer;padding:0";
closeBtn.addEventListener("click", onCancel);
header.append(title, closeBtn);
return header;
}
function createContent(
profileUrl: string,
onSubmit: (token: string) => void,
onCancel: () => void
): HTMLDivElement {
const content = document.createElement("div");
content.style.cssText = "padding:20px";
const instructions = document.createElement("p");
instructions.style.cssText = "color:#aaa;margin:0 0 16px;font-size:13px;line-height:1.5";
instructions.innerHTML = `
1. Visit your <a href="${profileUrl}" target="_blank" style="color:#81c784">profile page</a><br>
2. Generate an Editor Token<br>
3. Paste it below
`;
const input = document.createElement("textarea");
input.placeholder = "Paste your token here...";
input.style.cssText = `
width:100%;height:80px;padding:10px;border:1px solid #3c3c3c;border-radius:4px;
background:#2d2d2d;color:#e0e0e0;font-family:monospace;font-size:12px;resize:none;
box-sizing:border-box;
`;
const buttons = document.createElement("div");
buttons.style.cssText = "display:flex;gap:8px;margin-top:16px;justify-content:flex-end";
const cancelBtn = document.createElement("button");
cancelBtn.textContent = "Cancel";
cancelBtn.style.cssText = "padding:8px 16px;background:#6c757d;border:none;color:#fff;border-radius:4px;cursor:pointer";
cancelBtn.addEventListener("click", onCancel);
const submitBtn = document.createElement("button");
submitBtn.textContent = "Sign In";
submitBtn.style.cssText = "padding:8px 16px;background:#4fc3f7;border:none;color:#000;border-radius:4px;cursor:pointer;font-weight:500";
submitBtn.addEventListener("click", () => onSubmit(input.value));
buttons.append(cancelBtn, submitBtn);
content.append(instructions, input, buttons);
return content;
}

View File

@ -0,0 +1,82 @@
/**
* Imports LevelConfig into the editor scene
* Updates existing GLB meshes and creates asteroid instances
*/
import { Scene } from "@babylonjs/core/scene";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { InstancedMesh } from "@babylonjs/core/Meshes/instancedMesh";
import type { LevelConfig, AsteroidConfig } from "./types";
const SCRIPTS = {
asteroid: "scripts/editorScripts/AsteroidComponent.ts",
ship: "scripts/editorScripts/ShipComponent.ts",
};
export function importLevelConfig(scene: Scene, config: LevelConfig, onComplete?: () => void): void {
updateShip(scene, config);
updateBase(scene, config);
updateAsteroids(scene, config);
onComplete?.();
}
function updateShip(scene: Scene, config: LevelConfig): void {
const ship = findMeshByName(scene, "ship.glb") || findMeshByName(scene, "Ship");
if (!ship) { console.warn("Ship mesh not found"); return; }
ship.position = new Vector3(...config.ship.position);
if (config.ship.rotation) ship.rotation = new Vector3(...config.ship.rotation);
}
function updateBase(scene: Scene, config: LevelConfig): void {
if (!config.startBase) return;
const base = findMeshByName(scene, "base.glb");
if (!base) { console.warn("Base mesh not found"); return; }
if (config.startBase.position) base.position = new Vector3(...config.startBase.position);
if (config.startBase.rotation) base.rotation = new Vector3(...config.startBase.rotation);
}
function updateAsteroids(scene: Scene, config: LevelConfig): void {
const asteroidSource = findAsteroidSource(scene);
if (!asteroidSource) { console.warn("Asteroid source mesh not found"); return; }
clearAsteroidInstances(scene);
config.asteroids.forEach((a) => createAsteroidInstance(asteroidSource, a));
}
function findMeshByName(scene: Scene, name: string): Mesh | null {
return scene.meshes.find((m) => m.name === name) as Mesh | null;
}
function findAsteroidSource(scene: Scene): Mesh | null {
// Find the Asteroid mesh (not instances) - it's a child of asteroid.glb
const asteroidMesh = scene.meshes.find((m) =>
m.name === "Asteroid" && !(m instanceof InstancedMesh)
);
return asteroidMesh as Mesh | null;
}
function clearAsteroidInstances(scene: Scene): void {
// Clear all instances that have AsteroidComponent script attached
const instances = scene.meshes.filter((m) =>
m instanceof InstancedMesh && m.metadata?.scripts?.[0]?.key === SCRIPTS.asteroid
);
instances.forEach((inst) => inst.dispose());
}
function createAsteroidInstance(source: Mesh, a: AsteroidConfig): void {
const instance = source.createInstance(a.id);
instance.position = new Vector3(...a.position);
if (a.rotation) instance.rotation = new Vector3(...a.rotation);
instance.scaling = new Vector3(a.scale, a.scale, a.scale);
instance.metadata = {
scripts: [{
key: SCRIPTS.asteroid, enabled: true,
values: {
linearVelocity: { type: "vector3", value: a.linearVelocity },
angularVelocity: { type: "vector3", value: a.angularVelocity ?? [0, 0, 0] },
mass: { type: "number", value: a.mass ?? 200 },
targetId: { type: "string", value: a.targetId ?? "" },
targetMode: { type: "string", value: a.targetMode ?? "" },
},
}],
};
}

View File

@ -0,0 +1,73 @@
/**
* Collects meshes from scene grouped by their attached component type
*/
import { Scene } from "@babylonjs/core/scene";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { getScriptName } from "./scriptUtils";
export interface CollectedMeshes {
asteroids: AbstractMesh[];
planets: AbstractMesh[];
ship: AbstractMesh | null;
sun: AbstractMesh | null;
base: AbstractMesh | null;
targets: TransformNode[];
}
export function collectMeshesByComponent(scene: Scene): CollectedMeshes {
const result: CollectedMeshes = {
asteroids: [],
planets: [],
ship: null,
sun: null,
base: null,
targets: []
};
for (const mesh of scene.meshes) {
const scriptName = getScriptName(mesh);
categorizeByScript(scriptName, mesh, result);
}
collectTargetNodes(scene, result);
return result;
}
function categorizeByScript(
scriptName: string | null,
mesh: AbstractMesh,
result: CollectedMeshes
): void {
switch (scriptName) {
case "AsteroidComponent":
result.asteroids.push(mesh);
break;
case "PlanetComponent":
result.planets.push(mesh);
break;
case "ShipComponent":
result.ship = mesh;
break;
case "SunComponent":
result.sun = mesh;
break;
case "BaseComponent":
result.base = mesh;
break;
case "TargetComponent":
// Targets can be Mesh or TransformNode - handle both
result.targets.push(mesh as unknown as TransformNode);
break;
}
}
function collectTargetNodes(scene: Scene, result: CollectedMeshes): void {
for (const node of scene.transformNodes) {
if (getScriptName(node) === "TargetComponent") {
result.targets.push(node);
}
}
}

View File

@ -0,0 +1,72 @@
/**
* Panel section creators for the floating UI
*/
import { createButton, createNumberInput, createSeparator, createLabel, createRow } from "./uiComponents";
import { FloatingUICallbacks } from "./floatingUI";
import { createAuthSection } from "./levelBrowser/authStatus";
export async function createContent(callbacks: FloatingUICallbacks): Promise<HTMLDivElement> {
const content = document.createElement("div");
Object.assign(content.style, { padding: "12px", display: "flex", flexDirection: "column", gap: "8px" });
content.appendChild(createButton("Export Level", callbacks.onExport));
content.appendChild(createButton("Copy to Clipboard", callbacks.onExportClipboard));
content.appendChild(createSeparator());
content.appendChild(createScaleSection(callbacks.onApplyUniformScale));
content.appendChild(createSeparator());
content.appendChild(createCameraSpeedSection(callbacks));
content.appendChild(createSeparator());
content.appendChild(await createCloudLevelsSection(callbacks));
return content;
}
async function createCloudLevelsSection(callbacks: FloatingUICallbacks): Promise<HTMLDivElement> {
const section = document.createElement("div");
Object.assign(section.style, { display: "flex", flexDirection: "column", gap: "6px" });
section.appendChild(createLabel("Cloud Levels"));
section.appendChild(await createAuthSection(callbacks.onAuthChange));
const row = createRow();
const officialBtn = createButton("Official", callbacks.onBrowseOfficial);
const myBtn = createButton("My Levels", callbacks.onBrowseMyLevels);
officialBtn.style.flex = "1";
myBtn.style.flex = "1";
row.appendChild(officialBtn);
row.appendChild(myBtn);
section.appendChild(row);
return section;
}
function createScaleSection(onApply: (scale: number) => void): HTMLDivElement {
const row = createRow();
row.style.width = "100%";
const input = createNumberInput(1, "0.1", "0.1");
const btn = createButton("Scale", () => onApply(parseFloat(input.value) || 1));
btn.style.padding = "6px 12px";
btn.style.flexShrink = "0";
row.appendChild(input);
row.appendChild(btn);
return row;
}
function createCameraSpeedSection(callbacks: FloatingUICallbacks): HTMLDivElement {
const section = document.createElement("div");
Object.assign(section.style, { display: "flex", flexDirection: "column", gap: "6px" });
const row = createRow();
const input = createNumberInput(callbacks.getCameraSpeed(), "0.5", "0.5");
const btn = createButton("Set", () => callbacks.onCameraSpeedChange(parseFloat(input.value) || 1));
btn.style.padding = "6px 12px";
btn.style.flexShrink = "0";
row.appendChild(input);
row.appendChild(btn);
section.appendChild(createLabel("Camera Speed"));
section.appendChild(row);
return section;
}

View File

@ -0,0 +1,53 @@
/**
* Utilities for reading BabylonJS Editor script metadata
* Editor stores scripts in: mesh.metadata.scripts[].values[prop].value
*/
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
interface EditorScript {
key: string;
enabled?: boolean;
values?: Record<string, { type?: string; value?: unknown }>;
}
type NodeWithMetadata = AbstractMesh | TransformNode;
/**
* Extract component name from node's attached scripts
* e.g., "scenes/scripts/AsteroidComponent.ts" -> "AsteroidComponent"
*/
export function getScriptName(node: NodeWithMetadata): string | null {
const scripts = getScriptsArray(node);
if (!scripts.length) return null;
const script = scripts.find(s => s.enabled !== false);
if (!script?.key) return null;
const filename = script.key.split("/").pop() ?? "";
return filename.replace(/\.(ts|tsx|js)$/, "") || null;
}
/**
* Extract flattened property values from node's script
* Converts { prop: { value: x } } to { prop: x }
*/
export function getScriptValues(node: NodeWithMetadata): Record<string, unknown> {
const scripts = getScriptsArray(node);
if (!scripts.length) return {};
const script = scripts.find(s => s.enabled !== false);
if (!script?.values) return {};
const result: Record<string, unknown> = {};
for (const [key, data] of Object.entries(script.values)) {
result[key] = data?.value;
}
return result;
}
function getScriptsArray(node: NodeWithMetadata): EditorScript[] {
const metadata = node.metadata as { scripts?: EditorScript[] } | null;
const scripts = metadata?.scripts;
return Array.isArray(scripts) ? scripts : [];
}

View File

@ -0,0 +1,67 @@
/**
* Manual token-based auth for Electron plugin
* User generates token on website and pastes it here
*/
import { PLUGIN_CONFIG } from "../config";
import { showTokenEntryModal, closeTokenEntryModal } from "../levelBrowser/tokenEntryModal";
const STORAGE_KEY = "plugin_auth_token";
interface StoredAuth {
accessToken: string;
savedAt: number;
}
let currentAuth: StoredAuth | null = null;
export async function initAuth(): Promise<void> {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
currentAuth = JSON.parse(stored) as StoredAuth;
} catch { /* ignore invalid stored data */ }
}
}
export async function login(): Promise<void> {
return new Promise((resolve, reject) => {
showTokenEntryModal(
`${PLUGIN_CONFIG.WEBSITE_URL}/profile`,
(token: string) => {
if (token.trim()) {
storeAuth(token.trim());
closeTokenEntryModal();
resolve();
} else {
reject(new Error("No token provided"));
}
},
() => {
closeTokenEntryModal();
reject(new Error("Login cancelled"));
}
);
});
}
export async function logout(): Promise<void> {
currentAuth = null;
localStorage.removeItem(STORAGE_KEY);
}
export function isAuthenticated(): boolean {
return currentAuth !== null && !!currentAuth.accessToken;
}
export function getAccessToken(): string | undefined {
if (!currentAuth) return undefined;
return currentAuth.accessToken;
}
function storeAuth(token: string): void {
currentAuth = {
accessToken: token,
savedAt: Date.now(),
};
localStorage.setItem(STORAGE_KEY, JSON.stringify(currentAuth));
}

View File

@ -0,0 +1,101 @@
/**
* Level service for fetching levels from Supabase
*/
import { getSupabaseClient } from "./pluginSupabase";
import { getAccessToken } from "./pluginAuth";
import type { LevelConfig } from "../types";
export interface CloudLevelEntry {
id: string;
name: string;
description: string | null;
difficulty: string;
levelType: "official" | "private" | "pending_review" | "published" | "rejected";
config: LevelConfig;
updatedAt: string;
}
interface LevelRow {
id: string;
name: string;
description: string | null;
difficulty: string;
level_type: string;
config: LevelConfig | string;
updated_at: string;
}
function rowToEntry(row: LevelRow): CloudLevelEntry {
const config = typeof row.config === "string" ? JSON.parse(row.config) : row.config;
return {
id: row.id,
name: row.name,
description: row.description,
difficulty: row.difficulty,
levelType: row.level_type as CloudLevelEntry["levelType"],
config,
updatedAt: row.updated_at,
};
}
export async function getOfficialLevels(): Promise<CloudLevelEntry[]> {
const client = getSupabaseClient();
const { data, error } = await client
.from("levels")
.select("id, name, description, difficulty, level_type, config, updated_at")
.eq("level_type", "official")
.order("sort_order", { ascending: true });
if (error || !data) return [];
return data.map(rowToEntry);
}
export async function getPublishedLevels(): Promise<CloudLevelEntry[]> {
const { data, error } = await getSupabaseClient()
.from("levels")
.select("id, name, description, difficulty, level_type, config, updated_at")
.eq("level_type", "published")
.order("created_at", { ascending: false });
if (error || !data) return [];
return data.map(rowToEntry);
}
export async function getMyLevels(): Promise<CloudLevelEntry[]> {
const token = getAccessToken();
if (!token) return [];
const { data, error } = await getSupabaseClient()
.rpc("get_my_levels_by_token", { p_token: token });
if (error || !data) {
console.error("Failed to fetch my levels:", error);
return [];
}
return data.map(rowToEntry);
}
export async function saveLevel(
name: string,
difficulty: string,
config: LevelConfig,
levelId?: string
): Promise<string | null> {
const token = getAccessToken();
if (!token) return null;
const { data, error } = await getSupabaseClient()
.rpc("save_level_by_token", {
p_token: token,
p_name: name,
p_difficulty: difficulty,
p_config: config,
p_level_id: levelId || null,
});
if (error) {
console.error("Failed to save level:", error);
return null;
}
return data;
}

View File

@ -0,0 +1,14 @@
/**
* Lightweight Supabase client for plugin context
*/
import { createClient, SupabaseClient } from "@supabase/supabase-js";
import { PLUGIN_CONFIG } from "../config";
let client: SupabaseClient | null = null;
export function getSupabaseClient(): SupabaseClient {
if (!client) {
client = createClient(PLUGIN_CONFIG.SUPABASE_URL, PLUGIN_CONFIG.SUPABASE_ANON_KEY);
}
return client;
}

View File

@ -0,0 +1,70 @@
/**
* LevelConfig types - mirror of the game's level config schema
*/
export type Vector3Array = [number, number, number];
export interface ShipConfig {
position: Vector3Array;
rotation?: Vector3Array;
linearVelocity?: Vector3Array;
angularVelocity?: Vector3Array;
}
export interface SunConfig {
position: Vector3Array;
rotation?: Vector3Array;
diameter: number;
intensity?: number;
scale?: Vector3Array;
}
export interface StartBaseConfig {
position?: Vector3Array;
rotation?: Vector3Array;
baseGlbPath?: string;
landingGlbPath?: string;
}
export interface TargetConfig {
id: string;
name: string;
position: Vector3Array;
}
export interface PlanetConfig {
name: string;
position: Vector3Array;
diameter: number;
texturePath: string;
rotation?: Vector3Array;
}
export interface AsteroidConfig {
id: string;
position: Vector3Array;
rotation?: Vector3Array;
scale: number;
linearVelocity: Vector3Array;
angularVelocity?: Vector3Array;
mass?: number;
targetId?: string;
targetMode?: 'orbit' | 'moveToward';
}
export interface LevelConfig {
version: string;
difficulty: string;
timestamp?: string;
metadata?: {
author?: string;
description?: string;
[key: string]: unknown;
};
ship: ShipConfig;
startBase?: StartBaseConfig;
sun: SunConfig;
targets?: TargetConfig[];
planets: PlanetConfig[];
asteroids: AsteroidConfig[];
useOrbitConstraints?: boolean;
}

View File

@ -0,0 +1,65 @@
/**
* Reusable UI components for the floating panel
*/
export function createButton(text: string, onClick: () => void): HTMLButtonElement {
const btn = document.createElement("button");
btn.textContent = text;
Object.assign(btn.style, {
padding: "8px 12px",
backgroundColor: "#0d6efd",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
fontSize: "13px",
transition: "background-color 0.2s",
});
btn.addEventListener("mouseenter", () => (btn.style.backgroundColor = "#0b5ed7"));
btn.addEventListener("mouseleave", () => (btn.style.backgroundColor = "#0d6efd"));
btn.addEventListener("click", onClick);
return btn;
}
export function createNumberInput(value: number, step: string, min: string): HTMLInputElement {
const input = document.createElement("input");
input.type = "number";
input.value = String(value);
input.step = step;
input.min = min;
Object.assign(input.style, {
flex: "1",
minWidth: "0",
padding: "6px",
backgroundColor: "#2d2d2d",
border: "1px solid #3c3c3c",
borderRadius: "4px",
color: "#e0e0e0",
fontSize: "13px",
boxSizing: "border-box",
});
return input;
}
export function createSeparator(): HTMLDivElement {
const sep = document.createElement("div");
sep.style.borderTop = "1px solid #3c3c3c";
sep.style.margin = "4px 0";
return sep;
}
export function createLabel(text: string): HTMLLabelElement {
const label = document.createElement("label");
label.textContent = text;
label.style.fontSize = "12px";
label.style.color = "#aaa";
return label;
}
export function createRow(): HTMLDivElement {
const row = document.createElement("div");
Object.assign(row.style, { display: "flex", gap: "8px", alignItems: "center" });
return row;
}

View File

@ -0,0 +1,47 @@
/**
* Utility functions for notifications, clipboard, and file downloads
*/
export function showNotification(message: string, isError = false): void {
const el = document.createElement("div");
Object.assign(el.style, {
position: "fixed",
bottom: "20px",
right: "20px",
padding: "12px 20px",
backgroundColor: isError ? "#dc3545" : "#198754",
color: "white",
borderRadius: "6px",
zIndex: "10001",
fontSize: "13px",
boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
});
el.textContent = message;
document.body.appendChild(el);
setTimeout(() => el.remove(), 3000);
}
export async function copyToClipboard(text: string): Promise<void> {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
} else {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.opacity = "0";
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
}
}
export function downloadJson(content: string, filename: string): void {
const blob = new Blob([content], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,3 @@
# Babylon.js Editor
This folder contains all the assets used in the project.

Binary file not shown.

BIN
gameEditor/assets/base.glb Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -0,0 +1,192 @@
{
"clearColor": [
0.2,
0.2,
0.3,
1
],
"ambientColor": [
0,
0,
0
],
"environment": {
"environmentIntensity": 1
},
"fog": {
"fogEnabled": false,
"fogMode": 0,
"fogStart": 10,
"fogEnd": 1000,
"fogDensity": 0.001,
"fogColor": [
0.640006735140596,
0.640006735140596,
0.640006735140596
]
},
"physics": {
"gravity": [
0,
-981,
0
]
},
"rendering": [
{
"cameraId": "3cc9ef80-8c60-43e0-bcc4-1b55ff36010d",
"ssao2RenderingPipeline": null,
"vlsPostProcess": null,
"ssrRenderingPipeline": null,
"motionBlurPostProcess": null,
"defaultRenderingPipeline": null,
"iblShadowsRenderPipeline": null
}
],
"metadata": {
"rendering": [],
"physicsGravity": [
0,
-981,
0
],
"scripts": [
{
"enabled": true,
"key": "scripts/editorScripts/AsteroidComponent.ts",
"values": {
"linearVelocity": {
"type": "vector3",
"value": [
0,
0,
0
]
},
"angularVelocity": {
"type": "vector3",
"value": [
0,
0,
0
]
},
"mass": {
"type": "number",
"value": 1
},
"targetId": {
"type": "string",
"description": "Reference to a TargetComponent node",
"value": ""
},
"targetMode": {
"type": "string",
"description": "orbit | moveToward | (empty)",
"value": ""
}
},
"_id": "fe3c8020-9375-4c58-a54f-852339e3f40f"
}
]
},
"editorCamera": {
"tags": null,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"checkCollisions": false,
"applyGravity": false,
"updateUpVectorFromRotation": false,
"rotation": [
0.23466138679888465,
0.48668946590326523,
0
],
"speed": 2000,
"position": [
-19666.901457088374,
17401.223711429564,
-55872.0430330369
],
"upVector": [
0,
1,
0
],
"fov": 0.8,
"projectionPlaneTilt": 0,
"minZ": 1,
"maxZ": 1500000,
"inertia": 0.5,
"mode": 0,
"layerMask": 268435455,
"fovMode": 0,
"cameraRigMode": 0,
"ignoreCameraMaxZ": false,
"name": "Editor preview",
"id": "3cc9ef80-8c60-43e0-bcc4-1b55ff36010d",
"state": "",
"metadata": {},
"type": "EditorCamera",
"inputsmgr": {
"FreeCameraKeyboardMoveInput": {
"tags": null,
"keysUp": [
87
],
"keysUpward": [
69
],
"keysDown": [
83
],
"keysDownward": [
81
],
"keysLeft": [
65
],
"keysRight": [
68
],
"rotationSpeed": 0.5,
"keysRotateLeft": [],
"keysRotateRight": [],
"keysRotateUp": [],
"keysRotateDown": []
},
"FreeCameraMouseInput": {
"tags": null,
"buttons": [
0,
1,
2
],
"angularSensibility": 500
},
"FreeCameraMouseWheelInput": {
"tags": null,
"wheelXMoveRelative": 0,
"wheelYMoveRelative": 2,
"wheelPrecisionX": 3,
"wheelPrecisionY": 3,
"wheelPrecisionZ": 3
},
"EditorFreeCameraPanInput": {
"tags": null
}
},
"animations": [],
"ranges": [],
"isEnabled": true
},
"animations": []
}

View File

@ -0,0 +1,524 @@
{
"meshes": [
{
"name": "Ship_primitive3",
"id": "037071c4-2096-4616-8e14-2e9cadab7ade",
"uniqueId": 1764787012176,
"type": "Mesh",
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scaling": [
1,
1,
1
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 0,
"isUnIndexed": false,
"geometryUniqueId": 1764787012184,
"geometryId": "ab02f83d-3a7a-4abd-8e08-7dab01ba5931",
"subMeshes": null,
"materialUniqueId": 1764787012185,
"materialId": "4b8b6899-b62b-4c2c-aa9c-4102fac0c2fe",
"metadata": {
"parentId": 1764787012177
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/ab02f83d-3a7a-4abd-8e08-7dab01ba5931.babylonbinarymeshdata",
"boundingBoxMaximum": [
-0.36345699429512024,
0.7514887452125549,
4.199827194213867
],
"boundingBoxMinimum": [
-0.866409957408905,
0.29206109046936035,
4.168615341186523
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 12,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 12,
"stride": 3,
"offset": 48,
"dataType": 1
},
"uvsAttrDesc": {
"count": 8,
"stride": 2,
"offset": 96,
"dataType": 1
},
"indicesAttrDesc": {
"count": 6,
"stride": 1,
"offset": 128,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 152,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"albedoTexture": {
"tags": null,
"url": "assets/editor-generated_7dc5359ed77982dcccfa2ae9f8d1c491.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": -1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/editor-generated_7dc5359ed77982dcccfa2ae9f8d1c491.jpg",
"metadata": {
"baseSize": {
"width": 512,
"height": 512
}
},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 2,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": false,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"internalTextureLabel": "/Users/michaelmainguy/WebstormProjects/space-game/gameEditor",
"noMipmap": false
},
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"emissiveTexture": {
"tags": null,
"url": "assets/editor-generated_7dc5359ed77982dcccfa2ae9f8d1c491.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": -1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/editor-generated_7dc5359ed77982dcccfa2ae9f8d1c491.jpg",
"metadata": {
"baseSize": {
"width": 512,
"height": 512
}
},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 2,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": false,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"internalTextureLabel": "/Users/michaelmainguy/WebstormProjects/space-game/gameEditor",
"noMipmap": false
},
"metallic": 0,
"roughness": 0.5,
"metallicF0Factor": 1,
"metallicReflectanceColor": [
1,
1,
1
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
1,
1,
1
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
0.9999999403953552,
0.9999999403953552,
0.9999999403953552
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": true,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": true,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "4b8b6899-b62b-4c2c-aa9c-4102fac0c2fe",
"name": "Gauges",
"metadata": {},
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": false,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 0,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1764787012185,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": true,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}
],
"geometries": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"parentId": 1764787012177
},
"basePoseMatrix": [
1.6247152644956515,
0,
4.08643597401238e-7,
0,
0,
1.4727547823314542,
0,
0,
5.293994474577842e-7,
0,
-1.2541182156960042,
0,
0,
-0.9408809412868501,
0,
1
]
}

View File

@ -0,0 +1,279 @@
{
"meshes": [
{
"name": "New Box",
"id": "1679685b-ca9c-4a1c-8161-9f6064cb1d7c",
"uniqueId": 1764787809874,
"type": "Mesh",
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scaling": [
1,
1,
1
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 1,
"isUnIndexed": false,
"geometryUniqueId": 1764787809875,
"geometryId": "76c7442d-fb7e-4a05-b1c5-9c27b0beb0dc",
"subMeshes": null,
"materialUniqueId": 46,
"materialId": "default material",
"metadata": {
"type": "Box",
"width": 100,
"depth": 100,
"height": 100,
"sideOrientation": 0
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/76c7442d-fb7e-4a05-b1c5-9c27b0beb0dc.babylonbinarymeshdata",
"boundingBoxMaximum": [
50,
50,
50
],
"boundingBoxMinimum": [
-50,
-50,
-50
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 72,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 72,
"stride": 3,
"offset": 288,
"dataType": 1
},
"uvsAttrDesc": {
"count": 48,
"stride": 2,
"offset": 576,
"dataType": 1
},
"indicesAttrDesc": {
"count": 36,
"stride": 1,
"offset": 768,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 912,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"ambient": [
0,
0,
0
],
"diffuse": [
1,
1,
1
],
"specular": [
1,
1,
1
],
"emissive": [
0,
0,
0
],
"specularPower": 64,
"useAlphaFromDiffuseTexture": false,
"useEmissiveAsIllumination": false,
"linkEmissiveWithDiffuse": false,
"useSpecularOverAlpha": false,
"useReflectionOverAlpha": false,
"disableLighting": false,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"roughness": 0,
"indexOfRefraction": 0.98,
"invertRefractionY": true,
"alphaCutOff": 0.4,
"useLightmapAsShadowmap": false,
"useReflectionFresnelFromSpecular": false,
"useGlossinessFromSpecularMapAlpha": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": false,
"applyDecalMapAfterDetailMap": false,
"id": "default material",
"name": "default material",
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": true,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 46,
"plugins": {
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
}
}
],
"geometries": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"type": "Box",
"width": 100,
"depth": 100,
"height": 100,
"sideOrientation": 0
},
"basePoseMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
]
}

View File

@ -0,0 +1,428 @@
{
"meshes": [
{
"name": "BaseLandingZone",
"id": "2a185a78-d743-4a8f-b9a8-cf95f60f0ae0",
"uniqueId": 1764786749145,
"type": "Mesh",
"position": [
0,
6.372689247131348,
0
],
"rotationQuaternion": [
0,
0,
0,
1
],
"scaling": [
28.34206199645996,
28.34206199645996,
28.34206199645996
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 0,
"isUnIndexed": false,
"geometryUniqueId": 1764786749148,
"geometryId": "0d955d0c-93e1-4875-884d-db636576f51e",
"subMeshes": null,
"materialUniqueId": 1764786749149,
"materialId": "5bc1e4e2-2fa5-4c81-a3de-93e55ae0919e",
"metadata": {
"parentId": 1764786749143
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 0,
"overlayColor": [
0,
0,
0
],
"renderOverlay": false,
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/0d955d0c-93e1-4875-884d-db636576f51e.babylonbinarymeshdata",
"boundingBoxMaximum": [
0.9807851314544678,
0.3284308910369873,
0.9807849526405334
],
"boundingBoxMinimum": [
-0.9807848334312439,
-0.4938357472419739,
-0.9807852506637573
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 2688,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 2688,
"stride": 3,
"offset": 10752,
"dataType": 1
},
"uvsAttrDesc": {
"count": 1792,
"stride": 2,
"offset": 21504,
"dataType": 1
},
"indicesAttrDesc": {
"count": 1338,
"stride": 1,
"offset": 28672,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 34024,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"metallic": 0,
"roughness": 0.5,
"metallicF0Factor": 1,
"metallicReflectanceColor": [
1,
1,
1
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
0.0724073052406311,
0.8001724481582642,
0.08219180256128311
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
0.003482822866271551,
0.09999999403953552,
0.003765808193933462
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": true,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": true,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "5bc1e4e2-2fa5-4c81-a3de-93e55ae0919e",
"name": "Material",
"metadata": {},
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 0.2634408473968506,
"backFaceCulling": false,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 2,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1764786749149,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": true,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}
],
"geometries": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"parentId": 1764786749143
},
"basePoseMatrix": [
-0.03528324792052547,
0,
0,
0,
0,
0.03528324792052547,
0,
0,
0,
0,
0.03528324792052547,
0,
0,
-0.22484917462700213,
0,
0.9999999999999999
]
}

View File

@ -0,0 +1,594 @@
{
"meshes": [
{
"name": "Asteroid",
"id": "790b6a71-5425-47f2-ae64-f2d04e2e3c6d",
"uniqueId": 1764789858422,
"type": "Mesh",
"position": [
-33.08901451261646,
40.81207511231127,
-108.3875380136813
],
"rotationQuaternion": [
0,
0,
0,
1
],
"scaling": [
5,
5,
5
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 0,
"isUnIndexed": false,
"geometryUniqueId": 1764789858423,
"geometryId": "9092e1b8-d64b-409a-9067-1f9373f47df3",
"subMeshes": null,
"materialUniqueId": 1764789858424,
"materialId": "81400b58-6fbe-4364-8c25-b5a03c536f05",
"metadata": {
"scripts": [
{
"enabled": true,
"key": "scripts/editorScripts/AsteroidComponent.ts",
"values": {
"linearVelocity": {
"type": "vector3",
"value": [
0,
0,
-30
]
},
"angularVelocity": {
"type": "vector3",
"value": [
0,
0,
0
]
},
"mass": {
"type": "number",
"value": 1
},
"targetId": {
"type": "string",
"description": "Reference to a TargetComponent node",
"value": ""
},
"targetMode": {
"type": "string",
"description": "orbit | moveToward | (empty)",
"value": ""
}
},
"_id": "750ffa11-d797-43f4-a63b-0e1592a4f6af"
}
],
"parentId": 1764789858421
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 0,
"overlayColor": [
0,
0,
0
],
"renderOverlay": false,
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/9092e1b8-d64b-409a-9067-1f9373f47df3.babylonbinarymeshdata",
"boundingBoxMaximum": [
2.8008618354797363,
2.679150342941284,
2.679150104522705
],
"boundingBoxMinimum": [
-2.054845094680786,
-2.679150342941284,
-2.679150104522705
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 912,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 912,
"stride": 3,
"offset": 3648,
"dataType": 1
},
"uvsAttrDesc": {
"count": 608,
"stride": 2,
"offset": 7296,
"dataType": 1
},
"indicesAttrDesc": {
"count": 342,
"stride": 1,
"offset": 9728,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 11096,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"albedoTexture": {
"tags": null,
"url": "assets/editor-generated_aed030862c8a5064c80262817ba13a70.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": -1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/editor-generated_aed030862c8a5064c80262817ba13a70.jpg",
"metadata": {},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 0,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": false,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"internalTextureLabel": "/Users/michaelmainguy/WebstormProjects/space-game/gameEditor",
"noMipmap": false
},
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"emissiveTexture": {
"tags": null,
"url": "assets/editor-generated_aed030862c8a5064c80262817ba13a70.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": -1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/editor-generated_aed030862c8a5064c80262817ba13a70.jpg",
"metadata": {},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 0,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": false,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"internalTextureLabel": "/Users/michaelmainguy/WebstormProjects/space-game/gameEditor",
"noMipmap": false
},
"metallic": 0,
"roughness": 1,
"metallicF0Factor": 0,
"metallicReflectanceColor": [
0.23755916953086853,
0.23755916953086853,
0.23755916953086853
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
1,
1,
1
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
0.3999999761581421,
0.3999999761581421,
0.3999999761581421
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": true,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": true,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "81400b58-6fbe-4364-8c25-b5a03c536f05",
"name": "Material.001",
"metadata": {},
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": false,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 0,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1764789858424,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": true,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}
],
"geometries": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"scripts": [
{
"enabled": true,
"key": "scripts/editorScripts/AsteroidComponent.ts",
"values": {
"linearVelocity": {
"type": "vector3",
"value": [
0,
0,
-30
]
},
"angularVelocity": {
"type": "vector3",
"value": [
0,
0,
0
]
},
"mass": {
"type": "number",
"value": 1
},
"targetId": {
"type": "string",
"description": "Reference to a TargetComponent node",
"value": ""
},
"targetMode": {
"type": "string",
"description": "orbit | moveToward | (empty)",
"value": ""
}
},
"_id": "750ffa11-d797-43f4-a63b-0e1592a4f6af"
}
],
"parentId": 1764789858421
},
"basePoseMatrix": [
-1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
]
}

View File

@ -0,0 +1,426 @@
{
"meshes": [
{
"name": "Ship_primitive1",
"id": "b633ecf7-2331-4ebd-b586-3852eff6c50f",
"uniqueId": 1764787012174,
"type": "Mesh",
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scaling": [
1,
1,
1
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 0,
"isUnIndexed": false,
"geometryUniqueId": 1764787012180,
"geometryId": "7a537688-5920-4808-9d7b-d63e83375f90",
"subMeshes": null,
"materialUniqueId": 1764787012181,
"materialId": "436eb7a0-ac6c-472d-b6fe-0d986a8af5c7",
"metadata": {
"parentId": 1764787012177
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/7a537688-5920-4808-9d7b-d63e83375f90.babylonbinarymeshdata",
"boundingBoxMaximum": [
1.5084391832351685,
2.162097454071045,
5.122784614562988
],
"boundingBoxMinimum": [
-1.5084391832351685,
-1.0288662910461426,
-2.6966497898101807
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 420,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 420,
"stride": 3,
"offset": 1680,
"dataType": 1
},
"uvsAttrDesc": {
"count": 280,
"stride": 2,
"offset": 3360,
"dataType": 1
},
"indicesAttrDesc": {
"count": 192,
"stride": 1,
"offset": 4480,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 5248,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"metallic": 0,
"roughness": 0.059139788150787354,
"metallicF0Factor": 0,
"metallicReflectanceColor": [
1,
1,
1
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
0.800133466720581,
0.69917231798172,
0.16859057545661926
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
0,
0,
0
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": true,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": true,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "436eb7a0-ac6c-472d-b6fe-0d986a8af5c7",
"name": "ShipWindow",
"metadata": {},
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 0.1505376398563385,
"backFaceCulling": false,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 2,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1764787012181,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": true,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}
],
"geometries": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"parentId": 1764787012177
},
"basePoseMatrix": [
1.6247152644956515,
0,
4.08643597401238e-7,
0,
0,
1.4727547823314542,
0,
0,
5.293994474577842e-7,
0,
-1.2541182156960042,
0,
0,
-0.9408809412868501,
0,
1
]
}

View File

@ -0,0 +1,156 @@
{
"meshes": [
{
"name": "ship.glb",
"id": "bf9cd6cd-3743-4b89-9c33-e7b12cc3eb3d",
"uniqueId": 1764787012172,
"type": "Mesh",
"position": [
0,
2,
0
],
"rotation": [
0,
3.1129212205596573,
0
],
"scaling": [
100.00000000000159,
-100,
100.00000000000159
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 1,
"isUnIndexed": false,
"materialId": "default material",
"metadata": {
"scripts": [
{
"enabled": true,
"key": "scripts/editorScripts/ShipComponent.ts",
"values": {
"linearVelocity": {
"type": "vector3",
"value": [
0,
0,
0
]
},
"angularVelocity": {
"type": "vector3",
"value": [
0,
0,
0
]
}
},
"_id": "fa6b5e6d-29c3-4159-9248-cab250039a6e"
}
]
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [],
"metadata": {
"scripts": [
{
"enabled": true,
"key": "scripts/editorScripts/ShipComponent.ts",
"values": {
"linearVelocity": {
"type": "vector3",
"value": [
0,
0,
0
]
},
"angularVelocity": {
"type": "vector3",
"value": [
0,
0,
0
]
}
},
"_id": "fa6b5e6d-29c3-4159-9248-cab250039a6e"
}
]
},
"basePoseMatrix": [
-1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
]
}

View File

@ -0,0 +1,247 @@
{
"meshes": [
{
"name": "base.glb",
"id": "c7b3d00d-20da-4560-8611-f3186ffda0f0",
"uniqueId": 1764786749143,
"type": "Mesh",
"position": [
0,
0,
0
],
"rotationQuaternion": [
0,
1,
0,
0
],
"scaling": [
100,
100,
-100
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 1,
"isUnIndexed": false,
"materialUniqueId": 46,
"materialId": "default material",
"metadata": {
"scripts": [
{
"enabled": true,
"key": "scripts/editorScripts/BaseComponent.ts",
"values": {
"baseGlbPath": {
"type": "string",
"description": "Path to base GLB model",
"value": ""
},
"landingGlbPath": {
"type": "string",
"description": "Path to landing zone GLB",
"value": ""
}
},
"_id": "7741fd66-a746-4f78-a7fd-5f8f404b610c"
}
]
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"ambient": [
0,
0,
0
],
"diffuse": [
1,
1,
1
],
"specular": [
1,
1,
1
],
"emissive": [
0,
0,
0
],
"specularPower": 64,
"useAlphaFromDiffuseTexture": false,
"useEmissiveAsIllumination": false,
"linkEmissiveWithDiffuse": false,
"useSpecularOverAlpha": false,
"useReflectionOverAlpha": false,
"disableLighting": false,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"roughness": 0,
"indexOfRefraction": 0.98,
"invertRefractionY": true,
"alphaCutOff": 0.4,
"useLightmapAsShadowmap": false,
"useReflectionFresnelFromSpecular": false,
"useGlossinessFromSpecularMapAlpha": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": false,
"applyDecalMapAfterDetailMap": false,
"id": "default material",
"name": "default material",
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": true,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 46,
"plugins": {
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
}
}
],
"metadata": {
"scripts": [
{
"enabled": true,
"key": "scripts/editorScripts/BaseComponent.ts",
"values": {
"baseGlbPath": {
"type": "string",
"description": "Path to base GLB model",
"value": ""
},
"landingGlbPath": {
"type": "string",
"description": "Path to landing zone GLB",
"value": ""
}
},
"_id": "7741fd66-a746-4f78-a7fd-5f8f404b610c"
}
]
},
"basePoseMatrix": [
-1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
]
}

View File

@ -0,0 +1,207 @@
{
"meshes": [
{
"name": "asteroid.glb",
"id": "c8ccf36e-90e4-4cef-bd6c-f9856ae91783",
"uniqueId": 1764789858421,
"type": "Mesh",
"position": [
0,
0,
0
],
"rotationQuaternion": [
0,
1,
0,
0
],
"scaling": [
100,
100,
-100
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 1,
"isUnIndexed": false,
"materialUniqueId": 46,
"materialId": "default material",
"metadata": {},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"ambient": [
0,
0,
0
],
"diffuse": [
1,
1,
1
],
"specular": [
1,
1,
1
],
"emissive": [
0,
0,
0
],
"specularPower": 64,
"useAlphaFromDiffuseTexture": false,
"useEmissiveAsIllumination": false,
"linkEmissiveWithDiffuse": false,
"useSpecularOverAlpha": false,
"useReflectionOverAlpha": false,
"disableLighting": false,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"roughness": 0,
"indexOfRefraction": 0.98,
"invertRefractionY": true,
"alphaCutOff": 0.4,
"useLightmapAsShadowmap": false,
"useReflectionFresnelFromSpecular": false,
"useGlossinessFromSpecularMapAlpha": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": false,
"applyDecalMapAfterDetailMap": false,
"id": "default material",
"name": "default material",
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": true,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 46,
"plugins": {
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
}
}
],
"metadata": {},
"basePoseMatrix": [
-1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
]
}

View File

@ -0,0 +1,426 @@
{
"meshes": [
{
"name": "Ship_primitive2",
"id": "cbee3b60-c52a-451e-8a43-ddeccb72c2e6",
"uniqueId": 1764787012175,
"type": "Mesh",
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scaling": [
1,
1,
1
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 0,
"isUnIndexed": false,
"geometryUniqueId": 1764787012182,
"geometryId": "4cda27fd-eed5-4d1a-b69a-a10585be394d",
"subMeshes": null,
"materialUniqueId": 1764787012183,
"materialId": "a37af4ed-0bc4-4e4b-bee9-63cecf56c25c",
"metadata": {
"parentId": 1764787012177
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/4cda27fd-eed5-4d1a-b69a-a10585be394d.babylonbinarymeshdata",
"boundingBoxMaximum": [
0.21567727625370026,
0.7399396300315857,
4.235613822937012
],
"boundingBoxMinimum": [
-0.2839681804180145,
0.23974217474460602,
4.230499744415283
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 12,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 12,
"stride": 3,
"offset": 48,
"dataType": 1
},
"uvsAttrDesc": {
"count": 8,
"stride": 2,
"offset": 96,
"dataType": 1
},
"indicesAttrDesc": {
"count": 6,
"stride": 1,
"offset": 128,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 152,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"metallic": 0,
"roughness": 0.5,
"metallicF0Factor": 1,
"metallicReflectanceColor": [
1,
1,
1
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
0,
0,
0
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
0,
0,
0
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": true,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": true,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "a37af4ed-0bc4-4e4b-bee9-63cecf56c25c",
"name": "Screen",
"metadata": {},
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": false,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 0,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1764787012183,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": true,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}
],
"geometries": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"parentId": 1764787012177
},
"basePoseMatrix": [
1.6247152644956515,
0,
4.08643597401238e-7,
0,
0,
1.4727547823314542,
0,
0,
5.293994474577842e-7,
0,
-1.2541182156960042,
0,
0,
-0.9408809412868501,
0,
1
]
}

View File

@ -0,0 +1,526 @@
{
"meshes": [
{
"name": "Base",
"id": "df3bdc41-8314-45f8-9add-ccfd1d06451f",
"uniqueId": 1764786749144,
"type": "Mesh",
"position": [
0,
-8.583544731140137,
0
],
"rotationQuaternion": [
0,
0,
0,
1
],
"scaling": [
33.146690368652344,
1,
37.254390716552734
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 0,
"isUnIndexed": false,
"geometryUniqueId": 1764786749146,
"geometryId": "101bd857-f8e8-4230-a021-5d519fefb26c",
"subMeshes": null,
"materialUniqueId": 1764786749147,
"materialId": "00d2631a-e346-4509-b0bb-64e0467c0622",
"metadata": {
"parentId": 1764786749143
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 0,
"overlayColor": [
0,
0,
0
],
"renderOverlay": false,
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/101bd857-f8e8-4230-a021-5d519fefb26c.babylonbinarymeshdata",
"boundingBoxMaximum": [
1,
0.7045964002609253,
1
],
"boundingBoxMinimum": [
-1,
-2.1139473915100098,
-1
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 264,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 264,
"stride": 3,
"offset": 1056,
"dataType": 1
},
"uvsAttrDesc": {
"count": 176,
"stride": 2,
"offset": 2112,
"dataType": 1
},
"indicesAttrDesc": {
"count": 132,
"stride": 1,
"offset": 2816,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 3344,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1.399999976158142,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"albedoTexture": {
"tags": null,
"url": "assets/editor-generated_9f6c774a10727dd45e1e2122a14a3957.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": -1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/editor-generated_9f6c774a10727dd45e1e2122a14a3957.jpg",
"metadata": {
"baseSize": {
"width": 2048,
"height": 2048
}
},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 0,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": false,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"internalTextureLabel": "/Users/michaelmainguy/WebstormProjects/space-game/gameEditor",
"noMipmap": false
},
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"emissiveTexture": {
"tags": null,
"url": "assets/editor-generated_9f6c774a10727dd45e1e2122a14a3957.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": -1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/editor-generated_9f6c774a10727dd45e1e2122a14a3957.jpg",
"metadata": {
"baseSize": {
"width": 2048,
"height": 2048
}
},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 0,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": false,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"internalTextureLabel": "/Users/michaelmainguy/WebstormProjects/space-game/gameEditor",
"noMipmap": false
},
"metallic": 0,
"roughness": 0.5,
"metallicF0Factor": 1,
"metallicReflectanceColor": [
1,
1,
1
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
1,
1,
1
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
1,
1,
1
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": true,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": true,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "00d2631a-e346-4509-b0bb-64e0467c0622",
"name": "Material.001",
"metadata": {},
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": false,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 0,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1764786749147,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": true,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}
],
"geometries": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"parentId": 1764786749143
},
"basePoseMatrix": [
-0.030168924525439955,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0.026842473618973555,
0,
0,
8.583544731140138,
0,
1
]
}

View File

@ -0,0 +1,524 @@
{
"meshes": [
{
"name": "Ship_primitive0",
"id": "e76fcb83-e1df-4c7c-92cd-65b601ee9bba",
"uniqueId": 1764787012173,
"type": "Mesh",
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scaling": [
1,
1,
1
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 0,
"isUnIndexed": false,
"geometryUniqueId": 1764787012178,
"geometryId": "5a938545-3b1e-40a5-9ed3-a545ced11fd4",
"subMeshes": null,
"materialUniqueId": 1764787012179,
"materialId": "75fae723-6ee6-443b-ba23-704d76ebdb7b",
"metadata": {
"parentId": 1764787012177
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/5a938545-3b1e-40a5-9ed3-a545ced11fd4.babylonbinarymeshdata",
"boundingBoxMaximum": [
11.43801212310791,
2.162097454071045,
8.191577911376953
],
"boundingBoxMinimum": [
-11.43801212310791,
-1.028866171836853,
-7.255166530609131
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 2790,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 2790,
"stride": 3,
"offset": 11160,
"dataType": 1
},
"uvsAttrDesc": {
"count": 1860,
"stride": 2,
"offset": 22320,
"dataType": 1
},
"indicesAttrDesc": {
"count": 1284,
"stride": 1,
"offset": 29760,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 34896,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"materials": [
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1.8999998569488525,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"albedoTexture": {
"tags": null,
"url": "assets/editor-generated_407fefb1bb1a53ce6f7be55ea1b07228.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": -1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/editor-generated_407fefb1bb1a53ce6f7be55ea1b07228.jpg",
"metadata": {
"baseSize": {
"width": 4096,
"height": 4096
}
},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 1,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": false,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"internalTextureLabel": "/Users/michaelmainguy/WebstormProjects/space-game/gameEditor",
"noMipmap": false
},
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"emissiveTexture": {
"tags": null,
"url": "assets/editor-generated_407fefb1bb1a53ce6f7be55ea1b07228.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": -1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/editor-generated_407fefb1bb1a53ce6f7be55ea1b07228.jpg",
"metadata": {
"baseSize": {
"width": 4096,
"height": 4096
}
},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 1,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": false,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"internalTextureLabel": "/Users/michaelmainguy/WebstormProjects/space-game/gameEditor",
"noMipmap": false
},
"metallic": 0.5344085693359375,
"roughness": 0.4500977396965027,
"metallicF0Factor": 1,
"metallicReflectanceColor": [
1,
1,
1
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
1,
1,
1
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
1,
1,
1
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": true,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": true,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "75fae723-6ee6-443b-ba23-704d76ebdb7b",
"name": "Hull",
"metadata": {},
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": false,
"cullBackFaces": true,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 0,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1764787012179,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": true,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}
],
"geometries": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"parentId": 1764787012177
},
"basePoseMatrix": [
1.6247152644956515,
0,
4.08643597401238e-7,
0,
0,
1.4727547823314542,
0,
0,
5.293994474577842e-7,
0,
-1.2541182156960042,
0,
0,
-0.9408809412868501,
0,
1
]
}

View File

@ -0,0 +1,58 @@
{
"tags": null,
"position": [
0,
0.6388578414916992,
0
],
"rotation": [
0,
0,
0
],
"rotationQuaternion": [
0,
0,
0,
1
],
"scaling": [
0.6154924631118774,
0.678999662399292,
0.7973729968070984
],
"billboardMode": 0,
"scalingDeterminant": 1,
"infiniteDistance": false,
"ignoreNonUniformScaling": false,
"reIntegrateRotationIntoRotationQuaternion": false,
"name": "Ship",
"id": "315c338c-b9a0-4509-839c-8802b52dc0b3",
"state": "",
"metadata": {
"parentId": 1764787012172
},
"type": "TransformNode",
"uniqueId": 1764787012177,
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"animations": [],
"ranges": []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
gameEditor/assets/ship.glb Normal file

Binary file not shown.

17
gameEditor/index.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>VanillaJS App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<main id="app" class="flex w-screen h-screen flex-col items-center justify-between">
<canvas id="canvas" class="w-full h-full outline-none select-none"></canvas>
</main>
<script src="/src/main.ts" type="module"></script>
</body>
</html>

1802
gameEditor/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
gameEditor/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "babylonjs-editor-vanillajs-template",
"version": "0.0.0",
"description": "",
"type": "module",
"private": true,
"scripts": {
"start": "vite",
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"license": "MIT",
"devDependencies": {
"@tailwindcss/vite": "^4.0.7",
"@types/node": "^22",
"tailwindcss": "^4.0.7",
"typescript": "^5.7.2",
"vite": "^6.4.1"
},
"dependencies": {
"@babylonjs/core": "8.33.2",
"@babylonjs/gui": "8.33.2",
"@babylonjs/havok": "1.3.10",
"@babylonjs/materials": "8.33.2",
"babylonjs-editor-tools": "^5.2.4"
}
}

View File

@ -0,0 +1,12 @@
{
"plugins": [
{
"nameOrPath": "/Users/michaelmainguy/WebstormProjects/space-game/bjsEditorPlugin/dist"
}
],
"version": "5.2.4",
"packageManager": "npm",
"lastOpenedScene": "/assets/example.scene",
"compressedTexturesEnabled": false,
"compressedTexturesEnabledInPreview": false
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

View File

@ -0,0 +1,303 @@
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"albedoTexture": {
"tags": null,
"url": "assets/amiga.jpg",
"uOffset": 0,
"vOffset": 0,
"uScale": 1,
"vScale": 1,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/amiga.jpg",
"metadata": {
"baseSize": {
"width": 128,
"height": 128
}
},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 0,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": true,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"noMipmap": false
},
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"metallic": 0,
"roughness": 1,
"metallicF0Factor": 1,
"metallicReflectanceColor": [
1,
1,
1
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
1,
1,
1
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
0,
0,
0
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": false,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": false,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "9f2b8769-ac8e-4289-b8a4-171c5b7d3139",
"name": "box",
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": true,
"cullBackFaces": true,
"sideOrientation": 1,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 0,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1711813856778,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": false,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -0,0 +1,303 @@
{
"tags": null,
"directIntensity": 1,
"emissiveIntensity": 1,
"environmentIntensity": 1,
"specularIntensity": 1,
"disableBumpMap": false,
"albedoTexture": {
"tags": null,
"url": "assets/albedo.png",
"uOffset": 0,
"vOffset": 0,
"uScale": 500,
"vScale": 500,
"uAng": 0,
"vAng": 0,
"wAng": 0,
"uRotationCenter": 0.5,
"vRotationCenter": 0.5,
"wRotationCenter": 0.5,
"homogeneousRotationInUVTransform": false,
"isBlocking": true,
"name": "assets/albedo.png",
"metadata": {
"baseSize": {
"width": 512,
"height": 512
}
},
"hasAlpha": false,
"getAlphaFromRGB": false,
"level": 1,
"coordinatesIndex": 0,
"optimizeUVAllocation": true,
"coordinatesMode": 0,
"wrapU": 1,
"wrapV": 1,
"wrapR": 1,
"anisotropicFilteringLevel": 4,
"isCube": false,
"is3D": false,
"is2DArray": false,
"gammaSpace": true,
"invertZ": false,
"lodLevelInAlpha": false,
"lodGenerationOffset": 0,
"lodGenerationScale": 0,
"linearSpecularLOD": false,
"isRenderTarget": false,
"animations": [],
"invertY": true,
"samplingMode": 3,
"_useSRGBBuffer": false,
"noMipmap": false
},
"ambientTextureStrength": 1,
"ambientTextureImpactOnAnalyticalLights": 0,
"metallic": 0,
"roughness": 1,
"metallicF0Factor": 1,
"metallicReflectanceColor": [
1,
1,
1
],
"useOnlyMetallicFromMetallicReflectanceTexture": false,
"ambient": [
0,
0,
0
],
"albedo": [
1,
1,
1
],
"baseWeight": 1,
"reflectivity": [
1,
1,
1
],
"reflection": [
1,
1,
1
],
"emissive": [
0,
0,
0
],
"microSurface": 1,
"useLightmapAsShadowmap": false,
"useAlphaFromAlbedoTexture": false,
"forceAlphaTest": false,
"alphaCutOff": 0.4,
"useSpecularOverAlpha": true,
"useMicroSurfaceFromReflectivityMapAlpha": false,
"useRoughnessFromMetallicTextureAlpha": true,
"useRoughnessFromMetallicTextureGreen": false,
"useMetallnessFromMetallicTextureBlue": false,
"useAmbientOcclusionFromMetallicTextureRed": false,
"useAmbientInGrayScale": false,
"useAutoMicroSurfaceFromReflectivityMap": false,
"usePhysicalLightFalloff": true,
"useGLTFLightFalloff": false,
"useRadianceOverAlpha": true,
"useObjectSpaceNormalMap": false,
"useParallax": false,
"useParallaxOcclusion": false,
"parallaxScaleBias": 0.05,
"disableLighting": false,
"forceIrradianceInFragment": false,
"maxSimultaneousLights": 32,
"invertNormalMapX": false,
"invertNormalMapY": false,
"twoSidedLighting": false,
"useAlphaFresnel": false,
"useLinearAlphaFresnel": false,
"forceNormalForward": false,
"enableSpecularAntiAliasing": false,
"useHorizonOcclusion": true,
"useRadianceOcclusion": true,
"unlit": false,
"applyDecalMapAfterDetailMap": false,
"id": "27c18987-1817-46cd-bdb0-2328f55c7f8b",
"name": "ground",
"checkReadyOnEveryCall": false,
"checkReadyOnlyOnce": false,
"state": "",
"alpha": 1,
"backFaceCulling": true,
"cullBackFaces": true,
"sideOrientation": 1,
"_alphaMode": [
2
],
"_needDepthPrePass": false,
"disableDepthWrite": false,
"disableColorWrite": false,
"forceDepthWrite": false,
"depthFunction": 0,
"separateCullingPass": false,
"fogEnabled": true,
"pointSize": 1,
"zOffset": 0,
"zOffsetUnits": 0,
"pointsCloud": false,
"fillMode": 0,
"_isVertexOutputInvariant": false,
"transparencyMode": 0,
"stencil": {
"tags": null,
"func": 519,
"backFunc": 519,
"funcRef": 1,
"funcMask": 255,
"opStencilFail": 7680,
"opDepthFail": 7680,
"opStencilDepthPass": 7681,
"backOpStencilFail": 7680,
"backOpDepthFail": 7680,
"backOpStencilDepthPass": 7681,
"mask": 255,
"enabled": false
},
"uniqueId": 1711813881026,
"plugins": {
"PBRBRDFConfiguration": {
"tags": null,
"useEnergyConservation": true,
"useSmithVisibilityHeightCorrelated": true,
"useSphericalHarmonics": true,
"useSpecularGlossinessInputEnergyConservation": true,
"mixIblRadianceWithIrradiance": true,
"useLegacySpecularEnergyConservation": true,
"baseDiffuseModel": 0,
"dielectricSpecularModel": 0,
"conductorSpecularModel": 0,
"name": "PBRBRDF",
"priority": 90,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRClearCoatConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"roughness": 0,
"indexOfRefraction": 1.5,
"useRoughnessFromMainTexture": true,
"remapF0OnInterfaceChange": true,
"isTintEnabled": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"tintThickness": 1,
"name": "PBRClearCoat",
"priority": 100,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRIridescenceConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"minimumThickness": 100,
"maximumThickness": 400,
"indexOfRefraction": 1.3,
"name": "PBRIridescence",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRAnisotropicConfiguration": {
"tags": null,
"isEnabled": false,
"intensity": 1,
"direction": [
1,
0
],
"legacy": false,
"name": "PBRAnisotropic",
"priority": 110,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSheenConfiguration": {
"tags": null,
"isEnabled": false,
"linkSheenWithAlbedo": false,
"intensity": 1,
"color": [
1,
1,
1
],
"useRoughnessFromMainTexture": true,
"albedoScaling": false,
"name": "Sheen",
"priority": 120,
"resolveIncludes": false,
"registerForExtraEvents": false
},
"PBRSubSurfaceConfiguration": {
"tags": null,
"isRefractionEnabled": false,
"isTranslucencyEnabled": false,
"isDispersionEnabled": false,
"isScatteringEnabled": false,
"_scatteringDiffusionProfileIndex": 0,
"refractionIntensity": 1,
"translucencyIntensity": 1,
"useAlbedoToTintRefraction": false,
"useAlbedoToTintTranslucency": false,
"indexOfRefraction": 1.5,
"_volumeIndexOfRefraction": -1,
"invertRefractionY": false,
"linkRefractionWithTransparency": false,
"minimumThickness": 0,
"maximumThickness": 1,
"useThicknessAsDepth": false,
"tintColor": [
1,
1,
1
],
"tintColorAtDistance": 1,
"dispersion": 0,
"diffusionDistance": [
1,
1,
1
],
"useMaskFromThicknessTexture": false,
"useGltfStyleTextures": false,
"applyAlbedoAfterSubSurface": false,
"legacyTranslucency": false,
"name": "PBRSubSurface",
"priority": 130,
"resolveIncludes": false,
"registerForExtraEvents": true
},
"DetailMapConfiguration": {
"tags": null,
"diffuseBlendLevel": 1,
"roughnessBlendLevel": 1,
"bumpLevel": 1,
"normalBlendMethod": 0,
"isEnabled": false,
"name": "DetailMap",
"priority": 140,
"resolveIncludes": false,
"registerForExtraEvents": false
}
},
"customType": "BABYLON.PBRMaterial"
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More