- Fix desktop camera to be directly above platform by resetting local position
- Increase label back offset from 0.001 to 0.005 to prevent z-fighting
- Use refreshBoundingInfo({}) for consistency with codebase
- Remove unused copyToPublic from pouchData.ts
- Remove dead DiagramEntityAdapter and integration/gizmo module
- Remove unused netlify functions and integration utilities
- Clean up unused imports and commented code across multiple files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
280 lines
9.9 KiB
TypeScript
280 lines
9.9 KiB
TypeScript
import {Color3, DynamicTexture, HemisphericLight, PointLight, Scene, StandardMaterial, Vector3} from "@babylonjs/core";
|
|
import {RenderingMode} from "./renderingMode";
|
|
|
|
export class LightmapGenerator {
|
|
private static lightmapCache: Map<string, DynamicTexture> = new Map();
|
|
private static readonly DEFAULT_RESOLUTION = 512;
|
|
|
|
// Toggle to enable/disable lightmap usage (for performance testing)
|
|
public static ENABLED = true;
|
|
|
|
// Current rendering mode
|
|
private static currentMode: RenderingMode = RenderingMode.UNLIT_WITH_EMISSIVE_TEXTURE;
|
|
|
|
// Scene lights for DIFFUSE_WITH_LIGHTS mode
|
|
private static hemisphericLight?: HemisphericLight;
|
|
private static pointLight?: PointLight;
|
|
|
|
/**
|
|
* Generates or retrieves cached lightmap for a given color
|
|
* @param color The base color for the lightmap
|
|
* @param scene The BabylonJS scene
|
|
* @param resolution Texture resolution (default: 512)
|
|
* @returns DynamicTexture with baked lighting
|
|
*/
|
|
public static generateLightmapForColor(
|
|
color: Color3,
|
|
scene: Scene,
|
|
resolution: number = LightmapGenerator.DEFAULT_RESOLUTION
|
|
): DynamicTexture {
|
|
const colorKey = color.toHexString();
|
|
|
|
// Return cached lightmap if available
|
|
if (this.lightmapCache.has(colorKey)) {
|
|
return this.lightmapCache.get(colorKey)!;
|
|
}
|
|
|
|
// Create new lightmap
|
|
const lightmap = this.createLightmap(color, scene, resolution);
|
|
this.lightmapCache.set(colorKey, lightmap);
|
|
return lightmap;
|
|
}
|
|
|
|
/**
|
|
* Pre-generates lightmaps for all specified colors
|
|
* Call during initialization for better first-render performance
|
|
* @param colors Array of hex color strings
|
|
* @param scene The BabylonJS scene
|
|
*/
|
|
public static preloadLightmaps(colors: string[], scene: Scene): void {
|
|
colors.forEach(colorHex => {
|
|
const color = Color3.FromHexString(colorHex);
|
|
this.generateLightmapForColor(color, scene);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a lightmap texture with simulated lighting
|
|
* Uses radial gradient to simulate top-left directional light
|
|
* @param color Base color
|
|
* @param scene BabylonJS scene
|
|
* @param resolution Texture size
|
|
* @returns DynamicTexture with baked lighting gradient
|
|
*/
|
|
private static createLightmap(
|
|
color: Color3,
|
|
scene: Scene,
|
|
resolution: number
|
|
): DynamicTexture {
|
|
const texture = new DynamicTexture(
|
|
`lightmap-${color.toHexString()}`,
|
|
resolution,
|
|
scene,
|
|
false // generateMipMaps
|
|
);
|
|
|
|
const ctx = texture.getContext();
|
|
const canvas = ctx.canvas.getContext('2d') as CanvasRenderingContext2D;
|
|
|
|
// Create radial gradient simulating directional light from top-left
|
|
// Offset the gradient center to create directional effect
|
|
const centerX = resolution * 0.4; // Offset left
|
|
const centerY = resolution * 0.4; // Offset up
|
|
const radius = resolution * 0.8; // Larger radius for smoother falloff
|
|
|
|
const gradient = canvas.createRadialGradient(
|
|
centerX, centerY, 0,
|
|
centerX, centerY, radius
|
|
);
|
|
|
|
// Calculate lit and shadow colors
|
|
// Lit area: 1.5x brighter than base color (clamped to 1.0)
|
|
const litColor = new Color3(
|
|
Math.min(color.r * 1.5, 1.0),
|
|
Math.min(color.g * 1.5, 1.0),
|
|
Math.min(color.b * 1.5, 1.0)
|
|
);
|
|
|
|
// Shadow area: 0.3x darker than base color
|
|
const shadowColor = color.scale(0.3);
|
|
|
|
// Mid-tone: base color unchanged
|
|
const midColor = color;
|
|
|
|
// Build gradient with multiple stops for smoother transition
|
|
gradient.addColorStop(0, litColor.toHexString()); // Center: bright
|
|
gradient.addColorStop(0.5, midColor.toHexString()); // Mid: base color
|
|
gradient.addColorStop(1.0, shadowColor.toHexString()); // Edge: dark
|
|
|
|
// Fill canvas with gradient
|
|
canvas.fillStyle = gradient;
|
|
canvas.fillRect(0, 0, resolution, resolution);
|
|
|
|
// Update texture with canvas content
|
|
texture.update();
|
|
|
|
return texture;
|
|
}
|
|
|
|
/**
|
|
* Clears the lightmap cache
|
|
* Useful for memory management or when regenerating lightmaps
|
|
*/
|
|
public static clearCache(): void {
|
|
this.lightmapCache.forEach(texture => texture.dispose());
|
|
this.lightmapCache.clear();
|
|
}
|
|
|
|
/**
|
|
* Gets the current cache size
|
|
* @returns Number of cached lightmaps
|
|
*/
|
|
public static getCacheSize(): number {
|
|
return this.lightmapCache.size;
|
|
}
|
|
|
|
/**
|
|
* Sets the rendering mode
|
|
* @param mode The rendering mode to use
|
|
*/
|
|
public static setRenderingMode(mode: RenderingMode): void {
|
|
this.currentMode = mode;
|
|
}
|
|
|
|
/**
|
|
* Gets the current rendering mode
|
|
* @returns Current rendering mode
|
|
*/
|
|
public static getRenderingMode(): RenderingMode {
|
|
return this.currentMode;
|
|
}
|
|
|
|
/**
|
|
* Applies the specified rendering mode to a material
|
|
* @param material The material to update
|
|
* @param color The base color
|
|
* @param mode The rendering mode to apply
|
|
* @param scene The BabylonJS scene
|
|
*/
|
|
public static applyRenderingModeToMaterial(
|
|
material: StandardMaterial,
|
|
color: Color3,
|
|
mode: RenderingMode,
|
|
scene: Scene
|
|
): void {
|
|
// Clear existing textures and properties
|
|
material.diffuseColor = new Color3(0, 0, 0);
|
|
material.emissiveColor = new Color3(0, 0, 0);
|
|
material.diffuseTexture = null;
|
|
material.emissiveTexture = null;
|
|
material.lightmapTexture = null;
|
|
|
|
switch (mode) {
|
|
case RenderingMode.LIGHTMAP_WITH_LIGHTING:
|
|
// Use diffuseColor + lightmapTexture with lighting enabled
|
|
material.diffuseColor = color;
|
|
material.lightmapTexture = this.generateLightmapForColor(color, scene);
|
|
material.useLightmapAsShadowmap = false;
|
|
material.disableLighting = false;
|
|
break;
|
|
|
|
case RenderingMode.UNLIT_WITH_EMISSIVE_TEXTURE:
|
|
// Use emissiveColor + emissiveTexture with lighting disabled
|
|
material.emissiveColor = color;
|
|
material.emissiveTexture = this.generateLightmapForColor(color, scene);
|
|
material.disableLighting = true;
|
|
break;
|
|
|
|
case RenderingMode.FLAT_EMISSIVE:
|
|
// Use only emissiveColor with lighting disabled
|
|
material.emissiveColor = color;
|
|
material.disableLighting = true;
|
|
break;
|
|
|
|
case RenderingMode.DIFFUSE_WITH_LIGHTS:
|
|
// Use diffuseColor with dynamic lighting enabled
|
|
material.diffuseColor = color;
|
|
material.disableLighting = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates or enables scene lights for DIFFUSE_WITH_LIGHTS mode
|
|
* @param scene The BabylonJS scene
|
|
*/
|
|
private static createSceneLights(scene: Scene): void {
|
|
if (!this.hemisphericLight) {
|
|
this.hemisphericLight = new HemisphericLight("renderModeHemiLight", new Vector3(0, 1, 0), scene);
|
|
this.hemisphericLight.intensity = 0.7;
|
|
}
|
|
|
|
if (!this.pointLight) {
|
|
this.pointLight = new PointLight("renderModePointLight", new Vector3(2, 3, 2), scene);
|
|
this.pointLight.intensity = 0.8;
|
|
}
|
|
|
|
this.hemisphericLight.setEnabled(true);
|
|
this.pointLight.setEnabled(true);
|
|
}
|
|
|
|
/**
|
|
* Disables scene lights used for DIFFUSE_WITH_LIGHTS mode
|
|
*/
|
|
private static disableSceneLights(): void {
|
|
if (this.hemisphericLight) {
|
|
this.hemisphericLight.setEnabled(false);
|
|
}
|
|
if (this.pointLight) {
|
|
this.pointLight.setEnabled(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates all materials in the scene to use the specified rendering mode
|
|
* @param scene The BabylonJS scene
|
|
* @param mode The rendering mode to apply
|
|
*/
|
|
public static updateAllMaterials(scene: Scene, mode: RenderingMode): void {
|
|
this.currentMode = mode;
|
|
|
|
// Enable or disable scene lights based on mode
|
|
if (mode === RenderingMode.DIFFUSE_WITH_LIGHTS) {
|
|
this.createSceneLights(scene);
|
|
} else {
|
|
this.disableSceneLights();
|
|
}
|
|
|
|
scene.materials.forEach(material => {
|
|
if (material instanceof StandardMaterial) {
|
|
// Skip UI materials and connections that should preserve their textures
|
|
// Check metadata first (most reliable), then fall back to name/id checks
|
|
if (material.metadata?.isUI === true ||
|
|
material.metadata?.isConnection === true || // Preserve connection animated arrow textures
|
|
material.name.startsWith('buttonMat') || // Use startsWith for unique button names
|
|
material.name === 'handleMaterial' ||
|
|
material.name === 'text-mat' ||
|
|
material.id.includes('button') ||
|
|
material.id.includes('handle') ||
|
|
material.id.includes('text')) {
|
|
return;
|
|
}
|
|
|
|
// Try to determine the base color from existing material
|
|
let baseColor: Color3;
|
|
|
|
if (material.emissiveColor && material.emissiveColor.toLuminance() > 0) {
|
|
baseColor = material.emissiveColor.clone();
|
|
} else if (material.diffuseColor && material.diffuseColor.toLuminance() > 0) {
|
|
baseColor = material.diffuseColor.clone();
|
|
} else {
|
|
// Skip materials without a color set
|
|
return;
|
|
}
|
|
|
|
this.applyRenderingModeToMaterial(material, baseColor, mode, scene);
|
|
}
|
|
});
|
|
}
|
|
}
|