mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
This is dumb as heck
This commit is contained in:
parent
0f5d289ecb
commit
d12f4e3aca
191
assets/voxygen/shaders/lod-structure-frag.glsl
Normal file
191
assets/voxygen/shaders/lod-structure-frag.glsl
Normal file
@ -0,0 +1,191 @@
|
||||
#version 330 core
|
||||
|
||||
#include <constants.glsl>
|
||||
|
||||
#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION
|
||||
|
||||
#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
|
||||
|
||||
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
|
||||
|
||||
#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET
|
||||
|
||||
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
|
||||
|
||||
#define HAS_SHADOW_MAPS
|
||||
|
||||
#include <globals.glsl>
|
||||
|
||||
in vec3 f_pos;
|
||||
flat in vec3 f_norm;
|
||||
//flat in float f_select;
|
||||
// flat in vec3 f_pos_norm;
|
||||
in vec2 f_uv_pos;
|
||||
in vec2 f_inst_light;
|
||||
// flat in uint f_atlas_pos;
|
||||
// in vec3 f_col;
|
||||
// in float f_ao;
|
||||
// in float f_light;
|
||||
// in vec4 light_pos[2];
|
||||
|
||||
uniform sampler2D t_col_light;
|
||||
|
||||
//struct ShadowLocals {
|
||||
// mat4 shadowMatrices;
|
||||
// mat4 texture_mat;
|
||||
//};
|
||||
//
|
||||
//layout (std140)
|
||||
//uniform u_light_shadows {
|
||||
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
|
||||
//};
|
||||
|
||||
out vec4 tgt_color;
|
||||
|
||||
#include <sky.glsl>
|
||||
#include <light.glsl>
|
||||
#include <lod.glsl>
|
||||
|
||||
const float FADE_DIST = 32.0;
|
||||
|
||||
void main() {
|
||||
/* if (f_uv_pos.x < 757) {
|
||||
discard;
|
||||
} */
|
||||
// vec2 f_uv_pos = vec2(768,1) + 0.5;
|
||||
// vec2 f_uv_pos = vec2(760, 380);// + 0.5;
|
||||
// vec2 f_uv_pos = vec2((uvec2(f_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu)) + 0.5;
|
||||
/* if (f_uv_pos.x < 757) {
|
||||
discard;
|
||||
} */
|
||||
// vec3 du = dFdx(f_pos);
|
||||
// vec3 dv = dFdy(f_pos);
|
||||
// vec3 f_norm = normalize(cross(du, dv));
|
||||
|
||||
float f_ao, f_glow;
|
||||
vec3 f_col = greedy_extract_col_light_glow(t_col_light, f_uv_pos, f_ao, f_glow);
|
||||
|
||||
// vec3 my_chunk_pos = f_pos_norm;
|
||||
// tgt_color = vec4(hash(floor(vec4(my_chunk_pos.x, 0, 0, 0))), hash(floor(vec4(0, my_chunk_pos.y, 0, 1))), hash(floor(vec4(0, 0, my_chunk_pos.z, 2))), 1.0);
|
||||
// tgt_color = vec4(f_uv_pos / texSize, 0.0, 1.0);
|
||||
// tgt_color = vec4(f_col.rgb, 1.0);
|
||||
// return;
|
||||
// vec4 light_pos[2];
|
||||
//#if (SHADOW_MODE == SHADOW_MODE_MAP)
|
||||
// // for (uint i = 0u; i < light_shadow_count.z; ++i) {
|
||||
// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/;
|
||||
// // }
|
||||
// vec4 sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/;
|
||||
//#elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE)
|
||||
// vec4 sun_pos = vec4(0.0);
|
||||
//#endif
|
||||
vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
|
||||
// vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0);
|
||||
// vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/);
|
||||
vec3 view_dir = -cam_to_frag;
|
||||
|
||||
/* vec3 sun_dir = get_sun_dir(time_of_day.x);
|
||||
vec3 moon_dir = get_moon_dir(time_of_day.x); */
|
||||
// float sun_light = get_sun_brightness(sun_dir);
|
||||
// float moon_light = get_moon_brightness(moon_dir);
|
||||
|
||||
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
|
||||
float f_alt = alt_at(f_pos.xy);
|
||||
// float f_alt = f_pos.z;
|
||||
#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
|
||||
float f_alt = f_pos.z;
|
||||
#endif
|
||||
|
||||
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
|
||||
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy));
|
||||
float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
|
||||
// float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
|
||||
#elif (SHADOW_MODE == SHADOW_MODE_NONE)
|
||||
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
|
||||
#endif
|
||||
float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, moon_dir);
|
||||
// float sun_shade_frac = horizon_at(f_pos, sun_dir);
|
||||
// float moon_shade_frac = horizon_at(f_pos, moon_dir);
|
||||
// Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence).
|
||||
// Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing).
|
||||
// float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5);
|
||||
// NOTE: current assumption is that moon and sun shouldn't be out at the sae time.
|
||||
// This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow
|
||||
// for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally).
|
||||
// float shade_frac = sun_shade_frac + moon_shade_frac;
|
||||
|
||||
// DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos);
|
||||
float point_shadow = shadow_at(f_pos, f_norm);
|
||||
DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos);
|
||||
DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/);
|
||||
|
||||
vec3 surf_color = /*srgb_to_linear*//*linear_to_srgb*/(f_col);
|
||||
float alpha = 1.0;
|
||||
const float n2 = 1.5;
|
||||
const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2);
|
||||
const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2);
|
||||
const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2);
|
||||
const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2);
|
||||
float R_s = (f_pos.z < f_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x);
|
||||
|
||||
vec3 k_a = vec3(1.0);
|
||||
vec3 k_d = vec3(1.0);
|
||||
vec3 k_s = vec3(R_s);
|
||||
|
||||
vec3 emitted_light, reflected_light;
|
||||
|
||||
// Make voxel shadows block the sun and moon
|
||||
sun_info.block = f_inst_light.x;
|
||||
moon_info.block = f_inst_light.x;
|
||||
|
||||
// To account for prior saturation.
|
||||
// float vert_light = pow(f_light, 1.5);
|
||||
// vec3 light_frac = light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha);
|
||||
/* light_frac += light_reflection_factor(f_norm, view_dir, vec3(1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0);
|
||||
light_frac += light_reflection_factor(f_norm, view_dir, vec3(-1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0);
|
||||
light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, -1.0, 0.0), vec3(1.0), vec3(1.0), 2.0);
|
||||
light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, 1.0, 0.0), vec3(1.0), vec3(1.0), 2.0); */
|
||||
|
||||
// vec3 light, diffuse_light, ambient_light;
|
||||
// vec3 emitted_light, reflected_light;
|
||||
// float point_shadow = shadow_at(f_pos,f_norm);
|
||||
// vec3 point_light = light_at(f_pos, f_norm);
|
||||
// vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0));
|
||||
// vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
|
||||
float max_light = 0.0;
|
||||
max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*time_of_day.x, *//*cam_to_frag*/view_dir, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, emitted_light, reflected_light);
|
||||
// reflected_light *= /*vert_light * */point_shadow * shade_frac;
|
||||
// emitted_light *= /*vert_light * */point_shadow * max(shade_frac, MIN_SHADOW);
|
||||
// max_light *= /*vert_light * */point_shadow * shade_frac;
|
||||
// emitted_light *= point_shadow;
|
||||
// reflected_light *= point_shadow;
|
||||
// max_light *= point_shadow;
|
||||
// get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0);
|
||||
// float point_shadow = shadow_at(f_pos, f_norm);
|
||||
// diffuse_light *= f_light * point_shadow;
|
||||
// ambient_light *= f_light * point_shadow;
|
||||
// light += point_light;
|
||||
// diffuse_light += point_light;
|
||||
// reflected_light += point_light;
|
||||
|
||||
max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light);
|
||||
/* vec3 point_light = light_at(f_pos, f_norm);
|
||||
emitted_light += point_light;
|
||||
reflected_light += point_light; */
|
||||
|
||||
// float ao = /*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15;
|
||||
vec3 glow = pow(f_inst_light.y, 3) * 4 * glow_light(f_pos);
|
||||
emitted_light += glow;
|
||||
|
||||
float ao = f_ao;
|
||||
emitted_light *= ao;
|
||||
reflected_light *= ao;
|
||||
|
||||
surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light);
|
||||
// vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
|
||||
|
||||
//surf_color += f_select * (surf_color + 0.1) * vec3(0.15, 0.15, 0.15);
|
||||
|
||||
// tgt_color = vec4(color, 1.0);
|
||||
tgt_color = vec4(surf_color, 1.0);
|
||||
}
|
230
assets/voxygen/shaders/lod-structure-vert.glsl
Normal file
230
assets/voxygen/shaders/lod-structure-vert.glsl
Normal file
@ -0,0 +1,230 @@
|
||||
#version 330 core
|
||||
|
||||
#include <constants.glsl>
|
||||
|
||||
#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION
|
||||
|
||||
#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
|
||||
|
||||
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
|
||||
|
||||
#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET
|
||||
|
||||
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
|
||||
|
||||
#include <globals.glsl>
|
||||
#include <srgb.glsl>
|
||||
#include <sky.glsl>
|
||||
|
||||
in vec3 v_pos;
|
||||
in uint v_atlas_pos;
|
||||
// in uint v_col;
|
||||
in uint v_norm_ao;
|
||||
in vec3 inst_pos;
|
||||
|
||||
struct LodStructureLocals {
|
||||
uint nope;
|
||||
};
|
||||
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
uint nope;
|
||||
// LodStructureLocals lod_structures[8];
|
||||
};
|
||||
|
||||
// struct Instance {
|
||||
// mat4 inst_mat;
|
||||
// vec3 inst_col;
|
||||
// float inst_wind_sway;
|
||||
// };
|
||||
//
|
||||
// layout (std140)
|
||||
// uniform u_ibuf {
|
||||
// Instance sprite_instances[/*MAX_LAYER_FACES*/512];
|
||||
// };
|
||||
|
||||
//struct ShadowLocals {
|
||||
// mat4 shadowMatrices;
|
||||
// mat4 texture_mat;
|
||||
//};
|
||||
//
|
||||
//layout (std140)
|
||||
//uniform u_light_shadows {
|
||||
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
|
||||
//};
|
||||
|
||||
out vec3 f_pos;
|
||||
flat out vec3 f_norm;
|
||||
//flat out float f_select;
|
||||
// flat out vec3 f_pos_norm;
|
||||
// out vec3 f_col;
|
||||
// out float f_ao;
|
||||
out vec2 f_uv_pos;
|
||||
out vec2 f_inst_light;
|
||||
// flat out uint f_atlas_pos;
|
||||
// out vec3 light_pos[2];
|
||||
// out float f_light;
|
||||
|
||||
const float SCALE = 1.0 / 11.0;
|
||||
const float SCALE_FACTOR = pow(SCALE, 1.3) * 0.2;
|
||||
|
||||
const int EXTRA_NEG_Z = 32768;
|
||||
|
||||
void main() {
|
||||
// vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z));
|
||||
// uint inst_ori = (inst_pos_ori >> 29) & 0x7u;
|
||||
// SpriteLocals locals = sprites[inst_ori];
|
||||
// SpriteLocals locals = sprites;
|
||||
// mat4 inst_mat = locals.mat;
|
||||
// float inst_wind_sway = locals.wind_sway.w;
|
||||
|
||||
// mat4 inst_mat = mat4(vec4(1, 0, 0, 0), vec4(0, 1, 0, 0), vec4(0, 0, 1, 0), vec4(5.5, 5.5, 0, 1));
|
||||
// float inst_wind_sway = 0.0;
|
||||
//mat4 inst_mat;
|
||||
//inst_mat[0] = inst_mat0;
|
||||
//inst_mat[1] = inst_mat1;
|
||||
//inst_mat[2] = inst_mat2;
|
||||
//inst_mat[3] = inst_mat3;
|
||||
/* Instance instances = sprite_instances[gl_InstanceID & 1023];
|
||||
mat4 inst_mat = instances.inst_mat;
|
||||
vec3 inst_col = instances.inst_col;
|
||||
float inst_wind_sway = instances.inst_wind_sway; */
|
||||
//vec3 inst_offs = model_offs - focus_off.xyz;
|
||||
// mat3 inst_mat;
|
||||
// inst_mat[0] = inst_mat0.xyz;
|
||||
// inst_mat[1] = inst_mat1.xyz;
|
||||
// inst_mat[2] = inst_mat2.xyz;
|
||||
// /* Instance instances = sprite_instances[gl_InstanceID & 1023];
|
||||
// mat4 inst_mat = instances.inst_mat;
|
||||
// vec3 inst_col = instances.inst_col;
|
||||
// float inst_wind_sway = instances.inst_wind_sway; */
|
||||
// float inst_wind_sway = wind_sway.w;
|
||||
// vec3 inst_offs = model_offs - focus_off.xyz;
|
||||
|
||||
f_inst_light = vec2(1);//inst_light.xy;
|
||||
|
||||
// vec3 sprite_pos = floor(inst_mat3.xyz * SCALE) + inst_offs;
|
||||
|
||||
// f_pos_norm = v_pos;
|
||||
|
||||
// vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz;
|
||||
// vec3 sprite_pos = floor((inst_mat * vec4(0, 0, 0, 1)).xyz * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs;
|
||||
// vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(0, 0, 0, 1)).xyz - /* wind_sway.xyz * */offs.xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) - inst_offs;
|
||||
// vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs;
|
||||
|
||||
// vec3 v_pos = vec3(gl_VertexID * 32, gl_VertexID % 32, 1.0);
|
||||
// f_pos = v_pos + (model_offs - focus_off.xyz);
|
||||
|
||||
// vec3 v_pos = /*inst_mat*//*locals.*/wind_sway.xyz * v_pos;
|
||||
//vec3 v_pos_ = /*inst_mat*//*locals.*//*sprites[0].*/wind_sway.xyz * v_pos;
|
||||
// vec3 v_pos = (/*inst_mat*/locals.mat * vec4(v_pos, 1)).xyz + vec3(0.5, 0.5, 0.0);
|
||||
// f_pos = v_pos * SCALE + (inst_chunk_pos + model_offs - focus_off.xyz);
|
||||
|
||||
// vec3 v_pos_ = (inst_mat * vec4(v_pos/* * SCALE*/, 1)).xyz;
|
||||
// vec3 v_pos = (inst_mat * vec4(v_pos, 1)).xyz;
|
||||
// f_pos = v_pos + (model_offs - focus_off.xyz);
|
||||
|
||||
f_pos = inst_pos - focus_off.xyz + (v_pos - vec3(5.5, 5.5, 0)) * 3.0;
|
||||
|
||||
float dist = distance(focus_pos.xy, f_pos.xy);
|
||||
float pull_down = 0.2 / pow(dist / (view_distance.x * 0.9), 20.0);
|
||||
f_pos.z -= pull_down;
|
||||
|
||||
// Terrain 'pop-in' effect
|
||||
//f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0));
|
||||
// f_pos = (inst_mat * v_pos_) * SCALE + sprite_pos;
|
||||
|
||||
// f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz + (model_offs - focus_off.xyz);
|
||||
// f_pos = v_pos_ + (inst_chunk_pos + model_offs - focus_off.xyz + vec3(0.5, 0.5, 0.0));
|
||||
// f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0));
|
||||
|
||||
// Wind waving
|
||||
/* const float x_scale = sin(tick.x * 1.5 + f_pos.x * 0.1);
|
||||
const float y_scale = sin(tick.x * 1.5 + f_pos.y * 0.1);
|
||||
const float z_scale = pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR;
|
||||
const float xy_bias = sin(tick.x * 0.25);
|
||||
const float z_bias = xy_bias * t_scale;
|
||||
mat3 shear = mat4(
|
||||
vec3(x_scale , 0.0, 0.0, 0.0),
|
||||
vec3(0.0, y_scale, 0.0, 0.0),
|
||||
vec3(0.0, 0.0, z_bias, 0.0),
|
||||
vec3(0.0, 0.0, (1.0 / z_bias), 0.0)
|
||||
); */
|
||||
// const float x_scale = sin(tick.x * 1.5 + f_pos.x * 0.1);
|
||||
// const float y_scale = sin(tick.x * 1.5 + f_pos.y * 0.1);
|
||||
// const float z_scale = pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR;
|
||||
// const float xy_bias = sin(tick.x * 0.25);
|
||||
// const float z_bias = xy_bias * t_scale;
|
||||
// vec3 rotate = inst_wind_sway * vec3(
|
||||
// sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35),
|
||||
// sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25),
|
||||
// 0.0
|
||||
// ) * pow(abs(v_pos_.z/* + sprites[0].offs.z*/)/* * SCALE*/, 1.3) * /*0.2;*/SCALE_FACTOR;
|
||||
//
|
||||
// mat3 shear = mat4(
|
||||
// vec3(x_scale * , 0.0, 0.0, 0.0),
|
||||
// vec3(0.0, y_scale, 0.0, 0.0),
|
||||
// vec3(0.0, 0.0, z_bias, 0.0),
|
||||
// vec3(0.0, 0.0, (1.0 / z_bias), 0.0)
|
||||
// );
|
||||
/*if (wind_sway.w >= 0.4) */{
|
||||
//f_pos += /*inst_wind_sway*/wind_sway.w * vec3(
|
||||
// sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35),
|
||||
// sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25),
|
||||
// 0.0
|
||||
// ) * 4 * v_pos_.z * /*0.2;*/SCALE_FACTOR;
|
||||
}
|
||||
|
||||
// First 3 normals are negative, next 3 are positive
|
||||
// vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1));
|
||||
// uint norm_idx = (v_norm_ao >> 0) & 0x7u;
|
||||
// f_norm = (inst_mat * vec4(normals[], 0)).xyz;
|
||||
|
||||
// TODO: Consider adding a second, already-normalized (i.e. unscaled) matrix.
|
||||
// vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u].xyz);
|
||||
// vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u]);
|
||||
|
||||
// vec3 norm = bone_data.normals_mat[axis_idx].xyz;
|
||||
// norm = normalize(norm);
|
||||
// norm = norm / SCALE_FACTOR / locals.wind_sway.xyz;
|
||||
// norm = norm / (norm.x + norm.y + norm.z);
|
||||
// vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1);
|
||||
|
||||
// // Calculate normal here rather than for each pixel in the fragment shader
|
||||
// f_norm = normalize((
|
||||
// combined_mat *
|
||||
// vec4(norm, 0)
|
||||
// ).xyz);
|
||||
|
||||
const vec3 normals[8] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1), vec3(0,0,0), vec3(0,0,0));
|
||||
vec3 norm = /*normalize*/normals[(v_norm_ao >> 1u) & 3u].xyz;
|
||||
f_norm = mix(-norm, norm, v_norm_ao & 1u);
|
||||
|
||||
/* vec3 col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0;
|
||||
f_col = srgb_to_linear(col) * srgb_to_linear(inst_col);
|
||||
f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0; */
|
||||
f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));/* + 0.5*/;
|
||||
// f_atlas_pos = v_atlas_pos;
|
||||
/* for (uint i = 0u; i < light_shadow_count.z; ++i) {
|
||||
light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0));
|
||||
} */
|
||||
|
||||
// // Select glowing
|
||||
// if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) {
|
||||
// f_col *= 4.0;
|
||||
// }
|
||||
// f_light = 1.0;
|
||||
// if (select_pos.w > 0) */{
|
||||
//vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs;
|
||||
//f_select = (select_pos.w > 0 && select_pos.xyz == sprite_pos/* - vec3(0.5, 0.5, 0.0) * SCALE*/) ? 1.0 : 0.0;
|
||||
// }
|
||||
|
||||
gl_Position =
|
||||
all_mat *
|
||||
vec4(f_pos, 1);
|
||||
// gl_Position.z = -gl_Position.z;
|
||||
// gl_Position.z = -gl_Position.z / gl_Position.w;
|
||||
// gl_Position.z = -gl_Position.z / 100.0;
|
||||
// gl_Position.z = -gl_Position.z / 100.0;
|
||||
// gl_Position.z = -1000.0 / (gl_Position.z + 10000.0);
|
||||
}
|
BIN
assets/voxygen/voxel/mini_tree.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/mini_tree.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/carrot/1.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/sprite/carrot/1.vox
(Stored with Git LFS)
Binary file not shown.
@ -31,9 +31,11 @@ use common::{
|
||||
},
|
||||
event::{EventBus, LocalEvent},
|
||||
grid::Grid,
|
||||
lod::LodZone,
|
||||
outcome::Outcome,
|
||||
recipe::RecipeBook,
|
||||
resources::{DeltaTime, PlayerEntity, TimeOfDay},
|
||||
spiral::Spiral2d,
|
||||
terrain::{
|
||||
block::Block, map::MapConfig, neighbors, BiomeKind, SitesKind, SpriteKind, TerrainChunk,
|
||||
TerrainChunkSize,
|
||||
@ -186,8 +188,10 @@ pub struct Client {
|
||||
view_distance: Option<u32>,
|
||||
// TODO: move into voxygen
|
||||
loaded_distance: f32,
|
||||
zones: HashMap<Vec2<i32>, Option<LodZone>>,
|
||||
|
||||
pending_chunks: HashMap<Vec2<i32>, Instant>,
|
||||
pending_zones: HashMap<Vec2<i32>, Instant>,
|
||||
}
|
||||
|
||||
/// Holds data related to the current players characters, as well as some
|
||||
@ -685,8 +689,10 @@ impl Client {
|
||||
state,
|
||||
view_distance,
|
||||
loaded_distance: 0.0,
|
||||
zones: HashMap::new(),
|
||||
|
||||
pending_chunks: HashMap::new(),
|
||||
pending_zones: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -790,7 +796,8 @@ impl Client {
|
||||
| ClientGeneral::UnlockSkillGroup(_)
|
||||
| ClientGeneral::RequestPlayerPhysics { .. } => &mut self.in_game_stream,
|
||||
//Only in game, terrain
|
||||
ClientGeneral::TerrainChunkRequest { .. } => &mut self.terrain_stream,
|
||||
ClientGeneral::TerrainChunkRequest { .. }
|
||||
| ClientGeneral::LodZoneRequest { .. }=> &mut self.terrain_stream,
|
||||
//Always possible
|
||||
ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => {
|
||||
&mut self.general_stream
|
||||
@ -1334,6 +1341,7 @@ impl Client {
|
||||
pub fn clear_terrain(&mut self) {
|
||||
self.state.clear_terrain();
|
||||
self.pending_chunks.clear();
|
||||
self.pending_zones.clear();
|
||||
}
|
||||
|
||||
pub fn place_block(&mut self, pos: Vec3<i32>, block: Block) {
|
||||
@ -1486,6 +1494,8 @@ impl Client {
|
||||
self.state.remove_chunk(key);
|
||||
}
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
// Request chunks from the server.
|
||||
self.loaded_distance = ((view_distance * TerrainChunkSize::RECT_SIZE.x) as f32).powi(2);
|
||||
// +1 so we can find a chunk that's outside the vd for better fog
|
||||
@ -1523,7 +1533,7 @@ impl Client {
|
||||
self.send_msg_err(ClientGeneral::TerrainChunkRequest {
|
||||
key: *key,
|
||||
})?;
|
||||
self.pending_chunks.insert(*key, Instant::now());
|
||||
self.pending_chunks.insert(*key, now);
|
||||
} else {
|
||||
skip_mode = true;
|
||||
}
|
||||
@ -1546,10 +1556,29 @@ impl Client {
|
||||
+ (TerrainChunkSize::RECT_SIZE.y as f32 / 2.0).powi(2))
|
||||
.sqrt();
|
||||
|
||||
// Load nearby zones
|
||||
if let Some(zones) = self.nearby_zones() {
|
||||
for zone in zones {
|
||||
if self.zones.get(&zone).is_none() {
|
||||
if self.pending_zones.len() > 2 {
|
||||
break;
|
||||
} else {
|
||||
self.send_msg_err(ClientGeneral::LodZoneRequest {
|
||||
key: zone,
|
||||
})?;
|
||||
self.pending_zones.insert(zone, Instant::now());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If chunks are taking too long, assume they're no longer pending.
|
||||
let now = Instant::now();
|
||||
self.pending_chunks
|
||||
.retain(|_, created| now.duration_since(*created) < Duration::from_secs(3));
|
||||
.retain(|_, created| now.saturating_duration_since(*created) < Duration::from_secs(3));
|
||||
|
||||
// If zones are taking too long, assume they're no longer pending.
|
||||
self.pending_zones
|
||||
.retain(|_, created| now.saturating_duration_since(*created) < Duration::from_secs(8));
|
||||
}
|
||||
|
||||
// Send a ping to the server once every second
|
||||
@ -1587,6 +1616,18 @@ impl Client {
|
||||
Ok(frontend_events)
|
||||
}
|
||||
|
||||
pub fn nearby_zones(&self) -> Option<impl Iterator<Item = Vec2<i32>>> {
|
||||
const ZONE_RADIUS: u32 = 1;
|
||||
let player_zone = self.position()?.xy().map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / (sz * LodZone::SIZE) as i32);
|
||||
Some(Spiral2d::new()
|
||||
.take((ZONE_RADIUS * 2 + 1).pow(2) as usize)
|
||||
.map(move |rpos| player_zone + rpos))
|
||||
}
|
||||
|
||||
pub fn get_zone(&self, pos: Vec2<i32>) -> Option<&Option<LodZone>> {
|
||||
self.zones.get(&pos)
|
||||
}
|
||||
|
||||
/// Clean up the client after a tick.
|
||||
pub fn cleanup(&mut self) {
|
||||
// Cleanup the local state
|
||||
@ -1935,6 +1976,9 @@ impl Client {
|
||||
});
|
||||
}
|
||||
},
|
||||
ServerGeneral::LodZone { key, zone } => {
|
||||
self.zones.insert(key, zone);
|
||||
},
|
||||
_ => unreachable!("Not a terrain message"),
|
||||
}
|
||||
Ok(())
|
||||
|
@ -74,10 +74,13 @@ pub enum ClientGeneral {
|
||||
RefundSkill(Skill),
|
||||
UnlockSkillGroup(SkillGroupKind),
|
||||
RequestSiteInfo(SiteId),
|
||||
//Only in Game, via terrain stream
|
||||
// Only in Game, via terrain stream
|
||||
TerrainChunkRequest {
|
||||
key: Vec2<i32>,
|
||||
},
|
||||
LodZoneRequest {
|
||||
key: Vec2<i32>,
|
||||
},
|
||||
//Always possible
|
||||
ChatMsg(String),
|
||||
Terminate,
|
||||
@ -117,6 +120,7 @@ impl ClientMsg {
|
||||
| ClientGeneral::ExitInGame
|
||||
| ClientGeneral::PlayerPhysics { .. }
|
||||
| ClientGeneral::TerrainChunkRequest { .. }
|
||||
| ClientGeneral::LodZoneRequest { .. }
|
||||
| ClientGeneral::UnlockSkill(_)
|
||||
| ClientGeneral::RefundSkill(_)
|
||||
| ClientGeneral::RequestSiteInfo(_)
|
||||
|
@ -3,6 +3,7 @@ use crate::sync;
|
||||
use common::{
|
||||
character::{self, CharacterItem},
|
||||
comp::{self, invite::InviteKind, item::MaterialStatManifest},
|
||||
lod::LodZone,
|
||||
outcome::Outcome,
|
||||
recipe::RecipeBook,
|
||||
resources::TimeOfDay,
|
||||
@ -109,6 +110,10 @@ pub enum ServerGeneral {
|
||||
chunk: Result<CompressedData<TerrainChunk>, ()>,
|
||||
},
|
||||
TerrainBlockUpdates(CompressedData<HashMap<Vec3<i32>, Block>>),
|
||||
LodZone {
|
||||
key: Vec2<i32>,
|
||||
zone: Option<LodZone>,
|
||||
},
|
||||
// Always possible
|
||||
PlayerListUpdate(PlayerListUpdate),
|
||||
/// A message to go into the client chat box. The client is responsible for
|
||||
@ -231,6 +236,7 @@ impl ServerMsg {
|
||||
| ServerGeneral::InventoryUpdate(_, _)
|
||||
| ServerGeneral::TerrainChunkUpdate { .. }
|
||||
| ServerGeneral::TerrainBlockUpdates(_)
|
||||
| ServerGeneral::LodZone { .. }
|
||||
| ServerGeneral::SetViewDistance(_)
|
||||
| ServerGeneral::Outcomes(_)
|
||||
| ServerGeneral::Knockback(_)
|
||||
|
@ -46,6 +46,7 @@ pub mod figure;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod generation;
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod grid;
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod lod;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod lottery;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
18
common/src/lod.rs
Normal file
18
common/src/lod.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use vek::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct LodZone {
|
||||
pub trees: Vec<LodTree>,
|
||||
}
|
||||
|
||||
impl LodZone {
|
||||
/// Size in chunks
|
||||
pub const SIZE: u32 = 64;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct LodTree {
|
||||
pub pos: Vec2<u16>,
|
||||
pub alt: u16,
|
||||
}
|
@ -116,7 +116,8 @@ impl Client {
|
||||
},
|
||||
//Ingame related, terrain
|
||||
ServerGeneral::TerrainChunkUpdate { .. }
|
||||
| ServerGeneral::TerrainBlockUpdates(_) => {
|
||||
| ServerGeneral::TerrainBlockUpdates(_)
|
||||
| ServerGeneral::LodZone { .. } => {
|
||||
self.terrain_stream.lock().unwrap().send(g)
|
||||
},
|
||||
// Always possible
|
||||
@ -187,7 +188,8 @@ impl Client {
|
||||
},
|
||||
//Ingame related, terrain
|
||||
ServerGeneral::TerrainChunkUpdate { .. }
|
||||
| ServerGeneral::TerrainBlockUpdates(_) => {
|
||||
| ServerGeneral::TerrainBlockUpdates(_)
|
||||
| ServerGeneral::LodZone { .. } => {
|
||||
PreparedMsg::new(5, &g, &self.terrain_stream_params)
|
||||
},
|
||||
// Always possible
|
||||
|
@ -10,6 +10,7 @@ use common_net::msg::{ClientGeneral, CompressedData, ServerGeneral};
|
||||
use rayon::iter::ParallelIterator;
|
||||
use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage};
|
||||
use tracing::{debug, trace};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This system will handle new messages from clients
|
||||
#[derive(Default)]
|
||||
@ -19,6 +20,7 @@ impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
ReadExpect<'a, Arc<world::World>>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadExpect<'a, NetworkRequestMetrics>,
|
||||
ReadStorage<'a, Pos>,
|
||||
@ -35,6 +37,7 @@ impl<'a> System<'a> for Sys {
|
||||
(
|
||||
entities,
|
||||
server_event_bus,
|
||||
world,
|
||||
terrain,
|
||||
network_metrics,
|
||||
positions,
|
||||
@ -91,6 +94,12 @@ impl<'a> System<'a> for Sys {
|
||||
network_metrics.chunks_request_dropped.inc();
|
||||
}
|
||||
},
|
||||
ClientGeneral::LodZoneRequest { key } => {
|
||||
client.send(ServerGeneral::LodZone {
|
||||
key,
|
||||
zone: world.lod().get_zone(key).cloned(),
|
||||
})?;
|
||||
},
|
||||
_ => tracing::error!("not a client_terrain msg"),
|
||||
}
|
||||
Ok(())
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
},
|
||||
render::{
|
||||
self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline,
|
||||
TerrainPipeline,
|
||||
TerrainPipeline, LodStructurePipeline,
|
||||
},
|
||||
scene::math,
|
||||
};
|
||||
@ -267,6 +267,143 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a: 'b, 'b, V: 'a> Meshable<LodStructurePipeline, &'b mut GreedyMesh<'a>> for V
|
||||
where
|
||||
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
|
||||
/* TODO: Use VolIterator instead of manually iterating
|
||||
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
|
||||
* &'a V: BaseVol<Vox=Cell>, */
|
||||
{
|
||||
type Pipeline = LodStructurePipeline;
|
||||
type Result = ();
|
||||
type ShadowPipeline = ShadowPipeline;
|
||||
type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh<Self::Pipeline>, bool);
|
||||
type TranslucentPipeline = LodStructurePipeline;
|
||||
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
fn generate_mesh(
|
||||
self,
|
||||
(greedy, opaque_mesh, vertical_stripes): Self::Supplement,
|
||||
) -> MeshGen<LodStructurePipeline, &'b mut GreedyMesh<'a>, Self> {
|
||||
let max_size = greedy.max_size();
|
||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||
// in order to store the bone index. The two bits are instead taken out
|
||||
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
|
||||
// coordinate instead of 1 << 16.
|
||||
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
||||
|
||||
let lower_bound = self.lower_bound();
|
||||
let upper_bound = self.upper_bound();
|
||||
assert!(
|
||||
lower_bound.x <= upper_bound.x
|
||||
&& lower_bound.y <= upper_bound.y
|
||||
&& lower_bound.z <= upper_bound.z
|
||||
);
|
||||
// Lower bound coordinates must fit in an i16 (which means upper bound
|
||||
// coordinates fit as integers in a f23).
|
||||
assert!(
|
||||
i16::try_from(lower_bound.x).is_ok()
|
||||
&& i16::try_from(lower_bound.y).is_ok()
|
||||
&& i16::try_from(lower_bound.z).is_ok(),
|
||||
"Sprite offsets should fit in i16",
|
||||
);
|
||||
let greedy_size = upper_bound - lower_bound + 1;
|
||||
// TODO: Should this be 16, 16, 64?
|
||||
assert!(
|
||||
greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64,
|
||||
"Sprite size out of bounds: {:?} ≤ (31, 31, 63)",
|
||||
greedy_size - 1
|
||||
);
|
||||
|
||||
let (flat, flat_get) = {
|
||||
let (w, h, d) = (greedy_size + 2).into_tuple();
|
||||
let flat = {
|
||||
let vol = self;
|
||||
|
||||
let mut flat = vec![Cell::empty(); (w * h * d) as usize];
|
||||
let mut i = 0;
|
||||
for x in -1..greedy_size.x + 1 {
|
||||
for y in -1..greedy_size.y + 1 {
|
||||
for z in -1..greedy_size.z + 1 {
|
||||
let wpos = lower_bound + Vec3::new(x, y, z);
|
||||
let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty());
|
||||
flat[i] = block;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
flat
|
||||
};
|
||||
|
||||
let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
|
||||
.get((x * h * d + y * d + z) as usize)
|
||||
.copied()
|
||||
{
|
||||
Some(b) => b,
|
||||
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
|
||||
};
|
||||
|
||||
(flat, flat_get)
|
||||
};
|
||||
|
||||
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||
// into u16 which is safe to cast to usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
|
||||
let greedy_size_cross = greedy_size;
|
||||
let draw_delta = Vec3::new(1, 1, 1);
|
||||
|
||||
let get_light = move |flat: &mut _, pos: Vec3<i32>| {
|
||||
if flat_get(flat, pos).is_empty() {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
|
||||
let get_color = move |flat: &mut _, pos: Vec3<i32>| {
|
||||
flat_get(flat, pos).get_color().unwrap_or(Rgb::zero())
|
||||
};
|
||||
let get_opacity = move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty();
|
||||
let should_draw = move |flat: &mut _, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
|
||||
should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox))
|
||||
};
|
||||
// NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final
|
||||
// position, pos + mesh_delta, is guaranteed to fit in an f32).
|
||||
let mesh_delta = lower_bound.as_::<f32>();
|
||||
let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| {
|
||||
SpriteVertex::new(atlas_pos, pos + mesh_delta, norm)
|
||||
};
|
||||
|
||||
greedy.push(GreedyConfig {
|
||||
data: flat,
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
|
||||
));
|
||||
},
|
||||
make_face_texel: move |flat: &mut _, pos, light, glow| {
|
||||
TerrainVertex::make_col_light(light, glow, get_color(flat, pos))
|
||||
},
|
||||
});
|
||||
|
||||
(Mesh::new(), Mesh::new(), Mesh::new(), ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a: 'b, 'b, V: 'a> Meshable<ParticlePipeline, &'b mut GreedyMesh<'a>> for V
|
||||
where
|
||||
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
|
||||
|
@ -23,6 +23,7 @@ pub use self::{
|
||||
},
|
||||
fluid::FluidPipeline,
|
||||
lod_terrain::{Locals as LodTerrainLocals, LodData, LodTerrainPipeline},
|
||||
lod_structure::{Instance as LodStructureInstance, Locals as LodStructureLocals, LodStructurePipeline},
|
||||
particle::{Instance as ParticleInstance, ParticlePipeline},
|
||||
postprocess::{
|
||||
create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline,
|
||||
|
139
voxygen/src/render/pipelines/lod_structure.rs
Normal file
139
voxygen/src/render/pipelines/lod_structure.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use super::{
|
||||
super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt},
|
||||
shadow, terrain, Globals, Light, Shadow,
|
||||
};
|
||||
use core::fmt;
|
||||
use gfx::{
|
||||
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
|
||||
gfx_pipeline_inner, gfx_vertex_struct_meta, state::ColorMask,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
pub use super::sprite::Vertex;
|
||||
|
||||
gfx_defines! {
|
||||
/*
|
||||
vertex Vertex {
|
||||
pos: [f32; 3] = "v_pos",
|
||||
// Because we try to restrict terrain sprite data to a 128×128 block
|
||||
// we need an offset into the texture atlas.
|
||||
atlas_pos: u32 = "v_atlas_pos",
|
||||
// ____BBBBBBBBGGGGGGGGRRRRRRRR
|
||||
// col: u32 = "v_col",
|
||||
// ...AANNN
|
||||
// A = AO
|
||||
// N = Normal
|
||||
norm_ao: u32 = "v_norm_ao",
|
||||
}
|
||||
*/
|
||||
|
||||
constant Locals {
|
||||
nope: u32 = "nope",
|
||||
}
|
||||
|
||||
vertex/*constant*/ Instance {
|
||||
pos: [f32; 3] = "inst_pos",
|
||||
}
|
||||
|
||||
pipeline pipe {
|
||||
vbuf: gfx::VertexBuffer<Vertex> = (),
|
||||
ibuf: gfx::InstanceBuffer<Instance> = (),
|
||||
col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light",
|
||||
|
||||
locals: gfx::ConstantBuffer<Locals> = "u_locals",
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
lights: gfx::ConstantBuffer<Light> = "u_lights",
|
||||
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
|
||||
|
||||
point_shadow_maps: gfx::TextureSampler<f32> = "t_point_shadow_maps",
|
||||
directed_shadow_maps: gfx::TextureSampler<f32> = "t_directed_shadow_maps",
|
||||
|
||||
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
|
||||
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
|
||||
|
||||
noise: gfx::TextureSampler<f32> = "t_noise",
|
||||
|
||||
// Shadow stuff
|
||||
light_shadows: gfx::ConstantBuffer<shadow::Locals> = "u_light_shadows",
|
||||
|
||||
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
|
||||
tgt_depth_stencil: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
|
||||
// tgt_depth_stencil: gfx::DepthStencilTarget<TgtDepthStencilFmt> = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl fmt::Display for Vertex {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Vertex")
|
||||
.field("pos", &Vec3::<f32>::from(self.pos))
|
||||
.field(
|
||||
"atlas_pos",
|
||||
&Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF),
|
||||
)
|
||||
.field("norm_ao", &self.norm_ao)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
// NOTE: Limit to 16 (x) × 16 (y) × 32 (z).
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
pub fn new(
|
||||
atlas_pos: Vec2<u16>,
|
||||
pos: Vec3<f32>,
|
||||
norm: Vec3<f32>, /* , col: Rgb<f32>, ao: f32 */
|
||||
) -> Self {
|
||||
let norm_bits = if norm.x != 0.0 {
|
||||
if norm.x < 0.0 { 0 } else { 1 }
|
||||
} else if norm.y != 0.0 {
|
||||
if norm.y < 0.0 { 2 } else { 3 }
|
||||
} else {
|
||||
if norm.z < 0.0 { 4 } else { 5 }
|
||||
};
|
||||
|
||||
Self {
|
||||
// pos_norm: ((pos.x as u32) & 0x003F)
|
||||
// | ((pos.y as u32) & 0x003F) << 6
|
||||
// | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
|
||||
// | if meta { 1 } else { 0 } << 28
|
||||
// | (norm_bits & 0x7) << 29,
|
||||
pos: pos.into_array(),
|
||||
atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16,
|
||||
norm_ao: norm_bits,
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl Instance {
|
||||
pub fn new(
|
||||
pos: Vec3<f32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
pos: pos.into_array(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Instance {
|
||||
fn default() -> Self { Self::new(Vec3::zero()) }
|
||||
}
|
||||
|
||||
impl Default for Locals {
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
impl Locals {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
nope: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LodStructurePipeline;
|
||||
|
||||
impl Pipeline for LodStructurePipeline {
|
||||
type Vertex = Vertex;
|
||||
}
|
@ -2,6 +2,7 @@ pub mod clouds;
|
||||
pub mod figure;
|
||||
pub mod fluid;
|
||||
pub mod lod_terrain;
|
||||
pub mod lod_structure;
|
||||
pub mod particle;
|
||||
pub mod postprocess;
|
||||
pub mod shadow;
|
||||
|
@ -5,7 +5,7 @@ use super::{
|
||||
mesh::Mesh,
|
||||
model::{DynamicModel, Model},
|
||||
pipelines::{
|
||||
clouds, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain,
|
||||
clouds, figure, fluid, lod_terrain, lod_structure, particle, postprocess, shadow, skybox, sprite, terrain,
|
||||
ui, GlobalModel, Globals,
|
||||
},
|
||||
texture::Texture,
|
||||
@ -143,6 +143,8 @@ struct Shaders {
|
||||
fluid_frag_shiny: AssetHandle<Glsl>,
|
||||
sprite_vert: AssetHandle<Glsl>,
|
||||
sprite_frag: AssetHandle<Glsl>,
|
||||
lod_structure_vert: AssetHandle<Glsl>,
|
||||
lod_structure_frag: AssetHandle<Glsl>,
|
||||
particle_vert: AssetHandle<Glsl>,
|
||||
particle_frag: AssetHandle<Glsl>,
|
||||
ui_vert: AssetHandle<Glsl>,
|
||||
@ -203,6 +205,8 @@ impl assets::Compound for Shaders {
|
||||
fluid_frag_shiny: AssetExt::load("voxygen.shaders.fluid-frag.shiny")?,
|
||||
sprite_vert: AssetExt::load("voxygen.shaders.sprite-vert")?,
|
||||
sprite_frag: AssetExt::load("voxygen.shaders.sprite-frag")?,
|
||||
lod_structure_vert: AssetExt::load("voxygen.shaders.lod-structure-vert")?,
|
||||
lod_structure_frag: AssetExt::load("voxygen.shaders.lod-structure-frag")?,
|
||||
particle_vert: AssetExt::load("voxygen.shaders.particle-vert")?,
|
||||
particle_frag: AssetExt::load("voxygen.shaders.particle-frag")?,
|
||||
ui_vert: AssetExt::load("voxygen.shaders.ui-vert")?,
|
||||
@ -267,6 +271,7 @@ pub struct Renderer {
|
||||
terrain_pipeline: GfxPipeline<terrain::pipe::Init<'static>>,
|
||||
fluid_pipeline: GfxPipeline<fluid::pipe::Init<'static>>,
|
||||
sprite_pipeline: GfxPipeline<sprite::pipe::Init<'static>>,
|
||||
lod_structure_pipeline: GfxPipeline<lod_structure::pipe::Init<'static>>,
|
||||
particle_pipeline: GfxPipeline<particle::pipe::Init<'static>>,
|
||||
ui_pipeline: GfxPipeline<ui::pipe::Init<'static>>,
|
||||
lod_terrain_pipeline: GfxPipeline<lod_terrain::pipe::Init<'static>>,
|
||||
@ -319,6 +324,7 @@ impl Renderer {
|
||||
terrain_pipeline,
|
||||
fluid_pipeline,
|
||||
sprite_pipeline,
|
||||
lod_structure_pipeline,
|
||||
particle_pipeline,
|
||||
ui_pipeline,
|
||||
lod_terrain_pipeline,
|
||||
@ -415,6 +421,7 @@ impl Renderer {
|
||||
terrain_pipeline,
|
||||
fluid_pipeline,
|
||||
sprite_pipeline,
|
||||
lod_structure_pipeline,
|
||||
particle_pipeline,
|
||||
ui_pipeline,
|
||||
lod_terrain_pipeline,
|
||||
@ -907,6 +914,7 @@ impl Renderer {
|
||||
terrain_pipeline,
|
||||
fluid_pipeline,
|
||||
sprite_pipeline,
|
||||
lod_structure_pipeline,
|
||||
particle_pipeline,
|
||||
ui_pipeline,
|
||||
lod_terrain_pipeline,
|
||||
@ -922,6 +930,7 @@ impl Renderer {
|
||||
self.terrain_pipeline = terrain_pipeline;
|
||||
self.fluid_pipeline = fluid_pipeline;
|
||||
self.sprite_pipeline = sprite_pipeline;
|
||||
self.lod_structure_pipeline = lod_structure_pipeline;
|
||||
self.particle_pipeline = particle_pipeline;
|
||||
self.ui_pipeline = ui_pipeline;
|
||||
self.lod_terrain_pipeline = lod_terrain_pipeline;
|
||||
@ -1673,6 +1682,69 @@ impl Renderer {
|
||||
);
|
||||
}
|
||||
|
||||
/// Queue the rendering of the provided lod structures in the upcoming
|
||||
/// frame.
|
||||
pub fn render_lod_structures(
|
||||
&mut self,
|
||||
model: &Model<lod_structure::LodStructurePipeline>,
|
||||
col_lights: &Texture<ColLightFmt>,
|
||||
global: &GlobalModel,
|
||||
locals: &Consts<lod_structure::Locals>,
|
||||
instances: &Instances<lod_structure::Instance>,
|
||||
lod: &lod_terrain::LodData,
|
||||
) {
|
||||
let (point_shadow_maps, directed_shadow_maps) =
|
||||
if let Some(shadow_map) = &mut self.shadow_map {
|
||||
(
|
||||
(
|
||||
shadow_map.point_res.clone(),
|
||||
shadow_map.point_sampler.clone(),
|
||||
),
|
||||
(
|
||||
shadow_map.directed_res.clone(),
|
||||
shadow_map.directed_sampler.clone(),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
(self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
||||
(self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
||||
)
|
||||
};
|
||||
|
||||
self.encoder.draw(
|
||||
&gfx::Slice {
|
||||
start: model.vertex_range().start,
|
||||
end: model.vertex_range().end,
|
||||
base_vertex: 0,
|
||||
instances: Some((instances.count() as u32, 0)),
|
||||
buffer: gfx::IndexBuffer::Auto,
|
||||
},
|
||||
&self.lod_structure_pipeline.pso,
|
||||
&lod_structure::pipe::Data {
|
||||
vbuf: model.vbuf.clone(),
|
||||
ibuf: instances.ibuf.clone(),
|
||||
col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()),
|
||||
// NOTE: It would be nice if this wasn't needed and we could use a constant buffer
|
||||
// offset into the sprite data. Hopefully, when we switch to wgpu we can do this,
|
||||
// as it offers the exact API we want (the equivalent can be done in OpenGL using
|
||||
// glBindBufferOffset).
|
||||
locals: locals.buf.clone(),
|
||||
globals: global.globals.buf.clone(),
|
||||
lights: global.lights.buf.clone(),
|
||||
shadows: global.shadows.buf.clone(),
|
||||
light_shadows: global.shadow_mats.buf.clone(),
|
||||
point_shadow_maps,
|
||||
directed_shadow_maps,
|
||||
noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
||||
alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()),
|
||||
horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()),
|
||||
tgt_color: self.tgt_color_view.clone(),
|
||||
tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Queue the rendering of the provided LoD terrain model in the upcoming
|
||||
/// frame.
|
||||
pub fn render_lod_terrain(
|
||||
@ -1880,6 +1952,7 @@ fn create_pipelines(
|
||||
GfxPipeline<terrain::pipe::Init<'static>>,
|
||||
GfxPipeline<fluid::pipe::Init<'static>>,
|
||||
GfxPipeline<sprite::pipe::Init<'static>>,
|
||||
GfxPipeline<lod_structure::pipe::Init<'static>>,
|
||||
GfxPipeline<particle::pipe::Init<'static>>,
|
||||
GfxPipeline<ui::pipe::Init<'static>>,
|
||||
GfxPipeline<lod_terrain::pipe::Init<'static>>,
|
||||
@ -2011,6 +2084,16 @@ fn create_pipelines(
|
||||
gfx::state::CullFace::Back,
|
||||
)?;
|
||||
|
||||
// Construct a pipeline for rendering LoD structures
|
||||
let lod_structure_pipeline = create_pipeline(
|
||||
factory,
|
||||
lod_structure::pipe::new(),
|
||||
&shaders.lod_structure_vert.read().0,
|
||||
&shaders.lod_structure_frag.read().0,
|
||||
&include_ctx,
|
||||
gfx::state::CullFace::Back,
|
||||
)?;
|
||||
|
||||
// Construct a pipeline for rendering particles
|
||||
let particle_pipeline = create_pipeline(
|
||||
factory,
|
||||
@ -2145,6 +2228,7 @@ fn create_pipelines(
|
||||
terrain_pipeline,
|
||||
fluid_pipeline,
|
||||
sprite_pipeline,
|
||||
lod_structure_pipeline,
|
||||
particle_pipeline,
|
||||
ui_pipeline,
|
||||
lod_terrain_pipeline,
|
||||
|
@ -2,17 +2,31 @@ use crate::{
|
||||
render::{
|
||||
pipelines::lod_terrain::{Locals, LodData, Vertex},
|
||||
Consts, GlobalModel, LodTerrainPipeline, Mesh, Model, Quad, Renderer,
|
||||
LodStructurePipeline, LodStructureLocals, LodStructureInstance, Instances, Texture, ColLightFmt, ShadowPipeline,
|
||||
},
|
||||
settings::Settings,
|
||||
scene::SceneData,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{spiral::Spiral2d, util::srgba_to_linear};
|
||||
use common::{
|
||||
lod::LodZone,
|
||||
terrain::TerrainChunkSize,
|
||||
spiral::Spiral2d,
|
||||
util::srgba_to_linear,
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use vek::*;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
pub struct Lod {
|
||||
model: Option<(u32, Model<LodTerrainPipeline>)>,
|
||||
locals: Consts<Locals>,
|
||||
data: LodData,
|
||||
|
||||
structure_col_lights: Texture<ColLightFmt>,
|
||||
tree_locals: Consts<LodStructureLocals>,
|
||||
tree_model: Model<LodStructurePipeline>,
|
||||
zones: HashMap<Vec2<i32>, Zone>,
|
||||
}
|
||||
|
||||
// TODO: Make constant when possible.
|
||||
@ -23,6 +37,25 @@ pub fn water_color() -> Rgba<f32> {
|
||||
|
||||
impl Lod {
|
||||
pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self {
|
||||
let (tree_model, structure_col_lights) = {
|
||||
use crate::{mesh::{Meshable, greedy::GreedyMesh}, render::SpritePipeline};
|
||||
use common::{figure::Segment, assets::{DotVoxAsset, AssetExt}};
|
||||
|
||||
let max_texture_size = renderer.max_texture_size();
|
||||
let max_size = guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
||||
let mut greedy = GreedyMesh::new(max_size);
|
||||
let mut mesh = Mesh::new();
|
||||
Meshable::<LodStructurePipeline, &mut GreedyMesh>::generate_mesh(
|
||||
Segment::from(&DotVoxAsset::load_expect("voxygen.voxel.mini_tree").read().0),
|
||||
(&mut greedy, &mut mesh, false),
|
||||
);
|
||||
(
|
||||
renderer.create_model(&mesh).expect("Failed to upload sprite model data to the GPU!"),
|
||||
ShadowPipeline::create_col_lights(renderer, &greedy.finalize())
|
||||
.expect("Failed to upload sprite color and light data to the GPU!"),
|
||||
)
|
||||
};
|
||||
|
||||
Self {
|
||||
model: None,
|
||||
locals: renderer.create_consts(&[Locals::default()]).unwrap(),
|
||||
@ -35,6 +68,10 @@ impl Lod {
|
||||
settings.graphics.lod_detail.max(100).min(2500),
|
||||
water_color().into_array().into(),
|
||||
),
|
||||
tree_model,
|
||||
structure_col_lights,
|
||||
tree_locals: renderer.create_consts(&[LodStructureLocals::default()]).unwrap(),
|
||||
zones: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +82,11 @@ impl Lod {
|
||||
self.data.tgt_detail = (detail - detail % 2).max(100).min(2500);
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer) {
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
scene_data: &SceneData,
|
||||
) {
|
||||
if self
|
||||
.model
|
||||
.as_ref()
|
||||
@ -59,12 +100,56 @@ impl Lod {
|
||||
.unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(zones) = scene_data.client.nearby_zones() {
|
||||
for zone_pos in zones {
|
||||
if let Some(Some(zone)) = scene_data.client.get_zone(zone_pos) {
|
||||
self.zones
|
||||
.entry(zone_pos)
|
||||
.or_insert_with(|| {
|
||||
let zone_wpos = zone_pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e * (sz * LodZone::SIZE) as i32);
|
||||
println!("Zone {:?} ({:?}) has {} trees", zone_pos, zone_wpos, zone.trees.len());
|
||||
Zone {
|
||||
tree_instances: renderer
|
||||
.create_instances(&zone.trees
|
||||
.iter()
|
||||
.map(|tree| LodStructureInstance::new(
|
||||
(zone_wpos + tree.pos.map(|e| e as i32)).map(|e| e as f32).with_z(tree.alt as f32)
|
||||
))
|
||||
.collect::<Vec<_>>())
|
||||
.expect("Failed to upload lod tree instances to the GPU!"),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer, global: &GlobalModel) {
|
||||
pub fn render(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
global: &GlobalModel,
|
||||
lod: &LodData,
|
||||
scene_data: &SceneData,
|
||||
) {
|
||||
if let Some((_, model)) = self.model.as_ref() {
|
||||
renderer.render_lod_terrain(&model, global, &self.locals, &self.data);
|
||||
}
|
||||
|
||||
if let Some(zones) = scene_data.client.nearby_zones() {
|
||||
for zone_pos in zones {
|
||||
if let Some(zone) = self.zones.get(&zone_pos) {
|
||||
renderer.render_lod_structures(
|
||||
&self.tree_model,
|
||||
&self.structure_col_lights,
|
||||
global,
|
||||
&self.tree_locals,
|
||||
&zone.tree_instances,
|
||||
lod,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,3 +180,7 @@ fn create_lod_terrain_mesh(detail: u32) -> Mesh<LodTerrainPipeline> {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
struct Zone {
|
||||
tree_instances: Instances<LodStructureInstance>,
|
||||
}
|
||||
|
@ -686,7 +686,7 @@ impl Scene {
|
||||
.expect("Failed to update post-process locals");
|
||||
|
||||
// Maintain LoD.
|
||||
self.lod.maintain(renderer);
|
||||
self.lod.maintain(renderer, &scene_data);
|
||||
|
||||
// Maintain the terrain.
|
||||
let (_visible_bounds, visible_light_volume, visible_psr_bounds) = self.terrain.maintain(
|
||||
@ -1076,7 +1076,7 @@ impl Scene {
|
||||
lod,
|
||||
camera_data,
|
||||
);
|
||||
self.lod.render(renderer, global);
|
||||
self.lod.render(renderer, global, lod, scene_data);
|
||||
|
||||
// Render the skybox.
|
||||
renderer.render_skybox(&self.skybox.model, global, &self.skybox.locals, lod);
|
||||
|
@ -86,6 +86,7 @@ impl ForestKind {
|
||||
|
||||
pub struct TreeAttr {
|
||||
pub pos: Vec2<i32>,
|
||||
pub alt_approx: f32,
|
||||
pub seed: u32,
|
||||
pub scale: f32,
|
||||
pub forest_kind: ForestKind,
|
||||
|
@ -66,6 +66,7 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
scale,
|
||||
forest_kind,
|
||||
inhabited,
|
||||
..
|
||||
} in trees
|
||||
{
|
||||
let tree = if let Some(tree) = tree_cache.entry(pos).or_insert_with(|| {
|
||||
@ -75,19 +76,12 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
|
||||
// Ensure that it's valid to place a *thing* here
|
||||
if col.alt < col.water_level
|
||||
|| col.spawn_rate < 0.9
|
||||
|| col.water_dist.map(|d| d < 8.0).unwrap_or(false)
|
||||
|| col.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
// Ensure that it's valid to place a tree here
|
||||
if !is_quirky && ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Tree {
|
||||
pos: Vec3::new(pos.x, pos.y, col.alt as i32),
|
||||
model: 'model: {
|
||||
|
@ -25,6 +25,7 @@ pub mod config;
|
||||
pub mod index;
|
||||
pub mod land;
|
||||
pub mod layer;
|
||||
pub mod lod;
|
||||
pub mod pathfinding;
|
||||
pub mod sim;
|
||||
pub mod sim2;
|
||||
@ -37,6 +38,7 @@ pub use crate::{
|
||||
canvas::{Canvas, CanvasInfo},
|
||||
config::CONFIG,
|
||||
land::Land,
|
||||
lod::LodInfo,
|
||||
};
|
||||
pub use block::BlockGen;
|
||||
pub use column::ColumnSample;
|
||||
@ -67,6 +69,7 @@ pub enum Error {
|
||||
pub struct World {
|
||||
sim: sim::WorldSim,
|
||||
civs: civ::Civs,
|
||||
lod: LodInfo,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -94,13 +97,17 @@ impl World {
|
||||
|
||||
sim2::simulate(&mut index, &mut sim);
|
||||
|
||||
(Self { sim, civs }, IndexOwned::new(index))
|
||||
let lod = LodInfo::new(&sim);
|
||||
|
||||
(Self { sim, civs, lod }, IndexOwned::new(index))
|
||||
}
|
||||
|
||||
pub fn sim(&self) -> &sim::WorldSim { &self.sim }
|
||||
|
||||
pub fn civs(&self) -> &civ::Civs { &self.civs }
|
||||
|
||||
pub fn lod(&self) -> &LodInfo { &self.lod }
|
||||
|
||||
pub fn tick(&self, _dt: Duration) {
|
||||
// TODO
|
||||
}
|
||||
|
43
world/src/lod.rs
Normal file
43
world/src/lod.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use crate::{
|
||||
sim::WorldSim,
|
||||
util::Grid,
|
||||
};
|
||||
use common::{
|
||||
terrain::TerrainChunkSize,
|
||||
vol::RectVolSize,
|
||||
lod::{LodZone, LodTree},
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
pub struct LodInfo {
|
||||
zones: Grid<LodZone>,
|
||||
}
|
||||
|
||||
impl LodInfo {
|
||||
pub fn new(sim: &WorldSim) -> Self {
|
||||
Self {
|
||||
zones: Grid::populate_from(
|
||||
sim.get_size().map(|e| (e.next_power_of_two() / LodZone::SIZE).max(1) as i32),
|
||||
|pos| {
|
||||
let to_wpos = |pos| pos * (TerrainChunkSize::RECT_SIZE * LodZone::SIZE).map(|e| e as i32);
|
||||
|
||||
let zone_wpos = to_wpos(pos);
|
||||
let trees = sim
|
||||
.get_trees_area(zone_wpos, to_wpos(pos + 1))
|
||||
.map(|attr| LodTree {
|
||||
pos: (attr.pos - zone_wpos).map(|e| e as u16),
|
||||
alt: attr.alt_approx as u16,
|
||||
})
|
||||
// TODO: filter those outside zone bounds
|
||||
.collect();
|
||||
|
||||
LodZone { trees }
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_zone(&self, pos: Vec2<i32>) -> Option<&LodZone> {
|
||||
self.zones.get(pos)
|
||||
}
|
||||
}
|
@ -2044,12 +2044,17 @@ impl WorldSim {
|
||||
self.get_nearest_way(wpos, |chunk| Some(chunk.cave))
|
||||
}
|
||||
|
||||
pub fn get_near_trees(&self, wpos: Vec2<i32>) -> impl Iterator<Item = TreeAttr> + '_ {
|
||||
self.get_trees_area(wpos, wpos + 1)
|
||||
}
|
||||
|
||||
/// Return an iterator over candidate tree positions (note that only some of
|
||||
/// these will become trees since environmental parameters may forbid
|
||||
/// them spawning).
|
||||
pub fn get_near_trees(&self, wpos: Vec2<i32>) -> impl Iterator<Item = TreeAttr> + '_ {
|
||||
pub fn get_trees_area(&self, min: Vec2<i32>, max: Vec2<i32>) -> impl Iterator<Item = TreeAttr> + '_ {
|
||||
// Deterministic based on wpos
|
||||
let normal_trees = std::array::IntoIter::new(self.gen_ctx.structure_gen.get(wpos))
|
||||
let normal_trees = self.gen_ctx.structure_gen
|
||||
.par_iter(min, max)
|
||||
.filter_map(move |(pos, seed)| {
|
||||
let chunk = self.get_wpos(pos)?;
|
||||
let env = Environment {
|
||||
@ -2061,29 +2066,40 @@ impl WorldSim {
|
||||
0.0
|
||||
},
|
||||
};
|
||||
Some(TreeAttr {
|
||||
pos,
|
||||
seed,
|
||||
scale: 1.0,
|
||||
forest_kind: *Lottery::from(
|
||||
ForestKind::into_enum_iter()
|
||||
.enumerate()
|
||||
.map(|(i, fk)| {
|
||||
const CLUSTER_SIZE: f64 = 48.0;
|
||||
let nz = (FastNoise2d::new(i as u32 * 37)
|
||||
.get(pos.map(|e| e as f64) / CLUSTER_SIZE)
|
||||
+ 1.0)
|
||||
/ 2.0;
|
||||
(fk.proclivity(&env) * nz, Some(fk))
|
||||
})
|
||||
.chain(std::iter::once((0.001, None)))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.choose_seeded(seed)
|
||||
.as_ref()?,
|
||||
inhabited: false,
|
||||
})
|
||||
});
|
||||
if !chunk.is_underwater()
|
||||
&& ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 < chunk.tree_density
|
||||
&& chunk.spawn_rate > 0.5
|
||||
{
|
||||
Some(TreeAttr {
|
||||
pos,
|
||||
alt_approx: chunk.alt,
|
||||
seed,
|
||||
scale: 1.0,
|
||||
forest_kind: *Lottery::from(
|
||||
ForestKind::into_enum_iter()
|
||||
.enumerate()
|
||||
.map(|(i, fk)| {
|
||||
const CLUSTER_SIZE: f64 = 48.0;
|
||||
let nz = (FastNoise2d::new(i as u32 * 37)
|
||||
.get(pos.map(|e| e as f64) / CLUSTER_SIZE)
|
||||
+ 1.0)
|
||||
/ 2.0;
|
||||
(fk.proclivity(&env) * nz, Some(fk))
|
||||
})
|
||||
.chain(std::iter::once((0.001, None)))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.choose_seeded(seed)
|
||||
.as_ref()?,
|
||||
inhabited: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
// TODO: don't do this, it's horrible and slow
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter();
|
||||
|
||||
// // For testing
|
||||
// let giant_trees =
|
||||
|
Loading…
Reference in New Issue
Block a user