Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 258 additions & 1 deletion src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -4258,4 +4258,261 @@ input[type=number]::-webkit-outer-spin-button {
.mv-go-to-model:hover {
background: rgba(255, 255, 255, 0.2);
color: rgba(255, 255, 255, 0.9);
}
}

/* Combined Models/Creatures tab layout */
.sidebar .sidebar-tabs {
display: flex;
margin-bottom: 10px;
}
.sidebar .sidebar-tabs li {
flex: 1;
text-align: center;
padding: 8px 4px;
font-size: 13px;
}
.sidebar .sidebar-hint {
display: block;
margin-top: 10px;
font-size: 13px;
line-height: 1.5;
color: var(--font-faded);
}
#creature-preview {
border: none;
background: none;
box-shadow: none;
}
#tab-creatures .viewer-title {
position: absolute;
top: 16px;
left: 50%;
transform: translateX(-50%);
z-index: 2;
pointer-events: none;
max-width: 55%;
text-align: center;
font-size: 14px;
font-weight: bold;
color: var(--font-highlight);
background: var(--background-trans);
padding: 5px 14px;
border-radius: 6px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}
#tab-creatures .viewer-empty {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
pointer-events: none;
font-size: 16px;
color: var(--font-faded);
}
#tab-creatures.tab.list-tab {
/* keep the base .tab absolute inset:0 fill; just drop the 3-column grid */
display: block;
}
#tab-creatures .preview-container {
position: absolute;
inset: 0;
margin: 0;
}
#tab-creatures #creature-preview {
width: 100%;
height: 100%;
}
#tab-creatures .list-container {
position: absolute;
left: 20px;
top: 20px;
bottom: 74px;
width: 360px;
margin: 0;
z-index: 2;
}
#tab-creatures .filter {
position: absolute;
left: 20px;
bottom: 20px;
width: 360px;
margin: 0;
z-index: 2;
}
#tab-creatures .filter input {
margin: 0;
}
#tab-creatures .sidebar {
position: absolute;
right: 0;
top: 20px;
bottom: 74px;
width: 250px;
margin: 0;
padding: 14px 16px;
box-sizing: border-box;
overflow-y: auto;
z-index: 2;
background: var(--background-trans);
border-radius: 10px 0 0 10px;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.45);
backdrop-filter: blur(3px);
}
#tab-creatures .preview-controls {
position: absolute;
right: 20px;
bottom: 20px;
margin: 0;
z-index: 3;
}
#tab-creatures #texture-ribbon {
top: auto;
bottom: 20px;
left: 50%;
right: auto;
transform: translateX(-50%);
/* width must not derive from content: the slot count is computed from the
measured width (ResizeLayer), so a shrink-to-fit width feeds back into
itself and oscillates, making the ribbon flicker */
width: 45%;
/* cap so the centered pill clears the floating list panel (right edge at
380px) on narrow windows: left edge stays at >= 390px */
max-width: calc(100% - 780px);
background: var(--background-trans);
border-radius: 8px;
padding: 5px 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tab-creatures #texture-ribbon #texture-ribbon-next {
pointer-events: auto;
}
#tab-creatures .list-status.with-quick-filters {
height: auto;
min-height: 27px;
flex-wrap: wrap;
row-gap: 1px;
column-gap: 10px;
font-size: 11.5px;
align-content: center;
padding-bottom: 4px;
}
#tab-creatures .list-container .ui-listbox {
bottom: 45px;
}
#tab-creatures .browse-mode-switch {
position: absolute;
left: 20px;
top: 20px;
z-index: 3;
display: flex;
}
#tab-creatures .browse-mode-switch li {
padding: 7px 16px;
font-size: 13px;
}
#tab-creatures .list-container {
top: 64px;
}
#tab-creatures .sidebar {
top: 64px;
}
#creature-sidebar .header {
text-transform: uppercase;
font-size: 11px;
letter-spacing: 0.6px;
font-weight: bold;
color: var(--font-faded);
margin: 14px 0 6px;
}
#creature-sidebar > div > .header:first-child {
margin-top: 4px;
}
#tab-creatures .preview-dropdown-overlay {
left: 400px;
top: 20px;
}
#tab-creatures {
grid-template-columns: 1fr 1fr auto;
}
#tab-creatures #creature-sidebar .ui-checkboxlist, #tab-creatures #creature-sidebar .ui-listbox {
height: 156px;
}
#tab-creatures #creature-sidebar .list-toggles {
font-size: 14px;
text-align: center;
margin-top: 5px;
}

/* Adjustable viewer light + zone lighting controls */
.zone-light-time {
margin-top: 8px;
display: flex;
flex-direction: column;
gap: 4px;
}
.zone-light-time .prefix-label {
font-size: 13px;
color: var(--font-primary);
}
.light-control {
margin-top: 8px;
display: flex;
flex-direction: column;
gap: 4px;
}
.light-control .prefix-label {
font-size: 13px;
color: var(--font-primary);
}
.light-control input[type=range] {
width: 100%;
}
.light-gizmo-wrap {
margin-top: 8px;
display: flex;
flex-direction: column;
gap: 6px;
align-items: flex-start;
}
.light-gizmo {
position: relative;
width: 130px;
height: 96px;
border: 1px solid var(--border);
border-radius: 6px;
background: radial-gradient(circle at 50% 0%, rgba(255, 255, 255, 0.08), transparent 70%), var(--background-dark);
cursor: crosshair;
overflow: hidden;
}
.light-gizmo::before, .light-gizmo::after {
content: '';
position: absolute;
background: rgba(255, 255, 255, 0.08);
}
.light-gizmo::before { left: 50%; top: 0; bottom: 0; width: 1px; }
.light-gizmo::after { top: 50%; left: 0; right: 0; height: 1px; }
.light-gizmo-dot {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
background: #f0c850;
box-shadow: 0 0 8px 2px rgba(240, 200, 80, 0.7);
transform: translate(-50%, -50%);
pointer-events: none;
}
.light-gizmo-readout {
font-size: 12px;
color: var(--font-faded);
}
.light-reset-btn {
margin-top: 10px;
font-size: 13px;
padding: 6px 12px;
}
9 changes: 9 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const ExternalLinks = require('./js/external-links');
const textureRibbon = require('./js/ui/texture-ribbon');
const Shaders = require('./js/3D/Shaders');
const gpuInfo = require('./js/gpu-info');
const zoneLighting = require('./js/3D/zone-lighting');

const Vue = require('vue/dist/vue.cjs.js');
window.Vue = Vue;
Expand Down Expand Up @@ -582,6 +583,14 @@ document.addEventListener('click', function(e) {
log.write('No export directory set, setting to %s', core.view.config.exportDirectory);
}

// Drive real zone lighting (LightData) for the model/character preview.
zoneLighting.set_map(core.view.config.zoneLightMapId);
zoneLighting.set_time(core.view.config.zoneLightTime);
zoneLighting.set_enabled(core.view.config.zoneLightEnabled);
core.view.$watch('config.zoneLightEnabled', (v) => zoneLighting.set_enabled(v));
core.view.$watch('config.zoneLightMapId', (v) => zoneLighting.set_map(v));
core.view.$watch('config.zoneLightTime', (v) => zoneLighting.set_time(v));

listfile.preload();
dbd_manifest.preload();

Expand Down
12 changes: 12 additions & 0 deletions src/default_config.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,18 @@
"decorAutoPreview": true,
"exportDecorFormat": "OBJ",
"creatureAutoPreview": true,
"creatureAutoPlayAnim": true,
"lastBrowseMode": "models",
"exportCreatureFormat": "OBJ",
"exportLegacyModelFormat": "RAW",
"modelViewerShowGrid": true,
"modelViewerShowShadow": false,
"modelViewerUse3DCamera": true,
"modelViewerCustomLight": false,
"modelViewerLightAzimuth": 45,
"modelViewerLightElevation": 35,
"modelViewerLightIntensity": 0.8,
"modelViewerLightAmbient": 0.45,
"modelViewerShowBackground": false,
"modelViewerBackgroundColor": "#000000",
"modelViewerWireframe": false,
Expand All @@ -80,6 +89,9 @@
"chrRenderShadow": true,
"chrUse3DCamera": false,
"chrShowBackground": false,
"zoneLightEnabled": false,
"zoneLightMapId": 0,
"zoneLightTime": 720,
"chrBackgroundColor": "#000000",
"chrExportApplyPose": true,
"modelsExportApplyPose": true,
Expand Down
39 changes: 35 additions & 4 deletions src/js/3D/renderers/M2RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

const core = require('../../core');
const log = require('../../log');
const zoneLighting = require('../zone-lighting');

const BLPFile = require('../../casc/blp');
const M2Loader = require('../loaders/M2Loader');
Expand Down Expand Up @@ -1503,14 +1504,44 @@ class M2RendererGL {
if (bone_count)
ubo.ubo.upload_range(ubo.offsets[0], bone_count * 16 * 4);

// lighting - transform light direction to view space
const lx = 3, ly = -0.7, lz = -2;
// lighting - use real zone lighting (LightData) when active, otherwise a
// neutral default light. The shader expects the light *travel* direction
// in view space; zone light_dir points toward the sun, so it is negated.
let amb_r = 0.5, amb_g = 0.5, amb_b = 0.5;
let dif_r = 0.7, dif_g = 0.7, dif_b = 0.7;
let lx = 3, ly = -0.7, lz = -2;

const cfg = core.view.config;
const zl = zoneLighting.get_uniforms();
if (zl) {
amb_r = zl.ambient_color[0]; amb_g = zl.ambient_color[1]; amb_b = zl.ambient_color[2];
dif_r = zl.direct_color[0]; dif_g = zl.direct_color[1]; dif_b = zl.direct_color[2];
lx = -zl.light_dir[0]; ly = -zl.light_dir[1]; lz = -zl.light_dir[2];
} else if (cfg.modelViewerCustomLight) {
// adjustable directional light driven by azimuth/elevation sliders.
const az = (cfg.modelViewerLightAzimuth ?? 45) * Math.PI / 180;
const el = (cfg.modelViewerLightElevation ?? 35) * Math.PI / 180;
const intensity = cfg.modelViewerLightIntensity ?? 0.8;
const ambient = cfg.modelViewerLightAmbient ?? 0.45;

// direction from the model toward the light; the shader travel
// direction is its negation.
const cos_el = Math.cos(el);
lx = -(cos_el * Math.sin(az));
ly = -Math.sin(el);
lz = -(cos_el * Math.cos(az));

amb_r = amb_g = amb_b = ambient;
dif_r = dif_g = dif_b = intensity;
}

// transform light direction to view space
const light_view_x = view_matrix[0] * lx + view_matrix[4] * ly + view_matrix[8] * lz;
const light_view_y = view_matrix[1] * lx + view_matrix[5] * ly + view_matrix[9] * lz;
const light_view_z = view_matrix[2] * lx + view_matrix[6] * ly + view_matrix[10] * lz;

shader.set_uniform_3f('u_ambient_color', 0.5, 0.5, 0.5);
shader.set_uniform_3f('u_diffuse_color', 0.7, 0.7, 0.7);
shader.set_uniform_3f('u_ambient_color', amb_r, amb_g, amb_b);
shader.set_uniform_3f('u_diffuse_color', dif_r, dif_g, dif_b);
shader.set_uniform_3f('u_light_dir', light_view_x, light_view_y, light_view_z);

// wireframe
Expand Down
Loading