From c6831ac80ecae908a18d6e6e1579032b1a5e554f Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Wed, 30 Sep 2026 03:50:02 -0500 Subject: [PATCH] fix(dashboard): light the UGC model view like the game Metal was drawn with metalness 1 in the room environment at half strength with one sun, so metal groups came out nearly black. The game's metal keeps the vertex color (lit, plus a reflection tinted by it), so metal is now part metal with a stronger reflection, the environment is brighter and a sky over ground fill lights the sides away from the sun. Co-Authored-By: Claude Opus 5.5 --- dDashboardServer/static/js/ugc-viewer.js | 21 +++++++++++++++++---- docs/UgcServer.md | 4 +++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/dDashboardServer/static/js/ugc-viewer.js b/dDashboardServer/static/js/ugc-viewer.js index 6468af26c..85d147b45 100644 --- a/dDashboardServer/static/js/ugc-viewer.js +++ b/dDashboardServer/static/js/ugc-viewer.js @@ -11,6 +11,18 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; import { parseModel, mergeMeshes, linearColors, metalOf, addGlitter, glitterSettings, SHADER_LOOK } from '/js/scenery-core.js'; +/** + * The game's metal (Metallic.fx) is not a mirror: it is the vertex color lit (0.3 ambient plus 0.7 N.L^4), plus the + * metal cube's reflection tinted by the vertex color (up to twice it, facing the sun), plus a sun highlight. So the + * brick's color stays visible and metal is about as bright as its plastic; a metalness of 1 in a dim room is nearly + * black. Part metal, a little glossier than plastic, with the room's reflection a little stronger. + */ +const METAL = { + none: { roughness: 0.6, metalness: 0 }, + polished: { roughness: 0.25, metalness: 0.5, envMapIntensity: 1.6 }, + brushed: { roughness: 0.45, metalness: 0.4, envMapIntensity: 1.4 }, +}; + export function createNifViewer(container) { const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); @@ -22,9 +34,11 @@ export function createNifViewer(container) { scene.background = new THREE.Color(0x1e2126); const pmrem = new THREE.PMREMGenerator(renderer); scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.04).texture; - scene.environmentIntensity = 0.5; + scene.environmentIntensity = 0.8; const sun = new THREE.DirectionalLight(0xffffff, 1.4); - scene.add(sun, sun.target); + // A sky over ground fill, so no side of a model (a baseplate's underside, the side away from the sun) is black + const sky = new THREE.HemisphereLight(0xffffff, 0x8a8f99, 0.6); + scene.add(sun, sun.target, sky); const camera = new THREE.PerspectiveCamera(40, 1, 0.05, 20000); const controls = new OrbitControls(camera, renderer.domElement); @@ -130,8 +144,7 @@ export function createNifViewer(container) { const material = sparkles ? new THREE.MeshLambertMaterial({ color: baseColor.clone(), vertexColors: hasColors }) : look & SHADER_LOOK.EMISSIVE ? new THREE.MeshBasicMaterial({ color: baseColor.clone(), vertexColors: hasColors }) - : new THREE.MeshStandardMaterial({ color: baseColor.clone(), vertexColors: hasColors, transparent: seeThrough, - roughness: metal === 'polished' ? 0.18 : metal === 'brushed' ? 0.45 : 0.6, metalness: metal ? 1 : 0 }); + : new THREE.MeshStandardMaterial({ color: baseColor.clone(), vertexColors: hasColors, transparent: seeThrough, ...METAL[metal || 'none'] }); // Glitter: the fleck texture's UVs, one tile of it each; the sparkle shapes' UVs, one sparkle tile each let glitter = null; const settings = glitterSettings(); diff --git a/docs/UgcServer.md b/docs/UgcServer.md index c309c97cb..fdbe4f235 100644 --- a/docs/UgcServer.md +++ b/docs/UgcServer.md @@ -479,7 +479,9 @@ client's 88, 89 and 46) and draws glow at its plain color (by `glow_emissive`, u light, a sky over dark ground reflection tinted by its color and a sun highlight (sharp for polished, broad for brushed). This is an approximation of the game's environment maps. `NifFile::ShaderLookFor` gives 98 `REFLECTIVE`, 99 `REFLECTIVE | BRUSHED` and 53 `EMISSIVE`; the UGC page's 3D view gets each mesh's look (`/api/ugc/mesh`, "look") -and draws metal as reflective (metalness 1, the view's environment) and glow unlit, and the zone views draw +and draws metal as the game does, its color kept (part metal: metalness 0.5 polished, 0.4 brushed, with a stronger +reflection of the view's environment; a metalness of 1 in the view's dim room was nearly black) and glow unlit, under +a sun and a sky over ground fill so no side of a model is black, and the zone views draw LEGO-Emissive objects going to their vertex color by its alpha (metal there stays lit like the rest). #### Glitter