mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
feat(dashboard): metal and glow looks in the UGC and zone 3D views
NifFile::ShaderLookFor knows Polished Metal (98), Brushed Steel (99) and LEGO-Emissive (53). The UGC mesh route sends each mesh's look (from its multishader tag), and the UGC 3D view draws metal as reflective and glow unlit. The zone views draw LEGO-Emissive objects going to their vertex color by its alpha, as the shader does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -687,7 +687,7 @@ namespace NifFile {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ShaderLookFor(int32_t shader) {
|
||||
uint16_t ShaderLookFor(int32_t shader) {
|
||||
// By the technique each shader class sets up (ShaderManager's factory table at 0x01889608, indexed by gameValue;
|
||||
// the class's technique setup names it). Checked in the client: 33 and 82 Technique_Basic_NoLighting_VertColor_
|
||||
// NoTexture, 35 and 84 Technique_Basic_NoLighting_VertColor, 37 Technique_Basic_Lighting_VertColor_NoTexture,
|
||||
@@ -730,6 +730,15 @@ namespace NifFile {
|
||||
case 8: case 10: case 54: case 15: case 23: case 35: case 52: case 57: case 62: case 68: case 70: case 73: case 81:
|
||||
case 84: case 87: case 101:
|
||||
return UNLIT;
|
||||
// Polished Metal (Technique_Lighting_PolishedMetal_VertColor in Metallic.fx) and Brushed Steel (its noise
|
||||
// in object space); both load their reflection textures themselves
|
||||
case 98:
|
||||
return REFLECTIVE;
|
||||
case 99:
|
||||
return REFLECTIVE | BRUSHED;
|
||||
// LEGO-Emissive
|
||||
case 53:
|
||||
return EMISSIVE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -744,7 +753,7 @@ namespace NifFile {
|
||||
return Parser(data, 0).Dds(block, error);
|
||||
}
|
||||
|
||||
std::string Encode(const Model& model, const std::vector<std::string>& textures, const std::vector<std::string>& darkTextures) {
|
||||
std::string Encode(const Model& model, const std::vector<std::string>& textures, const std::vector<std::string>& darkTextures, const std::vector<uint16_t>& looks) {
|
||||
std::string body;
|
||||
nlohmann::json meshes = nlohmann::json::array();
|
||||
std::vector<std::string> names;
|
||||
@@ -771,6 +780,7 @@ namespace NifFile {
|
||||
{"vertexColors", material.vertexColorMode}, {"texture", textureIndex}, {"clampU", material.clampU}, {"clampV", material.clampV},
|
||||
{"shaderTag", material.shaderTag}, {"darkTexture", uv2 ? darkIndex : -1}, {"uv2", uv2}
|
||||
};
|
||||
if (m < looks.size()) entry["look"] = looks[m];
|
||||
Append(body, mesh.positions.data(), mesh.positions.size() * sizeof(float));
|
||||
if (!mesh.normals.empty()) {
|
||||
std::vector<int8_t> packed(mesh.normals.size());
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace NifFile {
|
||||
* BasicShaders.fx, LEGOPPLighting.fx and Ocean.fx). Fixed function (-1) has none of these: Gamebryo lights it with
|
||||
* the material and NiVertexColorProperty.
|
||||
*/
|
||||
enum eShaderLook : uint8_t {
|
||||
enum eShaderLook : uint16_t {
|
||||
UNLIT = 1, // no lighting: the colors as they are (the "NoLighting" techniques)
|
||||
NO_TEXTURE = 2, // the texture isn't sampled ("NoTexture")
|
||||
NO_VERTEX_COLORS = 4, // vertex colors aren't read
|
||||
@@ -91,11 +91,18 @@ namespace NifFile {
|
||||
// Two textures (base and dark, each with its UV set): blended by the vertex alpha, which is then no opacity
|
||||
// ("Two Layers Blended"), or added, weighted by the material's diffuse red and green ("Two Textures Added")
|
||||
TWO_LAYERS_BLENDED = 16,
|
||||
TWO_LAYERS_ADDED = 32
|
||||
TWO_LAYERS_ADDED = 32,
|
||||
// Metal (Metallic.fx): the lit color plus an environment map (textures/metal/metal_reflection_*.dds, which the
|
||||
// shader loads itself) tinted by the vertex color, polished or, with BRUSHED, brushed with object space noise
|
||||
REFLECTIVE = 64,
|
||||
BRUSHED = 128,
|
||||
// LEGO-Emissive: lerp(lit, vertex color, vertex alpha * NiMaterialProperty's emissive red); the vertex alpha is
|
||||
// that mask, not opacity
|
||||
EMISSIVE = 256
|
||||
};
|
||||
|
||||
// eShaderLook bits of a shader (mapShaders.gameValue); 0 for the usual lit look and for fixed function
|
||||
uint8_t ShaderLookFor(int32_t shader);
|
||||
uint16_t ShaderLookFor(int32_t shader);
|
||||
|
||||
// Where a node is in the model's space: p' = rotation * p + translation (row-major, scale folded in)
|
||||
struct NodeTransform {
|
||||
@@ -125,9 +132,11 @@ namespace NifFile {
|
||||
* int8 normals (3 per vertex, times 127, padded to 4 bytes) when "normals", float32 UVs (2 per vertex) when "uv",
|
||||
* float32 dark texture UVs when "uv2", uint8 RGBA colors when "colors", then uint16 indices (padded to 4 bytes).
|
||||
* `textures[i]` is where mesh i's texture is (empty: none) and `darkTextures[i]` its dark texture; the header
|
||||
* lists each once in "textures" and a mesh's "texture" and "darkTexture" index it (-1: none).
|
||||
* lists each once in "textures" and a mesh's "texture" and "darkTexture" index it (-1: none). `looks[i]`, when
|
||||
* given, is mesh i's "look" (eShaderLook bits of the shader it is drawn with, for views without a scenery manifest).
|
||||
*/
|
||||
std::string Encode(const Model& model, const std::vector<std::string>& textures, const std::vector<std::string>& darkTextures = {});
|
||||
std::string Encode(const Model& model, const std::vector<std::string>& textures, const std::vector<std::string>& darkTextures = {},
|
||||
const std::vector<uint16_t>& looks = {});
|
||||
|
||||
/**
|
||||
* A texture stored inside a .nif (NiPixelData or NiPersistentSrcTextureRendererData, block `block`) as a DDS file
|
||||
|
||||
@@ -867,6 +867,16 @@ namespace Scenery {
|
||||
return zone->json;
|
||||
}
|
||||
|
||||
std::vector<uint16_t> MultishaderLooks(const NifFile::Model& model) {
|
||||
std::vector<uint16_t> looks;
|
||||
for (const auto& mesh : model.meshes) {
|
||||
std::optional<int32_t> shader;
|
||||
if (const auto it = g_ShaderValues.find(mesh.material.shaderTag); it != g_ShaderValues.end()) shader = it->second;
|
||||
looks.push_back(NifFile::ShaderLookFor(NifFile::MultishaderPart(shader)));
|
||||
}
|
||||
return looks;
|
||||
}
|
||||
|
||||
bool ZoneReady(uint32_t zoneId) {
|
||||
return g_Zones.Ready(zoneId);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct HTTPReply;
|
||||
struct HTTPContext;
|
||||
@@ -11,6 +12,10 @@ namespace WorldScene {
|
||||
struct Object;
|
||||
}
|
||||
|
||||
namespace NifFile {
|
||||
struct Model;
|
||||
}
|
||||
|
||||
/**
|
||||
* A zone's scenery for the 3D views, drawn from the game client's files the way the client draws it: every object in
|
||||
* the zone's scene files (.lvl) with the model of its render component (RenderComponent.render_asset, a .nif or a
|
||||
@@ -46,6 +51,13 @@ namespace Scenery {
|
||||
// Whether ZoneJson is built (so answering it is quick)
|
||||
bool ZoneReady(uint32_t zoneId);
|
||||
|
||||
/**
|
||||
* The eShaderLook bits (NifFile::ShaderLookFor) of each mesh of a multishader model (a player model: RenderComponent
|
||||
* shader Multishader), from its parts' tags (mapShaders ids, read by Preload); for NifFile::Encode's `looks`.
|
||||
* Any thread.
|
||||
*/
|
||||
std::vector<uint16_t> MultishaderLooks(const NifFile::Model& model);
|
||||
|
||||
/**
|
||||
* The zone's flairs (the grass, flowers and small rocks its terrain file strews over it, models from FlairTable) as a
|
||||
* manifest in ZoneJson's form, whose models the same mesh and texture routes serve, plus: distance (how far from the
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Database.h"
|
||||
#include "UgcIconParams.h"
|
||||
#include "NifFile.h"
|
||||
#include "Scenery.h"
|
||||
#include "SettingsCatalog.h"
|
||||
#include "SettingsHistory.h"
|
||||
#include "UgcAssemblies.h"
|
||||
@@ -477,7 +478,7 @@ namespace UgcRoutes {
|
||||
});
|
||||
|
||||
Route(eHTTPMethod::GET, "/api/ugc/mesh/:id", Perm("properties_view"),
|
||||
"A player model's generated .nif converted for the 3D view (NifFile::Encode, as the scenery meshes). Query: ?lod=0 (most detailed) "
|
||||
"A player model's generated .nif converted for the 3D view (NifFile::Encode, as the scenery meshes, with each mesh's shader look). Query: ?lod=0 (most detailed) "
|
||||
"to 3, &version=current|previous, &ao=0 for the mesh before the lighting bake. The header adds triangles and vertices",
|
||||
[](HTTPReply& reply, const HTTPContext& context) {
|
||||
const auto id = PathId<LWOOBJID>(context.path, 3);
|
||||
@@ -495,7 +496,7 @@ namespace UgcRoutes {
|
||||
if (!model) return JsonError(out, eHTTPStatusCode::UNPROCESSABLE_ENTITY, "The .nif can't be read: " + error);
|
||||
out.status = eHTTPStatusCode::OK;
|
||||
out.contentType = eContentType::APPLICATION_OCTET_STREAM;
|
||||
out.message = NifFile::Encode(*model, std::vector<std::string>(model->meshes.size()));
|
||||
out.message = NifFile::Encode(*model, std::vector<std::string>(model->meshes.size()), {}, Scenery::MultishaderLooks(*model));
|
||||
out.headers.push_back("Cache-Control: private, no-cache");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,13 +67,16 @@ export function shaderOf(manifest, asset, mesh) {
|
||||
}
|
||||
|
||||
// NifFile::eShaderLook bits
|
||||
export const SHADER_LOOK = { UNLIT: 1, NO_TEXTURE: 2, NO_VERTEX_COLORS: 4, MATERIAL_COLOR: 8, TWO_LAYERS_BLENDED: 16, TWO_LAYERS_ADDED: 32 };
|
||||
export const SHADER_LOOK = { UNLIT: 1, NO_TEXTURE: 2, NO_VERTEX_COLORS: 4, MATERIAL_COLOR: 8, TWO_LAYERS_BLENDED: 16, TWO_LAYERS_ADDED: 32, REFLECTIVE: 64, BRUSHED: 128, EMISSIVE: 256 };
|
||||
|
||||
/**
|
||||
* How a mesh is drawn under the game's shaders, when the manifest has the zone's lighting: {lit, texture,
|
||||
* vertexColors, material, layers} — whether the scene's sun and ambient light it, its texture and vertex colors are
|
||||
* used, whether its NiMaterialProperty colors are (only fixed function and the "Material" shaders use them), and how
|
||||
* a two layer shader puts its dark texture with the base one ('blended', 'added' or null). Null without
|
||||
* a two layer shader puts its dark texture with the base one ('blended', 'added' or null), whether it is metal
|
||||
* ('polished', 'brushed' or null: an environment reflection tinted by the vertex color) and whether it glows
|
||||
* (LEGO-Emissive: the lit color goes to the vertex color by the vertex alpha times the material's emissive red, so
|
||||
* the vertex alpha is no opacity). Null without
|
||||
* lighting in the manifest (older servers), for the viewer's own lights.
|
||||
*/
|
||||
export function gameLook(manifest, asset, mesh) {
|
||||
@@ -87,10 +90,18 @@ export function gameLook(manifest, asset, mesh) {
|
||||
// Fixed function reads them as NiVertexColorProperty says; the shaders always do, unless they have none
|
||||
vertexColors: !!(mesh.colors && !(bits & SHADER_LOOK.NO_VERTEX_COLORS) && (!fixedFunction || mesh.vertexColors !== 0)),
|
||||
material: fixedFunction || !!(bits & SHADER_LOOK.MATERIAL_COLOR),
|
||||
layers: bits & SHADER_LOOK.TWO_LAYERS_BLENDED ? 'blended' : bits & SHADER_LOOK.TWO_LAYERS_ADDED ? 'added' : null
|
||||
layers: bits & SHADER_LOOK.TWO_LAYERS_BLENDED ? 'blended' : bits & SHADER_LOOK.TWO_LAYERS_ADDED ? 'added' : null,
|
||||
metal: metalOf(bits),
|
||||
emissive: !!(bits & SHADER_LOOK.EMISSIVE)
|
||||
};
|
||||
}
|
||||
|
||||
// A shader's metal from its eShaderLook bits: 'polished', 'brushed' or null
|
||||
export function metalOf(bits) {
|
||||
if (!(bits & SHADER_LOOK.REFLECTIVE)) return null;
|
||||
return bits & SHADER_LOOK.BRUSHED ? 'brushed' : 'polished';
|
||||
}
|
||||
|
||||
/**
|
||||
* Meshes of a model that look the same (texture, colors, blending, sides, attributes) joined into one, so a model
|
||||
* made of many pieces (the zones' "glom" files have over a hundred) costs a few draw calls instead of one per piece.
|
||||
|
||||
@@ -247,10 +247,18 @@ export function createScenery({ scene, camera, renderer, urls, focus, onProgress
|
||||
gameNormal = normalize( mat3( modelMatrix ) * gameNormal );
|
||||
vGameLight = pow( clamp( gameLightColor * max( 0.0, dot( gameNormal, gameLightVec ) ) + gameAmbient, 0.0, 1.0 ), vec3( 2.2 ) );`;
|
||||
|
||||
// LEGO-Emissive: the lit color goes to the vertex color by the vertex alpha times the material's emissive red
|
||||
const EMISSIVE_FRAGMENT = `
|
||||
#ifdef USE_COLOR_ALPHA
|
||||
outgoingLight = mix( outgoingLight, vColor.rgb, clamp( vColor.a * emissiveMix, 0.0, 1.0 ) );
|
||||
#endif
|
||||
#include <opaque_fragment>`;
|
||||
|
||||
/**
|
||||
* A material that draws a mesh the way its game shader does (gameLook): unlit by the view's own lights and tone
|
||||
* mapping, the zone's sun and ambient light per vertex when the shader is lit, the material's color only when the
|
||||
* shader reads it.
|
||||
* shader reads it. Metal is drawn lit like the rest (the game adds a reflection of its own textures); glowing
|
||||
* meshes go to their vertex color as the emissive shader does.
|
||||
*/
|
||||
function gameMaterial(options, mesh, alphaMode, look, darkMap = null) {
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
@@ -261,7 +269,12 @@ export function createScenery({ scene, camera, renderer, urls, focus, onProgress
|
||||
const layers = darkMap ? look.layers : null;
|
||||
// TwoLayersAdded_PS: base * material diffuse red + dark * material diffuse green (their animations)
|
||||
const weights = new THREE.Vector2(mesh.diffuse[0], mesh.diffuse[1]);
|
||||
const emissive = !!look.emissive;
|
||||
material.onBeforeCompile = (shader) => {
|
||||
if (emissive) {
|
||||
shader.uniforms.emissiveMix = { value: mesh.emissive[0] };
|
||||
shader.fragmentShader = 'uniform float emissiveMix;\n' + shader.fragmentShader.replace('#include <opaque_fragment>', EMISSIVE_FRAGMENT);
|
||||
}
|
||||
if (layers) {
|
||||
shader.uniforms.darkMap = { value: darkMap };
|
||||
shader.uniforms.layerWeights = { value: weights };
|
||||
@@ -280,7 +293,7 @@ export function createScenery({ scene, camera, renderer, urls, focus, onProgress
|
||||
shader.fragmentShader = 'varying vec3 vGameLight;\n' +
|
||||
shader.fragmentShader.replace('#include <aomap_fragment>', '#include <aomap_fragment>\n\treflectedLight.indirectDiffuse *= vGameLight;');
|
||||
};
|
||||
material.customProgramCacheKey = () => 'game:' + (options.map ? alphaMode : '') + ':' + look.lit + ':' + layers;
|
||||
material.customProgramCacheKey = () => 'game:' + (options.map ? alphaMode : '') + ':' + look.lit + ':' + layers + ':' + emissive;
|
||||
return material;
|
||||
}
|
||||
|
||||
@@ -292,7 +305,8 @@ export function createScenery({ scene, camera, renderer, urls, focus, onProgress
|
||||
let vertexAlpha = false;
|
||||
// A two layer blend reads the vertex alpha as the mix of its textures, not as opacity
|
||||
const layersBlended = !!(darkMap && look.layers === 'blended');
|
||||
if (vertexColors && !layersBlended) for (let i = 3; i < mesh.colors.length && !vertexAlpha; i += 4) vertexAlpha = mesh.colors[i] < 250;
|
||||
// ... and the emissive shader as how much the vertex color glows
|
||||
if (vertexColors && !layersBlended && !(look && look.emissive)) for (let i = 3; i < mesh.colors.length && !vertexAlpha; i += 4) vertexAlpha = mesh.colors[i] < 250;
|
||||
const textureAlpha = alphaMode === 'opacity' && !!(map && map.userData.alpha);
|
||||
// The game's shaders take alpha from the vertex colors and texture only; NiMaterialProperty's is for fixed function
|
||||
const materialAlpha = look && !look.material ? 1 : mesh.alpha;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
/**
|
||||
* A 3D view of a .nif the UGC server made, from /api/ugc/mesh/:id (the dashboard converts it with NifFile::Encode,
|
||||
* as it does the scenery's models), with wireframe and vertex color switches and triangle counts.
|
||||
* as it does the scenery's models), with wireframe and vertex color switches and triangle counts. The metal and glow
|
||||
* groups (the UGC server's shader settings; each mesh's "look" is its shader's eShaderLook bits) are drawn as metal
|
||||
* reflecting the view's environment and as unlit glow.
|
||||
*/
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
|
||||
import { parseModel, mergeMeshes, linearColors } from '/js/scenery-core.js';
|
||||
import { parseModel, mergeMeshes, linearColors, metalOf, SHADER_LOOK } from '/js/scenery-core.js';
|
||||
|
||||
export function createNifViewer(container) {
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
@@ -116,7 +118,13 @@ export function createNifViewer(container) {
|
||||
// Blending only matters where something is see-through (every shape of a brick model blends)
|
||||
let seeThrough = !!mesh.blend && mesh.alpha < 0.99;
|
||||
if (mesh.blend && hasColors) for (let i = 3; i < mesh.colors.length && !seeThrough; i += 4) seeThrough = mesh.colors[i] < 250;
|
||||
const material = new THREE.MeshStandardMaterial({ color: baseColor.clone(), vertexColors: hasColors, transparent: seeThrough, roughness: 0.6, metalness: 0 });
|
||||
const look = mesh.look || 0;
|
||||
const metal = metalOf(look);
|
||||
// Glow: the emissive shader's vertex color, unlit (its vertex alpha is the glow, not opacity)
|
||||
const material = 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 });
|
||||
const object = new THREE.Mesh(geometry, material);
|
||||
if (seeThrough) object.renderOrder = 1;
|
||||
root.add(object);
|
||||
|
||||
@@ -492,6 +492,31 @@ TEST(NifFileTests, KnowsWhatEachShaderLeavesOut) {
|
||||
EXPECT_EQ(NifFile::ShaderLookFor(37), NifFile::NO_TEXTURE); // Basic VC NT
|
||||
EXPECT_EQ(NifFile::ShaderLookFor(70), NifFile::UNLIT); // ScrollingUV_NoLight_AnimAlpha
|
||||
EXPECT_EQ(NifFile::ShaderLookFor(32), NifFile::UNLIT | NifFile::NO_VERTEX_COLORS | NifFile::MATERIAL_COLOR); // Basic NL Material
|
||||
// The UGC server's metal and glow groups: Polished Metal, Brushed Steel, LEGO-Emissive
|
||||
EXPECT_EQ(NifFile::ShaderLookFor(98), NifFile::REFLECTIVE);
|
||||
EXPECT_EQ(NifFile::ShaderLookFor(99), NifFile::REFLECTIVE | NifFile::BRUSHED);
|
||||
EXPECT_EQ(NifFile::ShaderLookFor(53), NifFile::EMISSIVE);
|
||||
// Through a multishader tag's gameValue (S88 -> 98), as a player model's parts are drawn
|
||||
EXPECT_EQ(NifFile::ShaderLookFor(NifFile::MultishaderPart(98)), NifFile::REFLECTIVE);
|
||||
EXPECT_EQ(NifFile::ShaderLookFor(NifFile::MultishaderPart(std::nullopt)), 0);
|
||||
}
|
||||
|
||||
TEST(NifFileTests, EncodesEachMeshsLook) {
|
||||
NifFile::Model model;
|
||||
model.meshes.resize(2);
|
||||
for (auto& mesh : model.meshes) {
|
||||
mesh.positions = { 0, 0, 0, 1, 0, 0, 0, 1, 0 };
|
||||
mesh.indices = { 0, 1, 2 };
|
||||
}
|
||||
const auto header = [](const std::string& encoded) {
|
||||
uint32_t length = 0;
|
||||
std::memcpy(&length, encoded.data(), 4);
|
||||
return nlohmann::json::parse(encoded.substr(4, length));
|
||||
};
|
||||
const auto with = header(NifFile::Encode(model, { "", "" }, {}, { NifFile::REFLECTIVE, NifFile::EMISSIVE }));
|
||||
EXPECT_EQ(with["meshes"][0]["look"], NifFile::REFLECTIVE);
|
||||
EXPECT_EQ(with["meshes"][1]["look"], NifFile::EMISSIVE);
|
||||
EXPECT_FALSE(header(NifFile::Encode(model, { "", "" }))["meshes"][0].contains("look"));
|
||||
}
|
||||
|
||||
// The game client's own meshes, when a client is configured (DLU_CLIENT_RES, else client_location in the build's
|
||||
|
||||
@@ -33,13 +33,13 @@ same(S.textureAlphaMode(manifest, 1, { shaderTag: 1 }), 'decal', 'LEGO part text
|
||||
same(S.textureAlphaMode(manifest, 1, { shaderTag: 30 }), 'opacity', 'Basic VC part texture alpha');
|
||||
|
||||
// Lit, textured, vertex colors, no material colors: Basic VC
|
||||
same(S.gameLook(manifest, 1, { ...colored, shaderTag: 30 }), { lit: true, texture: true, vertexColors: true, material: false, layers: null }, 'Basic VC');
|
||||
same(S.gameLook(manifest, 1, { ...colored, shaderTag: 30 }), { lit: true, texture: true, vertexColors: true, material: false, layers: null, metal: null, emissive: false }, 'Basic VC');
|
||||
// Vertex colors are read even when NiVertexColorProperty ignores them, but only if the mesh has some
|
||||
same(S.gameLook(manifest, 0, { ...colored, vertexColors: 0 }).vertexColors, true, 'shader reads vertex colors');
|
||||
same(S.gameLook(manifest, 0, {}).vertexColors, false, 'mesh without vertex colors');
|
||||
same(S.gameLook(manifest, 3, colored), { lit: false, texture: false, vertexColors: true, material: false, layers: null }, 'Basic NL VC NT');
|
||||
same(S.gameLook(manifest, 3, colored), { lit: false, texture: false, vertexColors: true, material: false, layers: null, metal: null, emissive: false }, 'Basic NL VC NT');
|
||||
// Fixed function: NiVertexColorProperty and the material decide
|
||||
same(S.gameLook(manifest, 2, { ...colored, vertexColors: 0 }), { lit: true, texture: true, vertexColors: false, material: true, layers: null }, 'fixed function');
|
||||
same(S.gameLook(manifest, 2, { ...colored, vertexColors: 0 }), { lit: true, texture: true, vertexColors: false, material: true, layers: null, metal: null, emissive: false }, 'fixed function');
|
||||
// Without the zone's lighting the viewer lights scenery itself
|
||||
same(S.gameLook({ ...manifest, lighting: null }, 0, colored), null, 'no lighting');
|
||||
|
||||
@@ -63,6 +63,15 @@ same(S.gameLook(layered, 0, colored).layers, 'blended', 'two layers blended');
|
||||
same(S.gameLook(layered, 1, colored).layers, 'added', 'two layers added');
|
||||
same(S.gameLook(manifest, 0, colored).layers, null, 'one layer');
|
||||
|
||||
// A player model's metal and glow groups (UGC server shader settings): Polished Metal, Brushed Steel, LEGO-Emissive
|
||||
const shiny = { ...manifest, shaders: [9999], shaderTags: { 1: 5, 88: 98, 89: 99, 46: 53 },
|
||||
shaderLooks: { 98: S.SHADER_LOOK.REFLECTIVE, 99: S.SHADER_LOOK.REFLECTIVE | S.SHADER_LOOK.BRUSHED, 53: S.SHADER_LOOK.EMISSIVE } };
|
||||
same(S.gameLook(shiny, 0, { ...colored, shaderTag: 88 }).metal, 'polished', 'polished metal');
|
||||
same(S.gameLook(shiny, 0, { ...colored, shaderTag: 89 }).metal, 'brushed', 'brushed steel');
|
||||
same(S.gameLook(shiny, 0, { ...colored, shaderTag: 46 }).emissive, true, 'emissive');
|
||||
same(S.gameLook(shiny, 0, { ...colored, shaderTag: 1 }).metal, null, 'plastic');
|
||||
same([S.metalOf(0), S.metalOf(S.SHADER_LOOK.REFLECTIVE), S.metalOf(S.SHADER_LOOK.REFLECTIVE | S.SHADER_LOOK.BRUSHED)], [null, 'polished', 'brushed'], 'metal of look bits');
|
||||
|
||||
// The near plane grows with the distance, within limits
|
||||
same([S.nearPlaneFor(10), S.nearPlaneFor(2000), S.nearPlaneFor(100000)], [0.5, 5, 20], 'near plane');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user