Merge branch 'zesterer/instancing' into 'master'

Instancing (block sprites)

See merge request veloren/veloren!450
This commit is contained in:
Joshua Barretto 2019-08-22 11:10:57 +00:00
commit d9854ad682
69 changed files with 909 additions and 81 deletions

View File

@ -32,7 +32,7 @@ void main() {
vec3 surf_color = srgb_to_linear(model_col.rgb * f_col) * 4.0 * light;
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x);
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true);
vec3 color = mix(surf_color, fog_color, fog_level);
tgt_color = vec4(color, 1.0);

View File

@ -41,14 +41,14 @@ void main() {
vec3 surf_color = f_col * light;
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x);
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true);
vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
vec3 reflect_ray_dir = reflect(cam_to_frag, norm);
// Hack to prevent the reflection ray dipping below the horizon and creating weird blue spots in the water
reflect_ray_dir.z = max(reflect_ray_dir.z, 0.05);
vec3 reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x) * f_light;
vec3 reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x, false) * f_light;
// 0 = 100% reflection, 1 = translucent water
float passthrough = pow(dot(faceforward(norm, norm, cam_to_frag), -cam_to_frag), 1.0);

View File

@ -5,12 +5,12 @@ const float PI = 3.141592;
const vec3 SKY_DAY_TOP = vec3(0.1, 0.2, 0.9);
const vec3 SKY_DAY_MID = vec3(0.02, 0.08, 0.8);
const vec3 SKY_DAY_BOT = vec3(0.02, 0.01, 0.3);
const vec3 DAY_LIGHT = vec3(0.75, 0.75, 1.0);
const vec3 DAY_LIGHT = vec3(1.3, 0.9, 1.1);
const vec3 SKY_DUSK_TOP = vec3(0.21, 0.28, 0.50);
const vec3 SKY_DUSK_MID = vec3(0.68, 0.03, 0.0);
const vec3 SKY_DUSK_BOT = vec3(0.0, 0.0, 0.13);
const vec3 DUSK_LIGHT = vec3(0.95, 0.6, 0.4);
const vec3 SKY_DUSK_TOP = vec3(0.06, 0.1, 0.20);
const vec3 SKY_DUSK_MID = vec3(0.35, 0.1, 0.15);
const vec3 SKY_DUSK_BOT = vec3(0.0, 0.1, 0.13);
const vec3 DUSK_LIGHT = vec3(3.0, 0.65, 0.3);
const vec3 SKY_NIGHT_TOP = vec3(0.001, 0.001, 0.0025);
const vec3 SKY_NIGHT_MID = vec3(0.001, 0.005, 0.02);
@ -33,7 +33,7 @@ float get_sun_brightness(vec3 sun_dir) {
const float PERSISTENT_AMBIANCE = 0.008;
vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
const float SUN_AMBIANCE = 0.075;
const float SUN_AMBIANCE = 0.15;
vec3 sun_dir = get_sun_dir(time_of_day);
@ -41,7 +41,7 @@ vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
// clamp() changed to max() as sun_dir.z is produced from a cos() function and therefore never greater than 1
vec3 sun_color = normalize(mix(
vec3 sun_color = mix(
mix(
DUSK_LIGHT,
NIGHT_LIGHT,
@ -49,7 +49,7 @@ vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
),
DAY_LIGHT,
max(-sun_dir.z, 0)
));
);
vec3 diffuse_light = (SUN_AMBIANCE + max(dot(-norm, sun_dir), 0.0) * sun_color) * sun_light + PERSISTENT_AMBIANCE;
@ -83,12 +83,15 @@ float is_star_at(vec3 dir) {
return 0.0;
}
vec3 get_sky_color(vec3 dir, float time_of_day) {
vec3 get_sky_color(vec3 dir, float time_of_day, bool with_stars) {
// Sky color
vec3 sun_dir = get_sun_dir(time_of_day);
// Add white dots for stars. Note these flicker and jump due to FXAA
float star = is_star_at(dir);
float star = 0.0;
if (with_stars) {
star = is_star_at(dir);
}
// Replaced all clamp(sun_dir, 0, 1) with max(sun_dir, 0) because sun_dir is calculated from sin and cos, which are never > 1
@ -134,8 +137,8 @@ vec3 get_sky_color(vec3 dir, float time_of_day) {
// Sun
const vec3 SUN_HALO_COLOR = vec3(1.0, 0.35, 0.1) * 0.3;
const vec3 SUN_SURF_COLOR = vec3(1.0, 0.9, 0.35) * 200.0;
const vec3 SUN_HALO_COLOR = vec3(1.5, 0.35, 0.0) * 0.3;
const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 200.0;
vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * SUN_HALO_COLOR;
vec3 sun_surf = pow(max(dot(dir, -sun_dir) - 0.0045, 0.0), 1000.0) * SUN_SURF_COLOR;

View File

@ -13,5 +13,5 @@ uniform u_locals {
out vec4 tgt_color;
void main() {
tgt_color = vec4(get_sky_color(normalize(f_pos), time_of_day.x), 1.0);
tgt_color = vec4(get_sky_color(normalize(f_pos), time_of_day.x, true), 1.0);
}

View File

@ -0,0 +1,27 @@
#version 330 core
#include <globals.glsl>
in vec3 f_pos;
flat in vec3 f_norm;
in vec3 f_col;
in float f_light;
out vec4 tgt_color;
#include <sky.glsl>
#include <light.glsl>
const float RENDER_DIST = 112.0;
const float FADE_DIST = 32.0;
void main() {
vec3 light = get_sun_diffuse(f_norm, time_of_day.x) * f_light + light_at(f_pos, f_norm);
vec3 surf_color = f_col * light;
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true);
vec3 color = mix(surf_color, fog_color, fog_level);
tgt_color = vec4(color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (RENDER_DIST - FADE_DIST)) / FADE_DIST, 0, 1));
}

View File

@ -0,0 +1,49 @@
#version 330 core
#include <globals.glsl>
#include <srgb.glsl>
in vec3 v_pos;
in vec3 v_norm;
in vec3 v_col;
in vec4 inst_mat0;
in vec4 inst_mat1;
in vec4 inst_mat2;
in vec4 inst_mat3;
in vec3 inst_col;
in float inst_wind_sway;
out vec3 f_pos;
flat out vec3 f_norm;
out vec3 f_col;
out float f_light;
const float SCALE = 1.0 / 11.0;
void main() {
mat4 inst_mat;
inst_mat[0] = inst_mat0;
inst_mat[1] = inst_mat1;
inst_mat[2] = inst_mat2;
inst_mat[3] = inst_mat3;
f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz;
// Wind waving
f_pos += 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(v_pos.z * SCALE, 1.3) * 0.2;
f_norm = (inst_mat * vec4(v_norm, 0)).xyz;
f_col = v_col * inst_col;
f_light = 1.0;
gl_Position =
proj_mat *
view_mat *
vec4(f_pos, 1);
}

View File

@ -22,7 +22,7 @@ void main() {
vec3 surf_color = f_col * light;
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x);
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true);
vec3 color = mix(surf_color, fog_color, fog_level);
tgt_color = vec4(color, 1.0);

BIN
assets/voxygen/voxel/sprite/cacti/barrel_cactus.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/cacti/large_cactus.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_blue_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_blue_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_pink_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_pink_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_pink_3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_purple_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_red_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_white_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/flower_yellow_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/sunflower_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/flowers/sunflower_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/fruit/apple.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/fruit/apple_half.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_long_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_long_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_long_3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_long_4.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_long_5.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_med_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_med_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_med_3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_med_4.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_med_5.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_short_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_short_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_short_3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_short_4.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/grass/grass_short_5.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_10.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_4.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_5.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_6.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_7.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_8.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/sprite/velorite_small/velorite_9.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/tree/fruit/1.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/fruit/2.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/fruit/3.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/fruit/4.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/fruit/5.vox (Stored with Git LFS)

Binary file not shown.

BIN
assets/world/tree/fruit/6.vox (Stored with Git LFS)

Binary file not shown.

View File

@ -3,19 +3,45 @@ use serde_derive::{Deserialize, Serialize};
use std::ops::Deref;
use vek::*;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[repr(u8)]
pub enum BlockKind {
Air,
Normal,
Dense,
Water,
LargeCactus,
BarrelCactus,
BlueFlower,
PinkFlower,
PurpleFlower,
RedFlower,
WhiteFlower,
YellowFlower,
Sunflower,
LongGrass,
MediumGrass,
ShortGrass,
Apple,
}
impl BlockKind {
pub fn is_air(&self) -> bool {
match self {
BlockKind::Air => true,
BlockKind::LargeCactus => false,
BlockKind::BarrelCactus => true,
BlockKind::BlueFlower => true,
BlockKind::PinkFlower => true,
BlockKind::PurpleFlower => true,
BlockKind::RedFlower => true,
BlockKind::WhiteFlower => true,
BlockKind::YellowFlower => true,
BlockKind::Sunflower => true,
BlockKind::LongGrass => true,
BlockKind::MediumGrass => true,
BlockKind::ShortGrass => true,
BlockKind::Apple => true,
_ => false,
}
}
@ -31,6 +57,19 @@ impl BlockKind {
match self {
BlockKind::Air => false,
BlockKind::Water => false,
BlockKind::LargeCactus => false,
BlockKind::BarrelCactus => false,
BlockKind::BlueFlower => false,
BlockKind::PinkFlower => false,
BlockKind::PurpleFlower => false,
BlockKind::RedFlower => false,
BlockKind::WhiteFlower => false,
BlockKind::YellowFlower => false,
BlockKind::Sunflower => false,
BlockKind::LongGrass => false,
BlockKind::MediumGrass => false,
BlockKind::ShortGrass => false,
BlockKind::Apple => false,
_ => true,
}
}
@ -39,6 +78,19 @@ impl BlockKind {
match self {
BlockKind::Air => false,
BlockKind::Water => false,
BlockKind::LargeCactus => true,
BlockKind::BarrelCactus => true,
BlockKind::BlueFlower => false,
BlockKind::PinkFlower => false,
BlockKind::PurpleFlower => false,
BlockKind::RedFlower => false,
BlockKind::WhiteFlower => false,
BlockKind::YellowFlower => false,
BlockKind::Sunflower => false,
BlockKind::LongGrass => false,
BlockKind::MediumGrass => false,
BlockKind::ShortGrass => false,
BlockKind::Apple => true,
_ => true,
}
}

View File

@ -4,7 +4,7 @@ mod vol;
use crate::render::{self, Mesh};
pub trait Meshable {
pub trait Meshable<P: render::Pipeline, T: render::Pipeline> {
type Pipeline: render::Pipeline;
type TranslucentPipeline: render::Pipeline;
type Supplement;

View File

@ -1,6 +1,6 @@
use crate::{
mesh::{vol, Meshable},
render::{self, FigurePipeline, Mesh},
render::{self, FigurePipeline, Mesh, SpritePipeline},
};
use common::{
figure::Segment,
@ -10,8 +10,9 @@ use common::{
use vek::*;
type FigureVertex = <FigurePipeline as render::Pipeline>::Vertex;
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex;
impl Meshable for Segment {
impl Meshable<FigurePipeline, FigurePipeline> for Segment {
type Pipeline = FigurePipeline;
type TranslucentPipeline = FigurePipeline;
type Supplement = Vec3<f32>;
@ -51,3 +52,43 @@ impl Meshable for Segment {
(mesh, Mesh::new())
}
}
impl Meshable<SpritePipeline, SpritePipeline> for Segment {
type Pipeline = SpritePipeline;
type TranslucentPipeline = SpritePipeline;
type Supplement = Vec3<f32>;
fn generate_mesh(
&self,
offs: Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) {
let mut mesh = Mesh::new();
for pos in self.iter_positions() {
if let Some(col) = self.get(pos).ok().and_then(|vox| vox.get_color()) {
let col = col.map(|e| e as f32 / 255.0);
vol::push_vox_verts(
&mut mesh,
self,
pos,
offs + pos.map(|e| e as f32),
col,
|origin, norm, col, ao, light| {
SpriteVertex::new(
origin,
norm,
linear_to_srgb(srgb_to_linear(col) * ao * light),
)
},
true,
&[[[1.0; 3]; 3]; 3],
|vox| vox.is_empty(),
|vox| !vox.is_empty(),
);
}
}
(mesh, Mesh::new())
}
}

View File

@ -14,15 +14,19 @@ type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
fn block_shadow_density(kind: BlockKind) -> (f32, f32) {
// (density, cap)
match kind {
BlockKind::Air => (0.0, 0.0),
BlockKind::Normal => (0.085, 0.3),
BlockKind::Dense => (0.3, 0.0),
BlockKind::Water => (0.15, 0.0),
kind if kind.is_air() => (0.0, 0.0),
_ => (1.0, 0.0),
}
}
impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for VolMap2d<V, S> {
impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone>
Meshable<TerrainPipeline, FluidPipeline> for VolMap2d<V, S>
{
type Pipeline = TerrainPipeline;
type TranslucentPipeline = FluidPipeline;
type Supplement = Aabb<i32>;

View File

@ -0,0 +1,36 @@
use super::{gfx_backend, RenderError};
use gfx::{
self,
buffer::Role,
memory::{Bind, Usage},
Factory,
};
/// Represents a mesh that has been sent to the GPU.
pub struct Instances<T: Copy + gfx::traits::Pod> {
pub ibuf: gfx::handle::Buffer<gfx_backend::Resources, T>,
}
impl<T: Copy + gfx::traits::Pod> Instances<T> {
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Result<Self, RenderError> {
Ok(Self {
ibuf: factory
.create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::TRANSFER_DST)
.map_err(|err| RenderError::BufferCreationError(err))?,
})
}
pub fn count(&self) -> usize {
self.ibuf.len()
}
pub fn update(
&mut self,
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
instances: &[T],
) -> Result<(), RenderError> {
encoder
.update_buffer(&self.ibuf, instances, 0)
.map_err(|err| RenderError::UpdateError(err))
}
}

View File

@ -1,4 +1,5 @@
pub mod consts;
pub mod instances;
pub mod mesh;
pub mod model;
pub mod pipelines;
@ -9,6 +10,7 @@ mod util;
// Reexports
pub use self::{
consts::Consts,
instances::Instances,
mesh::{Mesh, Quad, Tri},
model::{DynamicModel, Model},
pipelines::{
@ -18,6 +20,7 @@ pub use self::{
create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline,
},
skybox::{create_mesh as create_skybox_mesh, Locals as SkyboxLocals, SkyboxPipeline},
sprite::{Instance as SpriteInstance, SpritePipeline},
terrain::{Locals as TerrainLocals, TerrainPipeline},
ui::{
create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals,

View File

@ -10,22 +10,20 @@ use std::ops::Range;
/// Represents a mesh that has been sent to the GPU.
pub struct Model<P: Pipeline> {
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
pub slice: gfx::Slice<gfx_backend::Resources>,
pub vertex_range: Range<u32>,
}
impl<P: Pipeline> Model<P> {
pub fn new(factory: &mut gfx_backend::Factory, mesh: &Mesh<P>) -> Self {
Self {
vbuf: factory.create_vertex_buffer(mesh.vertices()),
slice: gfx::Slice {
start: 0,
end: mesh.vertices().len() as u32,
base_vertex: 0,
instances: None,
buffer: gfx::IndexBuffer::Auto,
},
vertex_range: 0..mesh.vertices().len() as u32,
}
}
pub fn vertex_range(&self) -> Range<u32> {
self.vertex_range.clone()
}
}
/// Represents a mesh on the GPU which can be updated dynamically.
@ -46,13 +44,7 @@ impl<P: Pipeline> DynamicModel<P> {
pub fn submodel(&self, range: Range<usize>) -> Model<P> {
Model {
vbuf: self.vbuf.clone(),
slice: gfx::Slice {
start: range.start as u32,
end: range.end as u32,
base_vertex: 0,
instances: None,
buffer: gfx::IndexBuffer::Auto,
},
vertex_range: range.start as u32..range.end as u32,
}
}

View File

@ -2,6 +2,7 @@ pub mod figure;
pub mod fluid;
pub mod postprocess;
pub mod skybox;
pub mod sprite;
pub mod terrain;
pub mod ui;

View File

@ -0,0 +1,79 @@
use super::{
super::{util::arr_to_mat, Pipeline, TgtColorFmt, TgtDepthFmt},
Globals, Light,
};
use gfx::{
self,
// Macros
gfx_defines,
gfx_impl_struct_meta,
gfx_pipeline,
gfx_pipeline_inner,
gfx_vertex_struct_meta,
state::ColorMask,
};
use vek::*;
gfx_defines! {
vertex Vertex {
pos: [f32; 3] = "v_pos",
norm: [f32; 3] = "v_norm",
col: [f32; 3] = "v_col",
}
vertex Instance {
inst_mat0: [f32; 4] = "inst_mat0",
inst_mat1: [f32; 4] = "inst_mat1",
inst_mat2: [f32; 4] = "inst_mat2",
inst_mat3: [f32; 4] = "inst_mat3",
inst_col: [f32; 3] = "inst_col",
inst_wind_sway: f32 = "inst_wind_sway",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
ibuf: gfx::InstanceBuffer<Instance> = (),
globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights",
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
}
}
impl Vertex {
pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>) -> Self {
Self {
pos: pos.into_array(),
col: col.into_array(),
norm: norm.into_array(),
}
}
}
impl Instance {
pub fn new(mat: Mat4<f32>, col: Rgb<f32>, wind_sway: f32) -> Self {
let mat_arr = arr_to_mat(mat.into_col_array());
Self {
inst_mat0: mat_arr[0],
inst_mat1: mat_arr[1],
inst_mat2: mat_arr[2],
inst_mat3: mat_arr[3],
inst_col: col.into_array(),
inst_wind_sway: wind_sway,
}
}
}
impl Default for Instance {
fn default() -> Self {
Self::new(Mat4::identity(), Rgb::broadcast(1.0), 0.0)
}
}
pub struct SpritePipeline;
impl Pipeline for SpritePipeline {
type Vertex = Vertex;
}

View File

@ -1,9 +1,10 @@
use super::{
consts::Consts,
gfx_backend,
instances::Instances,
mesh::Mesh,
model::{DynamicModel, Model},
pipelines::{figure, fluid, postprocess, skybox, terrain, ui, Globals, Light},
pipelines::{figure, fluid, postprocess, skybox, sprite, terrain, ui, Globals, Light},
texture::Texture,
Pipeline, RenderError,
};
@ -65,6 +66,7 @@ pub struct Renderer {
figure_pipeline: GfxPipeline<figure::pipe::Init<'static>>,
terrain_pipeline: GfxPipeline<terrain::pipe::Init<'static>>,
fluid_pipeline: GfxPipeline<fluid::pipe::Init<'static>>,
sprite_pipeline: GfxPipeline<sprite::pipe::Init<'static>>,
ui_pipeline: GfxPipeline<ui::pipe::Init<'static>>,
postprocess_pipeline: GfxPipeline<postprocess::pipe::Init<'static>>,
@ -86,6 +88,7 @@ impl Renderer {
figure_pipeline,
terrain_pipeline,
fluid_pipeline,
sprite_pipeline,
ui_pipeline,
postprocess_pipeline,
) = create_pipelines(&mut factory, &mut shader_reload_indicator)?;
@ -114,6 +117,7 @@ impl Renderer {
figure_pipeline,
terrain_pipeline,
fluid_pipeline,
sprite_pipeline,
ui_pipeline,
postprocess_pipeline,
@ -201,6 +205,7 @@ impl Renderer {
figure_pipeline,
terrain_pipeline,
fluid_pipeline,
sprite_pipeline,
ui_pipeline,
postprocess_pipeline,
)) => {
@ -208,6 +213,7 @@ impl Renderer {
self.figure_pipeline = figure_pipeline;
self.terrain_pipeline = terrain_pipeline;
self.fluid_pipeline = fluid_pipeline;
self.sprite_pipeline = sprite_pipeline;
self.ui_pipeline = ui_pipeline;
self.postprocess_pipeline = postprocess_pipeline;
}
@ -238,6 +244,16 @@ impl Renderer {
consts.update(&mut self.encoder, vals)
}
/// Create a new set of instances with the provided values.
pub fn create_instances<T: Copy + gfx::traits::Pod>(
&mut self,
vals: &[T],
) -> Result<Instances<T>, RenderError> {
let mut instances = Instances::new(&mut self.factory, vals.len())?;
instances.update(&mut self.encoder, vals)?;
Ok(instances)
}
/// Create a new model from the provided mesh.
pub fn create_model<P: Pipeline>(&mut self, mesh: &Mesh<P>) -> Result<Model<P>, RenderError> {
Ok(Model::new(&mut self.factory, mesh))
@ -350,7 +366,13 @@ impl Renderer {
locals: &Consts<skybox::Locals>,
) {
self.encoder.draw(
&model.slice,
&gfx::Slice {
start: model.vertex_range().start,
end: model.vertex_range().end,
base_vertex: 0,
instances: None,
buffer: gfx::IndexBuffer::Auto,
},
&self.skybox_pipeline.pso,
&skybox::pipe::Data {
vbuf: model.vbuf.clone(),
@ -372,7 +394,13 @@ impl Renderer {
lights: &Consts<Light>,
) {
self.encoder.draw(
&model.slice,
&gfx::Slice {
start: model.vertex_range().start,
end: model.vertex_range().end,
base_vertex: 0,
instances: None,
buffer: gfx::IndexBuffer::Auto,
},
&self.figure_pipeline.pso,
&figure::pipe::Data {
vbuf: model.vbuf.clone(),
@ -395,7 +423,13 @@ impl Renderer {
lights: &Consts<Light>,
) {
self.encoder.draw(
&model.slice,
&gfx::Slice {
start: model.vertex_range().start,
end: model.vertex_range().end,
base_vertex: 0,
instances: None,
buffer: gfx::IndexBuffer::Auto,
},
&self.terrain_pipeline.pso,
&terrain::pipe::Data {
vbuf: model.vbuf.clone(),
@ -417,7 +451,13 @@ impl Renderer {
lights: &Consts<Light>,
) {
self.encoder.draw(
&model.slice,
&gfx::Slice {
start: model.vertex_range().start,
end: model.vertex_range().end,
base_vertex: 0,
instances: None,
buffer: gfx::IndexBuffer::Auto,
},
&self.fluid_pipeline.pso,
&fluid::pipe::Data {
vbuf: model.vbuf.clone(),
@ -430,6 +470,34 @@ impl Renderer {
);
}
/// Queue the rendering of the provided terrain chunk model in the upcoming frame.
pub fn render_sprites(
&mut self,
model: &Model<sprite::SpritePipeline>,
globals: &Consts<Globals>,
instances: &Instances<sprite::Instance>,
lights: &Consts<Light>,
) {
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.sprite_pipeline.pso,
&sprite::pipe::Data {
vbuf: model.vbuf.clone(),
ibuf: instances.ibuf.clone(),
globals: globals.buf.clone(),
lights: lights.buf.clone(),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
},
);
}
/// Queue the rendering of the provided UI element in the upcoming frame.
pub fn render_ui_element(
&mut self,
@ -441,7 +509,13 @@ impl Renderer {
) {
let Aabr { min, max } = scissor;
self.encoder.draw(
&model.slice,
&gfx::Slice {
start: model.vertex_range().start,
end: model.vertex_range().end,
base_vertex: 0,
instances: None,
buffer: gfx::IndexBuffer::Auto,
},
&self.ui_pipeline.pso,
&ui::pipe::Data {
vbuf: model.vbuf.clone(),
@ -467,7 +541,13 @@ impl Renderer {
locals: &Consts<postprocess::Locals>,
) {
self.encoder.draw(
&model.slice,
&gfx::Slice {
start: model.vertex_range().start,
end: model.vertex_range().end,
base_vertex: 0,
instances: None,
buffer: gfx::IndexBuffer::Auto,
},
&self.postprocess_pipeline.pso,
&postprocess::pipe::Data {
vbuf: model.vbuf.clone(),
@ -495,6 +575,7 @@ fn create_pipelines(
GfxPipeline<figure::pipe::Init<'static>>,
GfxPipeline<terrain::pipe::Init<'static>>,
GfxPipeline<fluid::pipe::Init<'static>>,
GfxPipeline<sprite::pipe::Init<'static>>,
GfxPipeline<ui::pipe::Init<'static>>,
GfxPipeline<postprocess::pipe::Init<'static>>,
),
@ -571,6 +652,18 @@ fn create_pipelines(
gfx::state::CullFace::Nothing,
)?;
// Construct a pipeline for rendering sprites
let sprite_pipeline = create_pipeline(
factory,
sprite::pipe::new(),
&assets::load_watched::<String>("voxygen.shaders.sprite-vert", shader_reload_indicator)
.unwrap(),
&assets::load_watched::<String>("voxygen.shaders.sprite-frag", shader_reload_indicator)
.unwrap(),
&include_ctx,
gfx::state::CullFace::Back,
)?;
// Construct a pipeline for rendering UI elements
let ui_pipeline = create_pipeline(
factory,
@ -606,6 +699,7 @@ fn create_pipelines(
figure_pipeline,
terrain_pipeline,
fluid_pipeline,
sprite_pipeline,
ui_pipeline,
postprocess_pipeline,
))

View File

@ -163,8 +163,10 @@ impl FigureModelCache {
// TODO: Don't make this public.
pub fn load_mesh(mesh_name: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
Segment::from(assets::load_expect::<DotVoxData>(full_specifier.as_str()).as_ref())
.generate_mesh(position)
Meshable::<FigurePipeline, FigurePipeline>::generate_mesh(
&Segment::from(assets::load_expect::<DotVoxData>(full_specifier.as_str()).as_ref()),
position,
)
.0
}

View File

@ -68,7 +68,7 @@ impl Scene {
.create_consts(&[PostProcessLocals::default()])
.unwrap(),
},
terrain: Terrain::new(),
terrain: Terrain::new(renderer),
loaded_distance: 0.0,
figure_mgr: FigureMgr::new(),
}
@ -229,7 +229,12 @@ impl Scene {
// Render terrain and figures.
self.figure_mgr
.render(renderer, client, &self.globals, &self.lights, &self.camera);
self.terrain.render(renderer, &self.globals, &self.lights);
self.terrain.render(
renderer,
&self.globals,
&self.lights,
self.camera.get_focus_pos(),
);
renderer.render_post_process(
&self.postprocess.model,

View File

@ -1,27 +1,32 @@
use crate::{
mesh::Meshable,
render::{
Consts, FluidPipeline, Globals, Light, Mesh, Model, Renderer, TerrainLocals,
TerrainPipeline,
Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, SpriteInstance,
SpritePipeline, TerrainLocals, TerrainPipeline,
},
};
use client::Client;
use common::{
terrain::{TerrainChunkSize, TerrainMap},
vol::{SampleVol, VolSize},
assets,
figure::Segment,
terrain::{Block, BlockKind, TerrainChunkSize, TerrainMap},
vol::{ReadVol, SampleVol, VolSize, Vox},
volumes::vol_map_2d::VolMap2dErr,
};
use crossbeam::channel;
use dot_vox::DotVoxData;
use frustum_query::frustum::Frustum;
use hashbrown::HashMap;
use std::{i32, ops::Mul, time::Duration};
use std::{f32, i32, ops::Mul, time::Duration};
use vek::*;
struct TerrainChunk {
// GPU data
opaque_model: Model<TerrainPipeline>,
fluid_model: Model<FluidPipeline>,
sprite_instances: HashMap<(BlockKind, usize), Instances<SpriteInstance>>,
locals: Consts<TerrainLocals>,
visible: bool,
z_bounds: (f32, f32),
}
@ -38,9 +43,72 @@ struct MeshWorkerResponse {
z_bounds: (f32, f32),
opaque_mesh: Mesh<TerrainPipeline>,
fluid_mesh: Mesh<FluidPipeline>,
sprite_instances: HashMap<(BlockKind, usize), Vec<SpriteInstance>>,
started_tick: u64,
}
struct SpriteConfig {
variations: usize,
wind_sway: f32, // 1.0 is normal
}
fn sprite_config_for(kind: BlockKind) -> Option<SpriteConfig> {
match kind {
BlockKind::LargeCactus => Some(SpriteConfig {
variations: 1,
wind_sway: 0.0,
}),
BlockKind::BarrelCactus => Some(SpriteConfig {
variations: 1,
wind_sway: 0.0,
}),
BlockKind::BlueFlower => Some(SpriteConfig {
variations: 2,
wind_sway: 0.3,
}),
BlockKind::PinkFlower => Some(SpriteConfig {
variations: 3,
wind_sway: 0.3,
}),
BlockKind::RedFlower => Some(SpriteConfig {
variations: 1,
wind_sway: 0.3,
}),
BlockKind::WhiteFlower => Some(SpriteConfig {
variations: 1,
wind_sway: 0.3,
}),
BlockKind::YellowFlower => Some(SpriteConfig {
variations: 1,
wind_sway: 0.3,
}),
BlockKind::Sunflower => Some(SpriteConfig {
variations: 2,
wind_sway: 0.3,
}),
BlockKind::LongGrass => Some(SpriteConfig {
variations: 5,
wind_sway: 1.0,
}),
BlockKind::MediumGrass => Some(SpriteConfig {
variations: 5,
wind_sway: 1.0,
}),
BlockKind::ShortGrass => Some(SpriteConfig {
variations: 5,
wind_sway: 1.0,
}),
BlockKind::Apple => Some(SpriteConfig {
variations: 1,
wind_sway: 0.0,
}),
_ => None,
}
}
/// Function executed by worker threads dedicated to chunk meshing.
fn mesh_worker(
pos: Vec2<i32>,
@ -55,6 +123,43 @@ fn mesh_worker(
z_bounds,
opaque_mesh,
fluid_mesh,
// Extract sprite locations from volume
sprite_instances: {
let mut instances = HashMap::new();
for x in 0..TerrainChunkSize::SIZE.x as i32 {
for y in 0..TerrainChunkSize::SIZE.y as i32 {
for z in z_bounds.0 as i32..z_bounds.1 as i32 + 1 {
let wpos = Vec3::from(
pos * Vec2::from(TerrainChunkSize::SIZE).map(|e: u32| e as i32),
) + Vec3::new(x, y, z);
let kind = volume.get(wpos).unwrap_or(&Block::empty()).kind();
if let Some(cfg) = sprite_config_for(kind) {
let seed = wpos.x * 3 + wpos.y * 7 + wpos.z * 13 + wpos.x * wpos.y;
let instance = SpriteInstance::new(
Mat4::identity()
.rotated_z(f32::consts::PI * 0.5 * (seed % 4) as f32)
.translated_3d(
wpos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0),
),
Rgb::broadcast(1.0),
cfg.wind_sway,
);
instances
.entry((kind, seed as usize % cfg.variations))
.or_insert_with(|| Vec::new())
.push(instance);
}
}
}
}
instances
},
started_tick,
}
}
@ -67,19 +172,158 @@ pub struct Terrain {
mesh_send_tmp: channel::Sender<MeshWorkerResponse>,
mesh_recv: channel::Receiver<MeshWorkerResponse>,
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
// GPU data
sprite_models: HashMap<(BlockKind, usize), Model<SpritePipeline>>,
}
impl Terrain {
pub fn new() -> Self {
pub fn new(renderer: &mut Renderer) -> Self {
// Create a new mpsc (Multiple Produced, Single Consumer) pair for communicating with
// worker threads that are meshing chunks.
let (send, recv) = channel::unbounded();
let mut make_model = |s| {
renderer
.create_model(
&Meshable::<SpritePipeline, SpritePipeline>::generate_mesh(
&Segment::from(assets::load_expect::<DotVoxData>(s).as_ref()),
Vec3::new(-6.0, -6.0, 0.0),
)
.0,
)
.unwrap()
};
Self {
chunks: HashMap::default(),
mesh_send_tmp: send,
mesh_recv: recv,
mesh_todo: HashMap::default(),
sprite_models: vec![
// Cacti
(
(BlockKind::LargeCactus, 0),
make_model("voxygen.voxel.sprite.cacti.large_cactus"),
),
(
(BlockKind::BarrelCactus, 0),
make_model("voxygen.voxel.sprite.cacti.barrel_cactus"),
),
// Fruit
(
(BlockKind::Apple, 0),
make_model("voxygen.voxel.sprite.fruit.apple"),
),
// Flowers
(
(BlockKind::BlueFlower, 0),
make_model("voxygen.voxel.sprite.flowers.flower_blue_1"),
),
(
(BlockKind::BlueFlower, 1),
make_model("voxygen.voxel.sprite.flowers.flower_blue_2"),
),
(
(BlockKind::PinkFlower, 0),
make_model("voxygen.voxel.sprite.flowers.flower_pink_1"),
),
(
(BlockKind::PinkFlower, 1),
make_model("voxygen.voxel.sprite.flowers.flower_pink_2"),
),
(
(BlockKind::PinkFlower, 2),
make_model("voxygen.voxel.sprite.flowers.flower_pink_3"),
),
(
(BlockKind::PurpleFlower, 0),
make_model("voxygen.voxel.sprite.flowers.flower_purple_1"),
),
(
(BlockKind::RedFlower, 0),
make_model("voxygen.voxel.sprite.flowers.flower_red_1"),
),
(
(BlockKind::WhiteFlower, 0),
make_model("voxygen.voxel.sprite.flowers.flower_white_1"),
),
(
(BlockKind::YellowFlower, 0),
make_model("voxygen.voxel.sprite.flowers.flower_purple_1"),
),
(
(BlockKind::Sunflower, 0),
make_model("voxygen.voxel.sprite.flowers.sunflower_1"),
),
(
(BlockKind::Sunflower, 1),
make_model("voxygen.voxel.sprite.flowers.sunflower_2"),
),
// Grass
(
(BlockKind::LongGrass, 0),
make_model("voxygen.voxel.sprite.grass.grass_long_1"),
),
(
(BlockKind::LongGrass, 1),
make_model("voxygen.voxel.sprite.grass.grass_long_2"),
),
(
(BlockKind::LongGrass, 2),
make_model("voxygen.voxel.sprite.grass.grass_long_3"),
),
(
(BlockKind::LongGrass, 3),
make_model("voxygen.voxel.sprite.grass.grass_long_4"),
),
(
(BlockKind::LongGrass, 4),
make_model("voxygen.voxel.sprite.grass.grass_long_5"),
),
(
(BlockKind::MediumGrass, 0),
make_model("voxygen.voxel.sprite.grass.grass_med_1"),
),
(
(BlockKind::MediumGrass, 1),
make_model("voxygen.voxel.sprite.grass.grass_med_2"),
),
(
(BlockKind::MediumGrass, 2),
make_model("voxygen.voxel.sprite.grass.grass_med_3"),
),
(
(BlockKind::MediumGrass, 3),
make_model("voxygen.voxel.sprite.grass.grass_med_4"),
),
(
(BlockKind::MediumGrass, 4),
make_model("voxygen.voxel.sprite.grass.grass_med_5"),
),
(
(BlockKind::ShortGrass, 0),
make_model("voxygen.voxel.sprite.grass.grass_short_1"),
),
(
(BlockKind::ShortGrass, 1),
make_model("voxygen.voxel.sprite.grass.grass_short_2"),
),
(
(BlockKind::ShortGrass, 2),
make_model("voxygen.voxel.sprite.grass.grass_short_3"),
),
(
(BlockKind::ShortGrass, 3),
make_model("voxygen.voxel.sprite.grass.grass_short_3"),
),
(
(BlockKind::ShortGrass, 4),
make_model("voxygen.voxel.sprite.grass.grass_short_5"),
),
]
.into_iter()
.collect(),
}
}
@ -275,6 +519,18 @@ impl Terrain {
fluid_model: renderer
.create_model(&response.fluid_mesh)
.expect("Failed to upload chunk mesh to the GPU!"),
sprite_instances: response
.sprite_instances
.into_iter()
.map(|(kind, instances)| {
(
kind,
renderer.create_instances(&instances).expect(
"Failed to upload chunk sprite instances to the GPU!",
),
)
})
.collect(),
locals: renderer
.create_consts(&[TerrainLocals {
model_offs: Vec3::from(
@ -343,18 +599,37 @@ impl Terrain {
renderer: &mut Renderer,
globals: &Consts<Globals>,
lights: &Consts<Light>,
focus_pos: Vec3<f32>,
) {
// Opaque
for (_pos, chunk) in &self.chunks {
for (_, chunk) in &self.chunks {
if chunk.visible {
renderer.render_terrain_chunk(&chunk.opaque_model, globals, &chunk.locals, lights);
}
}
// Translucent
for (_pos, chunk) in &self.chunks {
for (pos, chunk) in &self.chunks {
if chunk.visible {
renderer.render_fluid_chunk(&chunk.fluid_model, globals, &chunk.locals, lights);
const SPRITE_RENDER_DISTANCE: f32 = 128.0;
let chunk_center = pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
(e as f32 + 0.5) * sz as f32
});
if Vec2::from(focus_pos).distance_squared(chunk_center)
< SPRITE_RENDER_DISTANCE * SPRITE_RENDER_DISTANCE
{
for (kind, instances) in &chunk.sprite_instances {
renderer.render_sprites(
&self.sprite_models[&kind],
globals,
&instances,
lights,
);
}
}
}
}
}

View File

@ -148,11 +148,13 @@ impl<'a> BlockGen<'a> {
//close_structures,
cave_xy,
cave_alt,
marble,
marble_small,
rock,
//cliffs,
cliff_hill,
close_cliffs,
//temp,
temp,
..
} = &z_cache?.sample;
@ -254,6 +256,49 @@ impl<'a> BlockGen<'a> {
BlockKind::Normal,
saturate_srgb(col, 0.45).map(|e| (e * 255.0) as u8),
))
} else if (wposf.z as f32) < height + 0.9
&& temp < CONFIG.desert_temp
&& (wposf.z as f32 > water_height + 3.0)
&& marble > 0.68
&& marble_small > 0.65
&& (marble * 3173.7).fract() < 0.5
{
let flowers = [
BlockKind::BlueFlower,
BlockKind::PinkFlower,
BlockKind::PurpleFlower,
BlockKind::RedFlower,
BlockKind::WhiteFlower,
BlockKind::YellowFlower,
BlockKind::Sunflower,
];
let grasses = [
BlockKind::LongGrass,
BlockKind::MediumGrass,
BlockKind::ShortGrass,
];
Some(Block::new(
if (height * 1271.0).fract() < 0.15 {
flowers[(height * 0.2) as usize % flowers.len()]
} else {
grasses[(height * 0.3) as usize % grasses.len()]
},
Rgb::broadcast(0),
))
} else if (wposf.z as f32) < height + 0.9
&& temp > CONFIG.desert_temp
&& (marble * 4423.5).fract() < 0.0005
{
Some(Block::new(
if (height * 1271.0).fract() < 0.5 {
BlockKind::LargeCactus
} else {
BlockKind::BarrelCactus
},
Rgb::broadcast(0),
))
} else {
None
};
@ -504,11 +549,7 @@ fn block_from_structure(
)
.map(|e| e as u8),
)),
StructureBlock::Fruit => Some(Block::new(
BlockKind::Normal,
Lerp::lerp(Rgb::new(237.0, 0.0, 0.0), Rgb::new(200.0, 237.0, 0.0), lerp)
.map(|e| e as u8),
)),
StructureBlock::Fruit => Some(Block::new(BlockKind::Apple, Rgb::new(194, 30, 37))),
StructureBlock::Hollow => Some(Block::empty()),
StructureBlock::Normal(color) => {
Some(Block::new(default_kind, color)).filter(|block| !block.is_empty())

View File

@ -366,11 +366,11 @@ lazy_static! {
pub static ref FRUIT_TREES: Vec<Arc<Structure>> = vec![
// fruit trees
st_asset("world.tree.fruit.1", (5, 5, 7)),
st_asset("world.tree.fruit.2", (5, 5, 7)),
st_asset("world.tree.fruit.3", (5, 5, 7)),
st_asset("world.tree.fruit.4", (5, 5, 7)),
st_asset("world.tree.fruit.5", (5, 5, 7)),
st_asset("world.tree.fruit.6", (5, 5, 7)),
st_asset("world.tree.fruit.2", (6, 6, 7)),
st_asset("world.tree.fruit.3", (6, 7, 7)),
st_asset("world.tree.fruit.4", (3, 3, 7)),
st_asset("world.tree.fruit.5", (6, 8, 7)),
st_asset("world.tree.fruit.6", (7, 7, 7)),
];
/*

View File

@ -359,6 +359,8 @@ impl<'a> Sampler for ColumnGen<'a> {
close_structures: self.gen_close_structures(wpos),
cave_xy,
cave_alt,
marble,
marble_small,
rock,
is_cliffs,
near_cliffs,
@ -384,6 +386,8 @@ pub struct ColumnSample<'a> {
pub close_structures: [Option<StructureData>; 9],
pub cave_xy: f32,
pub cave_alt: f32,
pub marble: f32,
pub marble_small: f32,
pub rock: f32,
pub is_cliffs: bool,
pub near_cliffs: bool,