perf(render): instance block geometry

Share source buffers across block placements and render nested content with GPU instance transforms.\n\nChunk silhouettes and compact mesh material data so large drawings stay under device buffer limits and remain responsive.
This commit is contained in:
Hakan Seven committed 2026-08-12 02:33:10 +03:00
1 parent 7bfb617745
commit 4fd9b98da4
68 files changed
+3417 -547

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
struct Uniforms {
viewport_size: vec2<f32>,
world_per_pixel: f32,
lwdisplay_enable: f32,
flat_shade: f32,
transparency_enable: f32,
_pad: vec2<f32>,
view_rot: mat4x4<f32>,
eye_high: vec3<f32>,
_pad_eh: f32,
eye_low: vec3<f32>,
_pad_el: f32,
}
@group(0) @binding(0) var<uniform> u: Uniforms;
struct VertexIn {
@location(0) position: vec3<f32>,
@location(1) color: vec4<f32>,
@location(2) source_depth: f32,
@location(3) position_low: vec3<f32>,
@location(4) translation: vec3<f32>,
@location(5) translation_low: vec3<f32>,
@location(6) instance_depth: f32,
}
struct VertexOut {
@builtin(position) clip_pos: vec4<f32>,
@location(0) color: vec4<f32>,
}
const DRAW_ORDER_BIAS: f32 = 0.001;
@vertex
fn vs_main(v: VertexIn) -> VertexOut {
var out: VertexOut;
let rel = (v.position + v.translation - u.eye_high)
+ (v.position_low + v.translation_low - u.eye_low);
out.clip_pos = u.view_rot * vec4<f32>(rel, 1.0);
out.clip_pos.z = out.clip_pos.z
- (v.source_depth + v.instance_depth) * DRAW_ORDER_BIAS * out.clip_pos.w;
out.color = v.color;
return out;
}
@fragment
fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
return in.color;
}
+59
View File
@@ -0,0 +1,59 @@
struct Uniforms {
viewport_size: vec2<f32>,
world_per_pixel: f32,
lwdisplay_enable: f32,
flat_shade: f32,
transparency_enable: f32,
_pad: vec2<f32>,
view_rot: mat4x4<f32>,
eye_high: vec3<f32>,
_pad_eh: f32,
eye_low: vec3<f32>,
_pad_el: f32,
}
@group(0) @binding(0) var<uniform> u: Uniforms;
@group(1) @binding(0) var atlas_tex: texture_2d<f32>;
@group(1) @binding(1) var atlas_samp: sampler;
struct VertIn {
@location(0) pos: vec3<f32>,
@location(1) pos_low: vec3<f32>,
@location(2) uv: vec2<f32>,
@location(3) color: vec4<f32>,
@location(4) source_depth: f32,
@location(5) translation: vec3<f32>,
@location(6) translation_low: vec3<f32>,
@location(7) instance_depth: f32,
}
struct VertOut {
@builtin(position) clip_pos: vec4<f32>,
@location(0) uv: vec2<f32>,
@location(1) color: vec4<f32>,
}
const DRAW_ORDER_BIAS: f32 = 0.001;
@vertex
fn vs_main(v: VertIn) -> VertOut {
var out: VertOut;
let rel = (v.pos + v.translation - u.eye_high)
+ (v.pos_low + v.translation_low - u.eye_low);
out.clip_pos = u.view_rot * vec4<f32>(rel, 1.0);
out.clip_pos.z = out.clip_pos.z
- (v.source_depth + v.instance_depth) * DRAW_ORDER_BIAS * out.clip_pos.w;
out.uv = v.uv;
out.color = v.color;
return out;
}
@fragment
fn fs_main(in: VertOut) -> @location(0) vec4<f32> {
let sd = textureSample(atlas_tex, atlas_samp, in.uv).r;
let aa = max(fwidth(sd), 1e-4);
let alpha = smoothstep(0.5 - aa, 0.5 + aa, sd);
if alpha <= 0.0 {
discard;
}
return vec4<f32>(in.color.rgb, in.color.a * alpha);
}
+250
View File
@@ -0,0 +1,250 @@
struct Uniforms {
viewport_size: vec2<f32>,
world_per_pixel: f32,
lwdisplay_enable: f32,
flat_shade: f32,
transparency_enable: f32,
linetype_scale: f32,
_pad: f32,
view_rot: mat4x4<f32>,
eye_high: vec3<f32>,
_pad_eh: f32,
eye_low: vec3<f32>,
_pad_el: f32,
}
@group(0) @binding(0) var<uniform> u: Uniforms;
struct WireConst {
color: vec4<f32>,
pat0: vec4<f32>,
pat1: vec4<f32>,
half_width: f32,
pattern_length: f32,
draw_depth: f32,
align_end: f32,
align_total: f32,
world_half_width: f32,
_pad1: f32,
_pad2: f32,
}
@group(1) @binding(0) var<uniform> wire_const: WireConst;
struct VertexIn {
@location(0) pos_a: vec3<f32>,
@location(1) pos_b: vec3<f32>,
@location(2) pos_a_low: vec3<f32>,
@location(3) pos_b_low: vec3<f32>,
@location(4) distances: vec2<f32>,
@location(5) taper_ratio: vec2<f32>,
@location(6) translation: vec3<f32>,
@location(7) translation_low: vec3<f32>,
@location(8) depth: vec2<f32>,
}
const DRAW_ORDER_BIAS: f32 = 0.001;
struct VertexOut {
@builtin(position) clip_pos: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) distance: f32,
@location(2) pattern_length: f32,
@location(3) pat0: vec4<f32>,
@location(4) pat1: vec4<f32>,
@location(5) @interpolate(flat) min_elem: f32,
@location(6) @interpolate(flat) align_end: f32,
@location(7) @interpolate(flat) align_total: f32,
@location(8) cap: vec2<f32>,
@location(9) @interpolate(flat) cap_ends: vec3<f32>,
}
fn resolve_hw(taper_ratio: f32, world_hw: f32, px_hw: f32) -> f32 {
if taper_ratio > 0.0 {
return max((taper_ratio * world_hw) / u.world_per_pixel, 0.5);
}
if world_hw > 0.0 {
return max(world_hw / u.world_per_pixel, 0.5);
}
return select(0.5, px_hw, u.lwdisplay_enable > 0.5);
}
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut {
let corner = vertex_index % 6u;
let which_end_arr = array<f32, 6>(0.0, 1.0, 1.0, 0.0, 1.0, 0.0);
let side_arr = array<f32, 6>(-1.0, -1.0, 1.0, -1.0, 1.0, 1.0);
let which_end = which_end_arr[corner];
let side = side_arr[corner];
let rel_a = (in.pos_a + in.translation - u.eye_high)
+ (in.pos_a_low + in.translation_low - u.eye_low);
let rel_b = (in.pos_b + in.translation - u.eye_high)
+ (in.pos_b_low + in.translation_low - u.eye_low);
let clip_a = u.view_rot * vec4<f32>(rel_a, 1.0);
let clip_b = u.view_rot * vec4<f32>(rel_b, 1.0);
let screen_a = clip_a.xy / clip_a.w * u.viewport_size * 0.5;
let screen_b = clip_b.xy / clip_b.w * u.viewport_size * 0.5;
let segment = screen_b - screen_a;
let segment_length = length(segment);
var direction = vec2<f32>(1.0, 0.0);
if segment_length > 1e-4 {
direction = segment / segment_length;
}
let perpendicular = vec2<f32>(-direction.y, direction.x);
let clip_position = mix(clip_a, clip_b, which_end);
let half_width_a = resolve_hw(
in.taper_ratio.x,
wire_const.world_half_width,
wire_const.half_width,
);
let half_width_b = resolve_hw(
in.taper_ratio.y,
wire_const.world_half_width,
wire_const.half_width,
);
let half_width = mix(half_width_a, half_width_b, which_end);
let extension = which_end * 2.0 - 1.0;
let offset_px = perpendicular * half_width * side
+ direction * half_width * extension;
let ndc_offset = offset_px / (u.viewport_size * 0.5);
let final_clip = clip_position
+ vec4<f32>(ndc_offset * clip_position.w, 0.0, 0.0);
let scale = u.linetype_scale;
var min_element = wire_const.pattern_length * scale;
let elements = array<f32, 8>(
wire_const.pat0.x * scale,
wire_const.pat0.y * scale,
wire_const.pat0.z * scale,
wire_const.pat0.w * scale,
wire_const.pat1.x * scale,
wire_const.pat1.y * scale,
wire_const.pat1.z * scale,
wire_const.pat1.w * scale,
);
for (var i = 0u; i < 8u; i++) {
let value = abs(elements[i]);
if value > 0.0 && value < min_element {
min_element = value;
}
}
var out: VertexOut;
out.clip_pos = final_clip;
out.clip_pos.z -= in.depth.x * DRAW_ORDER_BIAS * out.clip_pos.w;
out.color = wire_const.color;
out.distance = mix(in.distances.x, in.distances.y, which_end)
+ extension * half_width * u.world_per_pixel;
out.pattern_length = wire_const.pattern_length * scale;
out.pat0 = wire_const.pat0 * scale;
out.pat1 = wire_const.pat1 * scale;
out.min_elem = min_element;
out.align_end = wire_const.align_end * scale;
out.align_total = wire_const.align_total;
out.cap = vec2<f32>(
which_end * segment_length + extension * half_width,
half_width * side,
);
out.cap_ends = vec3<f32>(segment_length, half_width_a, half_width_b);
return out;
}
fn in_dash(
distance: f32,
pattern_length: f32,
pat0: vec4<f32>,
pat1: vec4<f32>,
align_end: f32,
align_total: f32,
) -> bool {
let elements = array<f32, 8>(
pat0.x, pat0.y, pat0.z, pat0.w,
pat1.x, pat1.y, pat1.z, pat1.w,
);
var count = 0u;
for (var i = 0u; i < 8u; i++) {
if elements[i] != 0.0 {
count = i + 1u;
}
}
var value: f32;
if align_total > 0.0 {
if distance <= align_end || distance >= align_total - align_end {
return true;
}
var first_dash = 0.0;
for (var i = 0u; i < count; i++) {
if elements[i] > 0.0 {
first_dash = elements[i];
break;
}
}
value = ((distance - align_end + first_dash) % pattern_length
+ pattern_length) % pattern_length;
} else {
value = ((distance % pattern_length) + pattern_length) % pattern_length;
}
var position = 0.0;
let dot_half = u.world_per_pixel * 0.75;
for (var i = 0u; i < count; i++) {
let element = elements[i];
if element == 0.0 {
let delta = abs(value - position);
if min(delta, pattern_length - delta) <= dot_half {
return true;
}
} else if element > 0.0 {
if value >= position && value < position + element {
return true;
}
position += element;
} else {
position -= element;
}
}
return false;
}
fn clipped_cap(cap: vec2<f32>, ends: vec3<f32>) -> bool {
if cap.x < 0.0 {
return length(cap) > ends.y;
}
if cap.x > ends.x {
return length(vec2<f32>(cap.x - ends.x, cap.y)) > ends.z;
}
return false;
}
fn visible_fragment(in: VertexOut) -> bool {
if clipped_cap(in.cap, in.cap_ends) {
return false;
}
if in.pattern_length > 0.0 && in.min_elem >= u.world_per_pixel {
return in_dash(
in.distance,
in.pattern_length,
in.pat0,
in.pat1,
in.align_end,
in.align_total,
);
}
return true;
}
@fragment
fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
if !visible_fragment(in) {
discard;
}
let alpha = select(1.0, in.color.a, u.transparency_enable > 0.5);
return vec4<f32>(in.color.rgb, alpha);
}
@fragment
fn fs_black(in: VertexOut) -> @location(0) vec4<f32> {
if !visible_fragment(in) {
discard;
}
let alpha = select(1.0, in.color.a, u.transparency_enable > 0.5);
return vec4<f32>(0.0, 0.0, 0.0, alpha);
}
+11 -6
View File
@@ -97,6 +97,10 @@ struct LineFamily {
struct VIn {
@location(0) local_xy: vec2<f32>,
@location(1) instance_index: u32,
@location(2) translation: vec2<f32>,
@location(3) translation_low: vec2<f32>,
@location(4) draw_depth: f32,
@location(5) visible: u32,
}
struct VOut {
@@ -114,7 +118,7 @@ struct VOut {
// frustum-culls the primitive and no fragment runs. (WGSL
// forbids literal NaN so this out-of-NDC trick replaces the
// usual NaN-degenerate-triangle.)
if visibility[v.instance_index] == 0u {
if visibility[v.instance_index] == 0u || v.visible == 0u {
o.clip = vec4<f32>(2.0, 2.0, 2.0, 1.0);
o.xz = vec2<f32>(0.0, 0.0);
o.instance_index = v.instance_index;
@@ -126,14 +130,15 @@ struct VOut {
// against eye_high (Sterbenz); local + anchor low + (−eye_low) carry the
// residual. `local` is small (boundary-relative), so adding it in the low
// term keeps full precision at UTM-scale anchors.
let hi = vec3<f32>(inst.world_origin.x - u.eye_high.x,
inst.world_origin.y - u.eye_high.y,
let hi = vec3<f32>(inst.world_origin.x + v.translation.x - u.eye_high.x,
inst.world_origin.y + v.translation.y - u.eye_high.y,
-u.eye_high.z);
let lo = vec3<f32>(local.x + inst.world_origin_low.x - u.eye_low.x,
local.y + inst.world_origin_low.y - u.eye_low.y,
let lo = vec3<f32>(local.x + inst.world_origin_low.x + v.translation_low.x - u.eye_low.x,
local.y + inst.world_origin_low.y + v.translation_low.y - u.eye_low.y,
-u.eye_low.z);
o.clip = u.view_rot * vec4<f32>(hi + lo, 1.0);
o.clip.z = o.clip.z - inst.draw_depth * DRAW_ORDER_BIAS * o.clip.w;
o.clip.z = o.clip.z
- (inst.draw_depth + v.draw_depth) * DRAW_ORDER_BIAS * o.clip.w;
o.xz = local;
o.instance_index = v.instance_index;
return o;
+11 -5
View File
@@ -92,7 +92,12 @@ fn load_family(fi: u32) -> LineFamily {
// ── Vertex shader (identical to wipeout.wgsl) ──────────────────────────────
struct VIn { @location(0) pos: vec3<f32> }
struct VIn {
@location(0) pos: vec3<f32>,
@location(1) translation: vec2<f32>,
@location(2) translation_low: vec2<f32>,
@location(3) draw_depth: f32,
}
struct VOut {
@builtin(position) clip: vec4<f32>,
@location(0) xz: vec2<f32>,
@@ -102,13 +107,14 @@ struct VOut {
var o: VOut;
// Double-single relative-to-eye: anchor high cancels eye_high (Sterbenz);
// boundary-local v.pos + anchor low + (−eye_low) carry the residual.
let hi = vec3<f32>(h.origin.x - u.eye_high.x,
h.origin.y - u.eye_high.y,
let hi = vec3<f32>(h.origin.x + v.translation.x - u.eye_high.x,
h.origin.y + v.translation.y - u.eye_high.y,
-u.eye_high.z);
let lo = vec3<f32>(v.pos.x + h.origin_low.x - u.eye_low.x,
v.pos.y + h.origin_low.y - u.eye_low.y,
let lo = vec3<f32>(v.pos.x + h.origin_low.x + v.translation_low.x - u.eye_low.x,
v.pos.y + h.origin_low.y + v.translation_low.y - u.eye_low.y,
v.pos.z - u.eye_low.z);
o.clip = u.view_rot * vec4<f32>(hi + lo, 1.0);
o.clip.z = o.clip.z - v.draw_depth * 0.001 * o.clip.w;
o.xz = vec2<f32>(v.pos.x, v.pos.y);
return o;
}
+7 -2
View File
@@ -41,6 +41,9 @@ struct VertIn {
@location(0) pos: vec3<f32>,
@location(1) uv: vec2<f32>,
@location(2) pos_low: vec3<f32>,
@location(3) translation: vec3<f32>,
@location(4) translation_low: vec3<f32>,
@location(5) draw_depth: f32,
};
struct VertOut {
@@ -51,9 +54,11 @@ struct VertOut {
@vertex
fn vs_main(in: VertIn) -> VertOut {
var out: VertOut;
let rel = (in.pos - u.eye_high) + (in.pos_low - u.eye_low);
let rel = (in.pos + in.translation - u.eye_high)
+ (in.pos_low + in.translation_low - u.eye_low);
out.clip_pos = u.view_rot * vec4<f32>(rel, 1.0);
out.clip_pos.z = out.clip_pos.z - img_params.draw_depth * DRAW_ORDER_BIAS * out.clip_pos.w;
out.clip_pos.z = out.clip_pos.z
- (img_params.draw_depth + in.draw_depth) * DRAW_ORDER_BIAS * out.clip_pos.w;
out.uv = in.uv;
return out;
}
+41 -53
View File
@@ -77,6 +77,17 @@ struct MaterialMaps {
@group(1) @binding(13) var normal_sampler: sampler;
@group(1) @binding(14) var<uniform> maps: MaterialMaps;
struct MeshSurface {
face_color: vec4<f32>,
material: vec4<f32>,
specular: vec4<f32>,
ambient: vec4<f32>,
advanced: vec4<f32>,
flags: vec4<u32>,
};
@group(1) @binding(16) var<uniform> surface: MeshSurface;
struct MeshInstance {
model_row_0: vec4<f32>,
model_row_1: vec4<f32>,
@@ -93,19 +104,8 @@ var<storage, read> mesh_instances: array<MeshInstance>;
struct VertexIn {
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
@location(2) color: vec4<f32>,
@location(3) position_low: vec3<f32>,
// gloss, reflectivity, self illumination, luminance
@location(4) material: vec4<f32>,
// specular RGB, refraction index
@location(5) specular: vec4<f32>,
@location(6) uv_diffuse: vec2<f32>,
// ambient RGB, translucence
@location(7) ambient: vec4<f32>,
// normal strength, bump scale, reflectance scale, transmittance scale
@location(8) advanced: vec4<f32>,
// illumination model, channel flags, material mode, luminance mode
@location(9) flags: vec4<u32>,
@location(10) uv_specular_reflection: vec4<f32>,
@location(11) uv_opacity_bump: vec4<f32>,
@location(12) uv_refraction_normal: vec4<f32>,
@@ -113,21 +113,15 @@ struct VertexIn {
struct VertexOut {
@builtin(position) clip_pos: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) normal: vec3<f32>,
@location(2) world_pos: vec3<f32>,
@location(3) material: vec4<f32>,
@location(4) specular: vec4<f32>,
@location(5) uv_diffuse: vec2<f32>,
@location(6) ambient: vec4<f32>,
@location(7) advanced: vec4<f32>,
@location(8) @interpolate(flat) flags: vec4<u32>,
@location(9) uv_specular: vec2<f32>,
@location(10) uv_reflection: vec2<f32>,
@location(11) uv_opacity: vec2<f32>,
@location(12) uv_bump: vec2<f32>,
@location(13) uv_refraction: vec2<f32>,
@location(14) uv_normal: vec2<f32>,
@location(0) normal: vec3<f32>,
@location(1) world_pos: vec3<f32>,
@location(2) uv_diffuse: vec2<f32>,
@location(3) uv_specular: vec2<f32>,
@location(4) uv_reflection: vec2<f32>,
@location(5) uv_opacity: vec2<f32>,
@location(6) uv_bump: vec2<f32>,
@location(7) uv_refraction: vec2<f32>,
@location(8) uv_normal: vec2<f32>,
};
struct EdgeVertexIn {
@@ -168,19 +162,13 @@ fn vs_main(
let instance = mesh_instances[instance_index];
let rel = relative_position(v.position, v.position_low, instance);
out.clip_pos = u.view_rot * vec4<f32>(rel, 1.0);
out.color = v.color;
out.normal = normalize(vec3<f32>(
dot(instance.normal_row_0.xyz, v.normal),
dot(instance.normal_row_1.xyz, v.normal),
dot(instance.normal_row_2.xyz, v.normal),
));
out.world_pos = rel;
out.material = v.material;
out.specular = v.specular;
out.uv_diffuse = v.uv_diffuse;
out.ambient = v.ambient;
out.advanced = v.advanced;
out.flags = v.flags;
out.uv_specular = v.uv_specular_reflection.xy;
out.uv_reflection = v.uv_specular_reflection.zw;
out.uv_opacity = v.uv_opacity_bump.xy;
@@ -305,7 +293,7 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
);
let strength = maps.blends1.z
* map_weight(in.uv_normal, maps.tiling1.z)
* max(in.advanced.x, 0.0);
* max(surface.advanced.x, 0.0);
n = normalize(mix(n, mapped, clamp(strength, 0.0, 1.0)));
} else if (maps.present1.x != 0u) {
let height = dot(
@@ -321,7 +309,7 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
);
}
var albedo = in.color.rgb;
var albedo = surface.face_color.rgb;
if (maps.present0.x != 0u) {
let texel = textureSample(diffuse_map, diffuse_sampler, in.uv_diffuse).rgb;
let blend = clamp(
@@ -348,7 +336,7 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
}
}
var specular_color = select(in.specular.rgb, albedo, in.flags.x == 1u);
var specular_color = select(surface.specular.rgb, albedo, surface.flags.x == 1u);
if (maps.present0.y != 0u) {
let texel = textureSample(specular_map, specular_sampler, in.uv_specular).rgb;
let blend = clamp(
@@ -359,14 +347,14 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
specular_color = mix(specular_color, texel, blend);
}
let view = normalize(-in.world_pos);
let gloss_exp = mix(2.0, 128.0, clamp(in.material.x, 0.0, 1.0));
let gloss_exp = mix(2.0, 128.0, clamp(surface.material.x, 0.0, 1.0));
let fresnel0 = pow(
(max(in.specular.w, 1.0) - 1.0) / (max(in.specular.w, 1.0) + 1.0),
(max(surface.specular.w, 1.0) - 1.0) / (max(surface.specular.w, 1.0) + 1.0),
2.0,
);
let fresnel = fresnel0
+ (1.0 - fresnel0) * pow(1.0 - abs(dot(n, view)), 5.0);
let specular_strength = clamp(1.0 + in.material.y + fresnel, 0.0, 1.5);
let specular_strength = clamp(1.0 + surface.material.y + fresnel, 0.0, 1.5);
let viewport_highlight = select(
1.0,
max(u.visual_style.z, 0.0),
@@ -441,10 +429,10 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
* pow(half_response, gloss_exp)
* specular_strength
* viewport_highlight
* max(in.advanced.z, 0.0)
* max(surface.advanced.z, 0.0)
* strength;
}
let ambient_light = clamp(in.ambient.rgb, vec3<f32>(0.0), vec3<f32>(1.0))
let ambient_light = clamp(surface.ambient.rgb, vec3<f32>(0.0), vec3<f32>(1.0))
* clamp(u.lighting.yzw, vec3<f32>(0.0), vec3<f32>(1.0));
var lit = ambient_light
+ albedo * clamp(direct_light, vec3<f32>(0.0), vec3<f32>(2.0))
@@ -473,7 +461,7 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
* specular_color
* specular_strength
* viewport_highlight
* max(in.advanced.z, 0.0)
* max(surface.advanced.z, 0.0)
* max(u.environment_params.w, 0.0);
}
if (maps.present0.z != 0u) {
@@ -488,8 +476,8 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
clamp(
maps.blends0.z
* map_weight(in.uv_reflection, maps.tiling0.z)
* in.material.y
* max(in.advanced.z, 0.0),
* surface.material.y
* max(surface.advanced.z, 0.0),
0.0,
1.0,
),
@@ -507,16 +495,16 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
clamp(
maps.blends1.y
* map_weight(in.uv_refraction, maps.tiling1.y)
* in.ambient.a
* max(in.advanced.w, 0.0),
* surface.ambient.a
* max(surface.advanced.w, 0.0),
0.0,
1.0,
),
);
}
var rgb = mix(lit, albedo, clamp(in.material.z, 0.0, 1.0));
if (in.flags.w == 1u) {
rgb = lit + albedo * max(in.material.w, 0.0);
var rgb = mix(lit, albedo, clamp(surface.material.z, 0.0, 1.0));
if (surface.flags.w == 1u) {
rgb = lit + albedo * max(surface.material.w, 0.0);
}
let contrast = 1.0 + clamp(u.view_tone.y, -1.0, 1.0);
rgb = (rgb - vec3<f32>(0.5)) * contrast + vec3<f32>(0.5);
@@ -533,7 +521,7 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
let fog_density = mix(u.fog_params.z, u.fog_params.w, fog_position);
rgb = mix(rgb, u.fog_color.rgb, clamp(fog_density, 0.0, 1.0));
}
var material_alpha = in.color.a;
var material_alpha = surface.face_color.a;
if (maps.present0.w != 0u) {
let opacity = textureSample(opacity_map, opacity_sampler, in.uv_opacity).r;
let blend = clamp(
@@ -543,7 +531,7 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
);
material_alpha *= mix(1.0, opacity, blend);
}
material_alpha *= 1.0 - clamp(in.ambient.a * max(in.advanced.w, 0.0), 0.0, 0.95);
material_alpha *= 1.0 - clamp(surface.ambient.a * max(surface.advanced.w, 0.0), 0.0, 0.95);
material_alpha *= clamp(u.visual_style.y, 0.0, 1.0);
let alpha = select(1.0, material_alpha, u.transparency_enable > 0.5);
return vec4<f32>(rgb, alpha);
@@ -553,7 +541,7 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
// wireframe and hidden-line edge passes so lines read at their true colour.
@fragment
fn fs_edge(in: EdgeVertexOut) -> @location(0) vec4<f32> {
return vec4<f32>(in.color.rgb, 1.0);
return vec4<f32>(surface.face_color.rgb, 1.0);
}
// Edge fragment for filled render modes: force black so edges frame the shaded
@@ -566,7 +554,7 @@ fn fs_edge_black(_in: EdgeVertexOut) -> @location(0) vec4<f32> {
@fragment
fn fs_highlight_selected(in: VertexOut) -> @location(0) vec4<f32> {
return vec4<f32>(
mix(in.color.rgb, vec3<f32>(0.15, 0.55, 1.0), 0.60),
mix(surface.face_color.rgb, vec3<f32>(0.15, 0.55, 1.0), 0.60),
0.90,
);
}
@@ -574,7 +562,7 @@ fn fs_highlight_selected(in: VertexOut) -> @location(0) vec4<f32> {
@fragment
fn fs_highlight_hover(in: VertexOut) -> @location(0) vec4<f32> {
return vec4<f32>(
mix(in.color.rgb, vec3<f32>(0.95, 0.55, 0.10), 0.35),
mix(surface.face_color.rgb, vec3<f32>(0.95, 0.55, 0.10), 0.35),
0.82,
);
}
+48
View File
@@ -0,0 +1,48 @@
struct Uniforms {
viewport_size: vec2<f32>,
world_per_pixel: f32,
lwdisplay_enable: f32,
flat_shade: f32,
transparency_enable: f32,
_pad: vec2<f32>,
view_rot: mat4x4<f32>,
eye_high: vec3<f32>,
_pad_eh: f32,
eye_low: vec3<f32>,
_pad_el: f32,
}
@group(0) @binding(0) var<uniform> u: Uniforms;
struct VertexIn {
@location(0) position: vec3<f32>,
@location(2) color: vec4<f32>,
@location(3) position_low: vec3<f32>,
@location(4) translation: vec3<f32>,
@location(5) translation_low: vec3<f32>,
}
struct VertexOut {
@builtin(position) clip_pos: vec4<f32>,
@location(0) color: vec4<f32>,
}
@vertex
fn vs_main(v: VertexIn) -> VertexOut {
var out: VertexOut;
let rel = (v.position + v.translation - u.eye_high)
+ (v.position_low + v.translation_low - u.eye_low);
out.clip_pos = u.view_rot * vec4<f32>(rel, 1.0);
out.color = v.color;
return out;
}
@fragment
fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
return in.color;
}
@fragment
fn fs_black(in: VertexOut) -> @location(0) vec4<f32> {
return vec4<f32>(0.0, 0.0, 0.0, in.color.a);
}
+11 -6
View File
@@ -89,7 +89,12 @@ struct FamilyBatch {
// ── Vertex shader ──────────────────────────────────────────────────────────
struct VIn { @location(0) pos: vec3<f32> }
struct VIn {
@location(0) pos: vec3<f32>,
@location(1) translation: vec2<f32>,
@location(2) translation_low: vec2<f32>,
@location(3) draw_depth: f32,
}
struct VOut {
@builtin(position) clip: vec4<f32>,
@location(0) xz: vec2<f32>,
@@ -107,17 +112,17 @@ struct VOut {
// pattern math doesn't catastrophically cancel.
// Double-single relative-to-eye: anchor high cancels eye_high (Sterbenz);
// boundary-local v.pos + anchor low + (−eye_low) carry the residual.
let hi = vec3<f32>(h.origin.x - u.eye_high.x,
h.origin.y - u.eye_high.y,
let hi = vec3<f32>(h.origin.x + v.translation.x - u.eye_high.x,
h.origin.y + v.translation.y - u.eye_high.y,
-u.eye_high.z);
let lo = vec3<f32>(v.pos.x + h.origin_low.x - u.eye_low.x,
v.pos.y + h.origin_low.y - u.eye_low.y,
let lo = vec3<f32>(v.pos.x + h.origin_low.x + v.translation_low.x - u.eye_low.x,
v.pos.y + h.origin_low.y + v.translation_low.y - u.eye_low.y,
v.pos.z - u.eye_low.z);
o.clip = u.view_rot * vec4<f32>(hi + lo, 1.0);
// Draw-order bias (see wire.wgsl): the mask erases what draws BELOW its
// entity and loses to what draws above — including its own frame wire,
// which rides a +half-rank override.
o.clip.z = o.clip.z - h.draw_depth * 0.001 * o.clip.w;
o.clip.z = o.clip.z - (h.draw_depth + v.draw_depth) * 0.001 * o.clip.w;
o.xz = vec2<f32>(v.pos.x, v.pos.y);
return o;
}