immersive2/src/util/customEnvironment.ts
Michael Mainguy 6d2049e1f6 Convert to unlit rendering and fix connection update error
Lighting changes:
- Disabled HemisphericLight in customEnvironment
- Changed all materials from diffuse to emissive colors
- Added disableLighting=true to all StandardMaterials
- Updated toolbox colors, diagram entities, and spinner

Bug fix:
- Fixed "Cannot read properties of undefined (reading 'pickedMesh')" error
- Added defensive check in DiagramObject.updateConnection()
- Now validates hit array has at least 2 elements before accessing

Materials now render at full brightness with unlit/flat shading.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 21:16:29 -06:00

162 lines
5.4 KiB
TypeScript

import {
Color3,
GroundMesh,
HemisphericLight,
Material,
MeshBuilder,
Observable,
PBRMaterial,
PhysicsAggregate,
PhysicsShapeType,
PointsCloudSystem,
Scene,
Sound,
Texture,
TransformNode,
Vector3
} from "@babylonjs/core";
import {CustomPhysics} from "./customPhysics";
import {AppConfig} from "./appConfig";
import {GridMaterial} from "@babylonjs/materials";
import {DefaultScene} from "../defaultScene";
import log from "loglevel";
export class CustomEnvironment {
private readonly scene: Scene;
private readonly name: string;
private readonly _groundMeshObservable: Observable<GroundMesh> = new Observable<GroundMesh>();
private readonly _logger = log.getLogger('CustomEnvironment');
constructor(name: string = "default", config: AppConfig) {
this._logger.debug('CustomEnvironment constructor', config);
this.scene = DefaultScene.Scene;
this.name = name;
this.scene.ambientColor = new Color3(.1, .1, .1);
// Light disabled for unlit rendering
// const light = new HemisphericLight("light1", new Vector3(.5, 1, 1).normalize(), this.scene);
// light.groundColor = new Color3(0, 0, 0);
// light.diffuse = new Color3(1, 1, 1);
// light.intensity = .8;
const physics = new CustomPhysics(this.scene);
physics
.initializeAsync()
.then(() => {
const ground = this.createGround();
new PhysicsAggregate(ground, PhysicsShapeType.BOX, {mass: 0}, this.scene);
//createPoints(20, 20);
this.createBackgroundAudio();
this._groundMeshObservable.notifyObservers(ground);
this.createWalls();
});
}
public get groundMeshObservable() {
return this._groundMeshObservable;
}
private createBackgroundAudio() {
const noise = new Sound("backgroundNoise", "/assets/sounds/noise.mp3", this.scene, null, {
loop: true,
volume: .2,
autoplay: true
});
}
private createGround() {
const scene = this.scene;
const ground: GroundMesh = MeshBuilder.CreateGround("ground", {
width: 20,
height: 20,
subdivisions: 1
}, scene);
ground.material = createGridMaterial(Color3.FromHexString("#aaffaa"), Color3.FromHexString("#111511"));
//buildAvatar(scene);
return ground;
}
private createWalls() {
const color1 = Color3.FromHexString("#ff9999");
const color2 = Color3.FromHexString("#221111");
const color3 = Color3.FromHexString("#9999ff");
const color4 = Color3.FromHexString("#111115");
this.createWall(new Vector3(0, 10, 10), new Vector3(0, 0, 0), color3, color4);
this.createWall(new Vector3(0, 10, -10), new Vector3(0, Math.PI, 0), color3, color4);
this.createWall(new Vector3(10, 10, 0), new Vector3(0, Math.PI / 2, 0), color1, color2);
this.createWall(new Vector3(-10, 10, 0), new Vector3(0, -Math.PI / 2, 0), color1, color2);
}
private createWall(position: Vector3, rotation: Vector3, color1: Color3, color2: Color3) {
const scene = this.scene;
const wall = MeshBuilder.CreatePlane("wall", {width: 20, height: 20}, scene);
wall.isPickable = false;
wall.isNearPickable = false;
wall.position = position;
wall.rotation = rotation;
wall.material = createGridMaterial(color1, color2);
return wall;
}
}
async function createPoints(divisions: number = 10, scale: number = 80) {
const scene = DefaultScene.Scene;
const half = .5;
const increment = 1 / divisions;
let x = -half;
let y = -half;
let z = -half;
const baseTransform = new TransformNode("baseTransform", scene);
baseTransform.scaling = new Vector3(scale, scale, scale);
baseTransform.position = new Vector3(0, scale / 2, 0);
const pcs = new PointsCloudSystem("pcs", 1, scene);
pcs.addPoints((divisions + 1) ** 3, function (particle) {
particle.position.x = x;
particle.position.y = y;
particle.position.z = z;
x += increment;
if (x > half) {
x = -half
y += increment;
if (y > half) {
y = -half;
z += increment;
if (z > half) {
}
}
}
});
const mesh = await pcs.buildMeshAsync();
mesh.visibility = .5;
mesh.parent = baseTransform;
}
function createGridMaterial(lineColor: Color3, mainColor: Color3): Material {
const scene = DefaultScene.Scene;
const material = new GridMaterial("gridMaterial", scene);
material.minorUnitVisibility = .1;
material.gridRatio = .1;
material.majorUnitFrequency = 10;
material.mainColor = mainColor;
material.lineColor = lineColor;
return material;
}
function createGrassGround(scene: Scene): Material {
const groundMaterial = new PBRMaterial("groundMaterial", scene);
const gText = new Texture("/assets/textures/grass1.jpeg", scene);
gText.uScale = 10;
gText.vScale = 10;
groundMaterial.albedoTexture = gText;
groundMaterial.metallic = 0;
groundMaterial.roughness = 1;
const grassBump = new Texture("/assets/textures/grassnormal.png", scene);
grassBump.uScale = 20;
grassBump.vScale = 20;
groundMaterial.bumpTexture =
grassBump;
return groundMaterial;
}