mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-08 03:29:48 -04:00
Particles (#1367)
https://github.com/user-attachments/assets/de82e298-acfb-431d-973a-2d37b9a707ae resolves #293 --------- Co-authored-by: Krzysztof Wiśniewski <argmaster.world@gmail.com> Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> Co-authored-by: Krzysztof Wiśniewski <Argmaster@users.noreply.github.com>
This commit is contained in:
parent
14c7fafce9
commit
de304e49d3
3
assets/cubyz/particles/poof.zig.zon
Normal file
3
assets/cubyz/particles/poof.zig.zon
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.{
|
||||||
|
.texture = "cubyz:poof",
|
||||||
|
}
|
BIN
assets/cubyz/particles/textures/poof.png
Normal file
BIN
assets/cubyz/particles/textures/poof.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 132 B |
17
assets/cubyz/shaders/particles/particles.frag
Normal file
17
assets/cubyz/shaders/particles/particles.frag
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#version 430
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 textureCoords;
|
||||||
|
layout(location = 1) flat in vec3 light;
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2DArray textureSampler;
|
||||||
|
layout(binding = 1) uniform sampler2DArray emissionTextureSampler;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
const vec4 texColor = texture(textureSampler, textureCoords);
|
||||||
|
if(texColor.a != 1) discard;
|
||||||
|
|
||||||
|
const vec3 pixelLight = max(light, texture(emissionTextureSampler, textureCoords).r*4);
|
||||||
|
fragColor = texColor*vec4(pixelLight, 1);
|
||||||
|
}
|
82
assets/cubyz/shaders/particles/particles.vert
Normal file
82
assets/cubyz/shaders/particles/particles.vert
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout(location = 0) out vec3 textureCoords;
|
||||||
|
layout(location = 1) flat out vec3 light;
|
||||||
|
|
||||||
|
layout(location = 0) uniform vec3 ambientLight;
|
||||||
|
layout(location = 1) uniform mat4 projectionAndViewMatrix;
|
||||||
|
layout(location = 2) uniform mat4 billboardMatrix;
|
||||||
|
|
||||||
|
struct ParticleData {
|
||||||
|
vec3 pos;
|
||||||
|
float rotation;
|
||||||
|
float lifeRatio;
|
||||||
|
uint light;
|
||||||
|
uint type;
|
||||||
|
};
|
||||||
|
layout(std430, binding = 13) restrict readonly buffer _particleData
|
||||||
|
{
|
||||||
|
ParticleData particleData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParticleTypeData {
|
||||||
|
float animationFrames;
|
||||||
|
float startFrame;
|
||||||
|
float size;
|
||||||
|
};
|
||||||
|
layout(std430, binding = 14) restrict readonly buffer _particleTypeData
|
||||||
|
{
|
||||||
|
ParticleTypeData particleTypeData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const vec2 uvPositions[4] = vec2[4]
|
||||||
|
(
|
||||||
|
vec2(0.0f, 0.0f),
|
||||||
|
vec2(0.0f, 1.0f),
|
||||||
|
vec2(1.0f, 0.0f),
|
||||||
|
vec2(1.0f, 1.0f)
|
||||||
|
);
|
||||||
|
|
||||||
|
const vec3 facePositions[4] = vec3[4]
|
||||||
|
(
|
||||||
|
vec3(-0.5f, -0.5f, 0.0f),
|
||||||
|
vec3(-0.5f, 0.5f, 0.0f),
|
||||||
|
vec3(0.5f, -0.5f, 0.0f),
|
||||||
|
vec3(0.5f, 0.5f, 0.0f)
|
||||||
|
);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
int particleID = gl_VertexID >> 2;
|
||||||
|
int vertexID = gl_VertexID & 3;
|
||||||
|
ParticleData particle = particleData[particleID];
|
||||||
|
ParticleTypeData particleType = particleTypeData[particle.type];
|
||||||
|
|
||||||
|
uint fullLight = particle.light;
|
||||||
|
vec3 sunLight = vec3(
|
||||||
|
fullLight >> 25 & 31u,
|
||||||
|
fullLight >> 20 & 31u,
|
||||||
|
fullLight >> 15 & 31u
|
||||||
|
);
|
||||||
|
vec3 blockLight = vec3(
|
||||||
|
fullLight >> 10 & 31u,
|
||||||
|
fullLight >> 5 & 31u,
|
||||||
|
fullLight >> 0 & 31u
|
||||||
|
);
|
||||||
|
light = max(sunLight*ambientLight, blockLight)/31;
|
||||||
|
|
||||||
|
float rotation = particle.rotation;
|
||||||
|
vec3 faceVertPos = facePositions[vertexID];
|
||||||
|
float sn = sin(rotation);
|
||||||
|
float cs = cos(rotation);
|
||||||
|
const vec3 vertexRotationPos = vec3(
|
||||||
|
faceVertPos.x*cs - faceVertPos.y*sn,
|
||||||
|
faceVertPos.x*sn + faceVertPos.y*cs,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
const vec3 vertexPos = (billboardMatrix*vec4(particleType.size*vertexRotationPos, 1)).xyz + particle.pos;
|
||||||
|
gl_Position = projectionAndViewMatrix*vec4(vertexPos, 1);
|
||||||
|
|
||||||
|
float textureIndex = floor(particle.lifeRatio*particleType.animationFrames + particleType.startFrame);
|
||||||
|
textureCoords = vec3(uvPositions[vertexID], textureIndex);
|
||||||
|
}
|
@ -5,6 +5,7 @@ const items_zig = @import("items.zig");
|
|||||||
const migrations_zig = @import("migrations.zig");
|
const migrations_zig = @import("migrations.zig");
|
||||||
const blueprints_zig = @import("blueprint.zig");
|
const blueprints_zig = @import("blueprint.zig");
|
||||||
const Blueprint = blueprints_zig.Blueprint;
|
const Blueprint = blueprints_zig.Blueprint;
|
||||||
|
const particles_zig = @import("particles.zig");
|
||||||
const ZonElement = @import("zon.zig").ZonElement;
|
const ZonElement = @import("zon.zig").ZonElement;
|
||||||
const main = @import("main");
|
const main = @import("main");
|
||||||
const biomes_zig = main.server.terrain.biomes;
|
const biomes_zig = main.server.terrain.biomes;
|
||||||
@ -33,6 +34,7 @@ pub const Assets = struct {
|
|||||||
models: BytesHashMap,
|
models: BytesHashMap,
|
||||||
structureBuildingBlocks: ZonHashMap,
|
structureBuildingBlocks: ZonHashMap,
|
||||||
blueprints: BytesHashMap,
|
blueprints: BytesHashMap,
|
||||||
|
particles: ZonHashMap,
|
||||||
|
|
||||||
fn init() Assets {
|
fn init() Assets {
|
||||||
return .{
|
return .{
|
||||||
@ -46,6 +48,7 @@ pub const Assets = struct {
|
|||||||
.models = .{},
|
.models = .{},
|
||||||
.structureBuildingBlocks = .{},
|
.structureBuildingBlocks = .{},
|
||||||
.blueprints = .{},
|
.blueprints = .{},
|
||||||
|
.particles = .{},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
fn deinit(self: *Assets, allocator: NeverFailingAllocator) void {
|
fn deinit(self: *Assets, allocator: NeverFailingAllocator) void {
|
||||||
@ -59,6 +62,7 @@ pub const Assets = struct {
|
|||||||
self.models.deinit(allocator.allocator);
|
self.models.deinit(allocator.allocator);
|
||||||
self.structureBuildingBlocks.deinit(allocator.allocator);
|
self.structureBuildingBlocks.deinit(allocator.allocator);
|
||||||
self.blueprints.deinit(allocator.allocator);
|
self.blueprints.deinit(allocator.allocator);
|
||||||
|
self.particles.deinit(allocator.allocator);
|
||||||
}
|
}
|
||||||
fn clone(self: Assets, allocator: NeverFailingAllocator) Assets {
|
fn clone(self: Assets, allocator: NeverFailingAllocator) Assets {
|
||||||
return .{
|
return .{
|
||||||
@ -72,6 +76,7 @@ pub const Assets = struct {
|
|||||||
.models = self.models.clone(allocator.allocator) catch unreachable,
|
.models = self.models.clone(allocator.allocator) catch unreachable,
|
||||||
.structureBuildingBlocks = self.structureBuildingBlocks.clone(allocator.allocator) catch unreachable,
|
.structureBuildingBlocks = self.structureBuildingBlocks.clone(allocator.allocator) catch unreachable,
|
||||||
.blueprints = self.blueprints.clone(allocator.allocator) catch unreachable,
|
.blueprints = self.blueprints.clone(allocator.allocator) catch unreachable,
|
||||||
|
.particles = self.particles.clone(allocator.allocator) catch unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
fn read(self: *Assets, allocator: NeverFailingAllocator, assetPath: []const u8) void {
|
fn read(self: *Assets, allocator: NeverFailingAllocator, assetPath: []const u8) void {
|
||||||
@ -88,12 +93,13 @@ pub const Assets = struct {
|
|||||||
addon.readAllZon(allocator, "sbb", true, &self.structureBuildingBlocks, null);
|
addon.readAllZon(allocator, "sbb", true, &self.structureBuildingBlocks, null);
|
||||||
addon.readAllBlueprints(allocator, "sbb", &self.blueprints);
|
addon.readAllBlueprints(allocator, "sbb", &self.blueprints);
|
||||||
addon.readAllModels(allocator, &self.models);
|
addon.readAllModels(allocator, &self.models);
|
||||||
|
addon.readAllZon(allocator, "particles", true, &self.particles, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn log(self: *Assets, typ: enum {common, world}) void {
|
fn log(self: *Assets, typ: enum {common, world}) void {
|
||||||
std.log.info(
|
std.log.info(
|
||||||
"Finished {s} assets reading with {} blocks ({} migrations), {} items, {} tools, {} biomes ({} migrations), {} recipes, {} structure building blocks and {} blueprints",
|
"Finished {s} assets reading with {} blocks ({} migrations), {} items, {} tools, {} biomes ({} migrations), {} recipes, {} structure building blocks, {} blueprints and {} particles",
|
||||||
.{@tagName(typ), self.blocks.count(), self.blockMigrations.count(), self.items.count(), self.tools.count(), self.biomes.count(), self.biomeMigrations.count(), self.recipes.count(), self.structureBuildingBlocks.count(), self.blueprints.count()},
|
.{@tagName(typ), self.blocks.count(), self.blockMigrations.count(), self.items.count(), self.tools.count(), self.biomes.count(), self.biomeMigrations.count(), self.recipes.count(), self.structureBuildingBlocks.count(), self.blueprints.count(), self.particles.count()},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,6 +604,11 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale
|
|||||||
try sbb.registerBlueprints(&worldAssets.blueprints);
|
try sbb.registerBlueprints(&worldAssets.blueprints);
|
||||||
try sbb.registerSBB(&worldAssets.structureBuildingBlocks);
|
try sbb.registerSBB(&worldAssets.structureBuildingBlocks);
|
||||||
|
|
||||||
|
iterator = worldAssets.particles.iterator();
|
||||||
|
while(iterator.next()) |entry| {
|
||||||
|
particles_zig.ParticleManager.register(assetFolder, entry.key_ptr.*, entry.value_ptr.*);
|
||||||
|
}
|
||||||
|
|
||||||
// Biomes:
|
// Biomes:
|
||||||
var nextBiomeNumericId: u32 = 0;
|
var nextBiomeNumericId: u32 = 0;
|
||||||
for(biomePalette.palette.items) |id| {
|
for(biomePalette.palette.items) |id| {
|
||||||
|
@ -11,6 +11,7 @@ const ZonElement = @import("zon.zig").ZonElement;
|
|||||||
const main = @import("main");
|
const main = @import("main");
|
||||||
const KeyBoard = main.KeyBoard;
|
const KeyBoard = main.KeyBoard;
|
||||||
const network = @import("network.zig");
|
const network = @import("network.zig");
|
||||||
|
const particles = @import("particles.zig");
|
||||||
const Connection = network.Connection;
|
const Connection = network.Connection;
|
||||||
const ConnectionManager = network.ConnectionManager;
|
const ConnectionManager = network.ConnectionManager;
|
||||||
const vec = @import("vec.zig");
|
const vec = @import("vec.zig");
|
||||||
@ -674,6 +675,7 @@ pub const World = struct { // MARK: World
|
|||||||
main.Window.setMouseGrabbed(true);
|
main.Window.setMouseGrabbed(true);
|
||||||
|
|
||||||
main.blocks.meshes.generateTextureArray();
|
main.blocks.meshes.generateTextureArray();
|
||||||
|
main.particles.ParticleManager.generateTextureArray();
|
||||||
main.models.uploadModels();
|
main.models.uploadModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1219,4 +1221,5 @@ pub fn update(deltaTime: f64) void { // MARK: update()
|
|||||||
fog.fogHigher = (biome.fogHigher - fog.fogHigher)*t + fog.fogHigher;
|
fog.fogHigher = (biome.fogHigher - fog.fogHigher)*t + fog.fogHigher;
|
||||||
|
|
||||||
world.?.update();
|
world.?.update();
|
||||||
|
particles.ParticleSystem.update(@floatCast(deltaTime));
|
||||||
}
|
}
|
||||||
|
@ -1829,9 +1829,15 @@ pub const SSBO = struct { // MARK: SSBO
|
|||||||
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
|
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createDynamicBuffer(self: SSBO, size: usize) void {
|
pub fn bufferSubData(self: SSBO, comptime T: type, data: []const T, length: usize) void {
|
||||||
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, self.bufferID);
|
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, self.bufferID);
|
||||||
c.glBufferData(c.GL_SHADER_STORAGE_BUFFER, @intCast(size), null, c.GL_DYNAMIC_DRAW);
|
c.glBufferSubData(c.GL_SHADER_STORAGE_BUFFER, 0, @intCast(length*@sizeOf(T)), data.ptr);
|
||||||
|
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createDynamicBuffer(self: SSBO, comptime T: type, size: usize) void {
|
||||||
|
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, self.bufferID);
|
||||||
|
c.glBufferData(c.GL_SHADER_STORAGE_BUFFER, @intCast(size*@sizeOf(T)), null, c.GL_DYNAMIC_DRAW);
|
||||||
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
|
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2159,8 +2165,8 @@ pub const TextureArray = struct { // MARK: TextureArray
|
|||||||
|
|
||||||
/// (Re-)Generates the GPU buffer.
|
/// (Re-)Generates the GPU buffer.
|
||||||
pub fn generate(self: TextureArray, images: []Image, mipmapping: bool, alphaCorrectMipmapping: bool) void {
|
pub fn generate(self: TextureArray, images: []Image, mipmapping: bool, alphaCorrectMipmapping: bool) void {
|
||||||
var maxWidth: u31 = 0;
|
var maxWidth: u31 = 1;
|
||||||
var maxHeight: u31 = 0;
|
var maxHeight: u31 = 1;
|
||||||
for(images) |image| {
|
for(images) |image| {
|
||||||
maxWidth = @max(maxWidth, image.width);
|
maxWidth = @max(maxWidth, image.width);
|
||||||
maxHeight = @max(maxHeight, image.height);
|
maxHeight = @max(maxHeight, image.height);
|
||||||
|
@ -124,5 +124,7 @@ pub fn render() void {
|
|||||||
}
|
}
|
||||||
draw.print("Opaque faces: {}, Transparent faces: {}", .{main.renderer.chunk_meshing.quadsDrawn, main.renderer.chunk_meshing.transparentQuadsDrawn}, 0, y, 8, .left);
|
draw.print("Opaque faces: {}, Transparent faces: {}", .{main.renderer.chunk_meshing.quadsDrawn, main.renderer.chunk_meshing.transparentQuadsDrawn}, 0, y, 8, .left);
|
||||||
y += 8;
|
y += 8;
|
||||||
|
draw.print("Particle count: {}/{}", .{main.particles.ParticleSystem.getParticleCount(), main.particles.ParticleSystem.maxCapacity}, 0, y, 8, .left);
|
||||||
|
y += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ pub const Samples = enum(u8) {
|
|||||||
chunk_rendering_occlusion_test,
|
chunk_rendering_occlusion_test,
|
||||||
chunk_rendering_new_visible,
|
chunk_rendering_new_visible,
|
||||||
entity_rendering,
|
entity_rendering,
|
||||||
|
particle_rendering,
|
||||||
transparent_rendering_preparation,
|
transparent_rendering_preparation,
|
||||||
transparent_rendering_occlusion_test,
|
transparent_rendering_occlusion_test,
|
||||||
transparent_rendering,
|
transparent_rendering,
|
||||||
@ -41,6 +42,7 @@ const names = [_][]const u8{
|
|||||||
"Chunk Rendering Occlusion Test",
|
"Chunk Rendering Occlusion Test",
|
||||||
"Chunk Rendering New Visible",
|
"Chunk Rendering New Visible",
|
||||||
"Entity Rendering",
|
"Entity Rendering",
|
||||||
|
"Particle Rendering",
|
||||||
"Transparent Rendering Preparation",
|
"Transparent Rendering Preparation",
|
||||||
"Transparent Rendering Occlusion Test",
|
"Transparent Rendering Occlusion Test",
|
||||||
"Transparent Rendering",
|
"Transparent Rendering",
|
||||||
|
@ -23,6 +23,7 @@ pub const random = @import("random.zig");
|
|||||||
pub const renderer = @import("renderer.zig");
|
pub const renderer = @import("renderer.zig");
|
||||||
pub const rotation = @import("rotation.zig");
|
pub const rotation = @import("rotation.zig");
|
||||||
pub const settings = @import("settings.zig");
|
pub const settings = @import("settings.zig");
|
||||||
|
pub const particles = @import("particles.zig");
|
||||||
const tag = @import("tag.zig");
|
const tag = @import("tag.zig");
|
||||||
pub const Tag = tag.Tag;
|
pub const Tag = tag.Tag;
|
||||||
pub const utils = @import("utils.zig");
|
pub const utils = @import("utils.zig");
|
||||||
@ -644,6 +645,9 @@ pub fn main() void { // MARK: main()
|
|||||||
gui.init();
|
gui.init();
|
||||||
defer gui.deinit();
|
defer gui.deinit();
|
||||||
|
|
||||||
|
particles.ParticleManager.init();
|
||||||
|
defer particles.ParticleManager.deinit();
|
||||||
|
|
||||||
if(settings.playerName.len == 0) {
|
if(settings.playerName.len == 0) {
|
||||||
gui.openWindow("change_name");
|
gui.openWindow("change_name");
|
||||||
} else {
|
} else {
|
||||||
|
450
src/particles.zig
Normal file
450
src/particles.zig
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const main = @import("main");
|
||||||
|
const chunk_meshing = @import("renderer/chunk_meshing.zig");
|
||||||
|
const graphics = @import("graphics.zig");
|
||||||
|
const SSBO = graphics.SSBO;
|
||||||
|
const TextureArray = graphics.TextureArray;
|
||||||
|
const Shader = graphics.Shader;
|
||||||
|
const Image = graphics.Image;
|
||||||
|
const c = graphics.c;
|
||||||
|
const game = @import("game.zig");
|
||||||
|
const ZonElement = @import("zon.zig").ZonElement;
|
||||||
|
const random = @import("random.zig");
|
||||||
|
const vec = @import("vec.zig");
|
||||||
|
const Mat4f = vec.Mat4f;
|
||||||
|
const Vec3d = vec.Vec3d;
|
||||||
|
const Vec4d = vec.Vec4d;
|
||||||
|
const Vec3f = vec.Vec3f;
|
||||||
|
const Vec4f = vec.Vec4f;
|
||||||
|
const Vec3i = vec.Vec3i;
|
||||||
|
|
||||||
|
var seed: u64 = undefined;
|
||||||
|
|
||||||
|
var arena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator);
|
||||||
|
const arenaAllocator = arena.allocator();
|
||||||
|
|
||||||
|
pub const ParticleManager = struct {
|
||||||
|
var particleTypesSSBO: SSBO = undefined;
|
||||||
|
var types: main.List(ParticleType) = undefined;
|
||||||
|
var textures: main.List(Image) = undefined;
|
||||||
|
var emissionTextures: main.List(Image) = undefined;
|
||||||
|
|
||||||
|
var textureArray: TextureArray = undefined;
|
||||||
|
var emissionTextureArray: TextureArray = undefined;
|
||||||
|
|
||||||
|
const ParticleIndex = u16;
|
||||||
|
var particleTypeHashmap: std.StringHashMapUnmanaged(ParticleIndex) = undefined;
|
||||||
|
|
||||||
|
pub fn init() void {
|
||||||
|
types = .init(arenaAllocator);
|
||||||
|
textures = .init(arenaAllocator);
|
||||||
|
emissionTextures = .init(arenaAllocator);
|
||||||
|
textureArray = .init();
|
||||||
|
emissionTextureArray = .init();
|
||||||
|
particleTypesSSBO = SSBO.init();
|
||||||
|
ParticleSystem.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit() void {
|
||||||
|
types.deinit();
|
||||||
|
textures.deinit();
|
||||||
|
emissionTextures.deinit();
|
||||||
|
textureArray.deinit();
|
||||||
|
emissionTextureArray.deinit();
|
||||||
|
particleTypeHashmap.deinit(arenaAllocator.allocator);
|
||||||
|
ParticleSystem.deinit();
|
||||||
|
particleTypesSSBO.deinit();
|
||||||
|
arena.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(assetsFolder: []const u8, id: []const u8, zon: ZonElement) void {
|
||||||
|
const textureId = zon.get(?[]const u8, "texture", null) orelse {
|
||||||
|
std.log.err("Particle texture id was not specified for {s} ({s})", .{id, assetsFolder});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const particleType = readTextureDataAndParticleType(assetsFolder, textureId);
|
||||||
|
|
||||||
|
particleTypeHashmap.put(arenaAllocator.allocator, id, @intCast(types.items.len)) catch unreachable;
|
||||||
|
types.append(particleType);
|
||||||
|
|
||||||
|
std.log.debug("Registered particle type: {s}", .{id});
|
||||||
|
}
|
||||||
|
fn readTextureDataAndParticleType(assetsFolder: []const u8, textureId: []const u8) ParticleType {
|
||||||
|
var typ: ParticleType = undefined;
|
||||||
|
|
||||||
|
const base = readTexture(assetsFolder, textureId, ".png", Image.defaultImage, .isMandatory);
|
||||||
|
const emission = readTexture(assetsFolder, textureId, "_emission.png", Image.emptyImage, .isOptional);
|
||||||
|
const hasEmission = (emission.imageData.ptr != Image.emptyImage.imageData.ptr);
|
||||||
|
const baseAnimationFrameCount = base.height/base.width;
|
||||||
|
const emissionAnimationFrameCount = emission.height/emission.width;
|
||||||
|
|
||||||
|
typ.frameCount = @floatFromInt(baseAnimationFrameCount);
|
||||||
|
typ.startFrame = @floatFromInt(textures.items.len);
|
||||||
|
typ.size = @as(f32, @floatFromInt(base.width))/16;
|
||||||
|
|
||||||
|
var isBaseBroken = false;
|
||||||
|
var isEmissionBroken = false;
|
||||||
|
|
||||||
|
if(base.height%base.width != 0) {
|
||||||
|
std.log.err("Particle base texture has incorrect dimensions ({}x{}) expected height to be multiple of width for {s} ({s})", .{base.width, base.height, textureId, assetsFolder});
|
||||||
|
isBaseBroken = true;
|
||||||
|
}
|
||||||
|
if(hasEmission and emission.height%emission.width != 0) {
|
||||||
|
std.log.err("Particle emission texture has incorrect dimensions ({}x{}) expected height to be multiple of width for {s} ({s})", .{base.width, base.height, textureId, assetsFolder});
|
||||||
|
isEmissionBroken = true;
|
||||||
|
}
|
||||||
|
if(hasEmission and baseAnimationFrameCount != emissionAnimationFrameCount) {
|
||||||
|
std.log.err("Particle base texture and emission texture frame count mismatch ({} vs {}) for {s} ({s})", .{baseAnimationFrameCount, emissionAnimationFrameCount, textureId, assetsFolder});
|
||||||
|
isEmissionBroken = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
createAnimationFrames(&textures, baseAnimationFrameCount, base, isBaseBroken);
|
||||||
|
createAnimationFrames(&emissionTextures, baseAnimationFrameCount, emission, isBaseBroken or isEmissionBroken or !hasEmission);
|
||||||
|
|
||||||
|
return typ;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readTexture(assetsFolder: []const u8, textureId: []const u8, suffix: []const u8, default: graphics.Image, status: enum {isOptional, isMandatory}) graphics.Image {
|
||||||
|
var splitter = std.mem.splitScalar(u8, textureId, ':');
|
||||||
|
const mod = splitter.first();
|
||||||
|
const id = splitter.rest();
|
||||||
|
|
||||||
|
const gameAssetsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "assets/{s}/particles/textures/{s}{s}", .{mod, id, suffix}) catch unreachable;
|
||||||
|
defer main.stackAllocator.free(gameAssetsPath);
|
||||||
|
|
||||||
|
const worldAssetsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}/{s}/particles/textures/{s}{s}", .{assetsFolder, mod, id, suffix}) catch unreachable;
|
||||||
|
defer main.stackAllocator.free(worldAssetsPath);
|
||||||
|
|
||||||
|
return graphics.Image.readFromFile(arenaAllocator, worldAssetsPath) catch graphics.Image.readFromFile(arenaAllocator, gameAssetsPath) catch {
|
||||||
|
if(status == .isMandatory) std.log.err("Particle texture not found in {s} and {s}.", .{worldAssetsPath, gameAssetsPath});
|
||||||
|
return default;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn createAnimationFrames(container: *main.List(Image), frameCount: usize, image: Image, isBroken: bool) void {
|
||||||
|
for(0..frameCount) |i| {
|
||||||
|
container.append(if(isBroken) image else extractAnimationSlice(image, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extractAnimationSlice(image: Image, frameIndex: usize) Image {
|
||||||
|
const frameCount = image.height/image.width;
|
||||||
|
const frameHeight = image.height/frameCount;
|
||||||
|
const startHeight = frameHeight*frameIndex;
|
||||||
|
const endHeight = frameHeight*(frameIndex + 1);
|
||||||
|
var result = image;
|
||||||
|
result.height = @intCast(frameHeight);
|
||||||
|
result.imageData = result.imageData[startHeight*image.width .. endHeight*image.width];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generateTextureArray() void {
|
||||||
|
textureArray.generate(textures.items, true, true);
|
||||||
|
emissionTextureArray.generate(emissionTextures.items, true, false);
|
||||||
|
|
||||||
|
particleTypesSSBO.bufferData(ParticleType, ParticleManager.types.items);
|
||||||
|
particleTypesSSBO.bind(14);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ParticleSystem = struct {
|
||||||
|
pub const maxCapacity: u32 = 524288;
|
||||||
|
var particleCount: u32 = 0;
|
||||||
|
var particles: [maxCapacity]Particle = undefined;
|
||||||
|
var particlesLocal: [maxCapacity]ParticleLocal = undefined;
|
||||||
|
var properties: EmitterProperties = undefined;
|
||||||
|
var previousPlayerPos: Vec3d = undefined;
|
||||||
|
|
||||||
|
var particlesSSBO: SSBO = undefined;
|
||||||
|
|
||||||
|
var pipeline: graphics.Pipeline = undefined;
|
||||||
|
const UniformStruct = struct {
|
||||||
|
projectionAndViewMatrix: c_int,
|
||||||
|
billboardMatrix: c_int,
|
||||||
|
ambientLight: c_int,
|
||||||
|
};
|
||||||
|
var uniforms: UniformStruct = undefined;
|
||||||
|
|
||||||
|
pub fn init() void {
|
||||||
|
pipeline = graphics.Pipeline.init(
|
||||||
|
"assets/cubyz/shaders/particles/particles.vert",
|
||||||
|
"assets/cubyz/shaders/particles/particles.frag",
|
||||||
|
"",
|
||||||
|
&uniforms,
|
||||||
|
.{},
|
||||||
|
.{.depthTest = true, .depthWrite = true},
|
||||||
|
.{.attachments = &.{.noBlending}},
|
||||||
|
);
|
||||||
|
|
||||||
|
properties = EmitterProperties{
|
||||||
|
.gravity = .{0, 0, -2},
|
||||||
|
.drag = 0.2,
|
||||||
|
.lifeTimeMin = 10,
|
||||||
|
.lifeTimeMax = 10,
|
||||||
|
.velMin = 0.1,
|
||||||
|
.velMax = 0.3,
|
||||||
|
.rotVelMin = std.math.pi*0.2,
|
||||||
|
.rotVelMax = std.math.pi*0.6,
|
||||||
|
.randomizeRotationOnSpawn = true,
|
||||||
|
};
|
||||||
|
particlesSSBO = SSBO.init();
|
||||||
|
particlesSSBO.createDynamicBuffer(Particle, maxCapacity);
|
||||||
|
particlesSSBO.bind(13);
|
||||||
|
|
||||||
|
seed = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp())));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit() void {
|
||||||
|
pipeline.deinit();
|
||||||
|
particlesSSBO.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(deltaTime: f32) void {
|
||||||
|
const vecDeltaTime: Vec4f = @as(Vec4f, @splat(deltaTime));
|
||||||
|
const playerPos = game.Player.getEyePosBlocking();
|
||||||
|
const prevPlayerPosDifference: Vec3f = @floatCast(previousPlayerPos - playerPos);
|
||||||
|
|
||||||
|
var i: u32 = 0;
|
||||||
|
while(i < particleCount) {
|
||||||
|
const particle = &particles[i];
|
||||||
|
const particleLocal = &particlesLocal[i];
|
||||||
|
particle.lifeRatio -= particleLocal.lifeVelocity*deltaTime;
|
||||||
|
if(particle.lifeRatio < 0) {
|
||||||
|
particleCount -= 1;
|
||||||
|
particles[i] = particles[particleCount];
|
||||||
|
particlesLocal[i] = particlesLocal[particleCount];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rot = particle.posAndRotation[3];
|
||||||
|
const rotVel = particleLocal.velAndRotationVel[3];
|
||||||
|
rot += rotVel*deltaTime;
|
||||||
|
|
||||||
|
particleLocal.velAndRotationVel += vec.combine(properties.gravity, 0)*vecDeltaTime;
|
||||||
|
particleLocal.velAndRotationVel *= @splat(@exp(-properties.drag*deltaTime));
|
||||||
|
const posDelta = particleLocal.velAndRotationVel*vecDeltaTime;
|
||||||
|
|
||||||
|
if(particleLocal.collides) {
|
||||||
|
const size = ParticleManager.types.items[particle.typ].size;
|
||||||
|
const hitBox: game.collision.Box = .{.min = @splat(size*-0.5), .max = @splat(size*0.5)};
|
||||||
|
var v3Pos = playerPos + @as(Vec3d, @floatCast(Vec3f{particle.posAndRotation[0], particle.posAndRotation[1], particle.posAndRotation[2]} + prevPlayerPosDifference));
|
||||||
|
v3Pos[0] += posDelta[0];
|
||||||
|
if(game.collision.collides(.client, .x, -posDelta[0], v3Pos, hitBox)) |box| {
|
||||||
|
v3Pos[0] = if(posDelta[0] < 0)
|
||||||
|
box.max[0] - hitBox.min[0]
|
||||||
|
else
|
||||||
|
box.min[0] - hitBox.max[0];
|
||||||
|
}
|
||||||
|
v3Pos[1] += posDelta[1];
|
||||||
|
if(game.collision.collides(.client, .y, -posDelta[1], v3Pos, hitBox)) |box| {
|
||||||
|
v3Pos[1] = if(posDelta[1] < 0)
|
||||||
|
box.max[1] - hitBox.min[1]
|
||||||
|
else
|
||||||
|
box.min[1] - hitBox.max[1];
|
||||||
|
}
|
||||||
|
v3Pos[2] += posDelta[2];
|
||||||
|
if(game.collision.collides(.client, .z, -posDelta[2], v3Pos, hitBox)) |box| {
|
||||||
|
v3Pos[2] = if(posDelta[2] < 0)
|
||||||
|
box.max[2] - hitBox.min[2]
|
||||||
|
else
|
||||||
|
box.min[2] - hitBox.max[2];
|
||||||
|
}
|
||||||
|
particle.posAndRotation = vec.combine(@as(Vec3f, @floatCast(v3Pos - playerPos)), 0);
|
||||||
|
} else {
|
||||||
|
particle.posAndRotation += posDelta + vec.combine(prevPlayerPosDifference, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
particle.posAndRotation[3] = rot;
|
||||||
|
particleLocal.velAndRotationVel[3] = rotVel;
|
||||||
|
|
||||||
|
const positionf64 = @as(Vec4d, @floatCast(particle.posAndRotation)) + Vec4d{playerPos[0], playerPos[1], playerPos[2], 0};
|
||||||
|
const intPos: vec.Vec4i = @intFromFloat(@floor(positionf64));
|
||||||
|
const light: [6]u8 = main.renderer.mesh_storage.getLight(intPos[0], intPos[1], intPos[2]) orelse @splat(0);
|
||||||
|
const compressedLight =
|
||||||
|
@as(u32, light[0] >> 3) << 25 |
|
||||||
|
@as(u32, light[1] >> 3) << 20 |
|
||||||
|
@as(u32, light[2] >> 3) << 15 |
|
||||||
|
@as(u32, light[3] >> 3) << 10 |
|
||||||
|
@as(u32, light[4] >> 3) << 5 |
|
||||||
|
@as(u32, light[5] >> 3);
|
||||||
|
particle.light = compressedLight;
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
previousPlayerPos = playerPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addParticle(typ: u32, pos: Vec3d, vel: Vec3f, collides: bool) void {
|
||||||
|
const lifeTime = properties.lifeTimeMin + random.nextFloat(&seed)*properties.lifeTimeMax;
|
||||||
|
const rot = if(properties.randomizeRotationOnSpawn) random.nextFloat(&seed)*std.math.pi*2 else 0;
|
||||||
|
|
||||||
|
particles[particleCount] = Particle{
|
||||||
|
.posAndRotation = vec.combine(@as(Vec3f, @floatCast(pos - previousPlayerPos)), rot),
|
||||||
|
.typ = typ,
|
||||||
|
};
|
||||||
|
particlesLocal[particleCount] = ParticleLocal{
|
||||||
|
.velAndRotationVel = vec.combine(vel, properties.rotVelMin + random.nextFloatSigned(&seed)*properties.rotVelMax),
|
||||||
|
.lifeVelocity = 1/lifeTime,
|
||||||
|
.collides = collides,
|
||||||
|
};
|
||||||
|
particleCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(projectionMatrix: Mat4f, viewMatrix: Mat4f, ambientLight: Vec3f) void {
|
||||||
|
particlesSSBO.bufferSubData(Particle, &particles, particleCount);
|
||||||
|
|
||||||
|
pipeline.bind(null);
|
||||||
|
|
||||||
|
const projectionAndViewMatrix = Mat4f.mul(projectionMatrix, viewMatrix);
|
||||||
|
c.glUniformMatrix4fv(uniforms.projectionAndViewMatrix, 1, c.GL_TRUE, @ptrCast(&projectionAndViewMatrix));
|
||||||
|
c.glUniform3fv(uniforms.ambientLight, 1, @ptrCast(&ambientLight));
|
||||||
|
|
||||||
|
const billboardMatrix = Mat4f.rotationZ(-game.camera.rotation[2] + std.math.pi*0.5)
|
||||||
|
.mul(Mat4f.rotationY(game.camera.rotation[0] - std.math.pi*0.5));
|
||||||
|
c.glUniformMatrix4fv(uniforms.billboardMatrix, 1, c.GL_TRUE, @ptrCast(&billboardMatrix));
|
||||||
|
|
||||||
|
c.glActiveTexture(c.GL_TEXTURE0);
|
||||||
|
ParticleManager.textureArray.bind();
|
||||||
|
c.glActiveTexture(c.GL_TEXTURE1);
|
||||||
|
ParticleManager.emissionTextureArray.bind();
|
||||||
|
|
||||||
|
c.glBindVertexArray(chunk_meshing.vao);
|
||||||
|
|
||||||
|
for(0..std.math.divCeil(u32, particleCount, chunk_meshing.maxQuadsInIndexBuffer) catch unreachable) |_| {
|
||||||
|
c.glDrawElements(c.GL_TRIANGLES, @intCast(particleCount*6), c.GL_UNSIGNED_INT, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getParticleCount() u32 {
|
||||||
|
return particleCount;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const EmitterProperties = struct {
|
||||||
|
gravity: Vec3f = @splat(0),
|
||||||
|
drag: f32 = 0,
|
||||||
|
velMin: f32 = 0,
|
||||||
|
velMax: f32 = 0,
|
||||||
|
rotVelMin: f32 = 0,
|
||||||
|
rotVelMax: f32 = 0,
|
||||||
|
lifeTimeMin: f32 = 0,
|
||||||
|
lifeTimeMax: f32 = 0,
|
||||||
|
randomizeRotationOnSpawn: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DirectionMode = union(enum(u8)) {
|
||||||
|
// The particle goes in the direction away from the center
|
||||||
|
spread: void,
|
||||||
|
// The particle goes in a random direction
|
||||||
|
scatter: void,
|
||||||
|
// The particle goes in the specified direction
|
||||||
|
direction: Vec3f,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Emitter = struct {
|
||||||
|
typ: u16 = 0,
|
||||||
|
collides: bool,
|
||||||
|
|
||||||
|
pub const SpawnPoint = struct {
|
||||||
|
mode: DirectionMode,
|
||||||
|
position: Vec3d,
|
||||||
|
|
||||||
|
pub fn spawn(self: SpawnPoint) struct {Vec3d, Vec3f} {
|
||||||
|
const particlePos = self.position;
|
||||||
|
const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&seed)*ParticleSystem.properties.velMax);
|
||||||
|
const dir: Vec3f = switch(self.mode) {
|
||||||
|
.direction => |dir| dir,
|
||||||
|
.scatter, .spread => vec.normalize(random.nextFloatVectorSigned(3, &seed)),
|
||||||
|
};
|
||||||
|
const particleVel = dir*speed;
|
||||||
|
|
||||||
|
return .{particlePos, particleVel};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SpawnSphere = struct {
|
||||||
|
radius: f32,
|
||||||
|
mode: DirectionMode,
|
||||||
|
position: Vec3d,
|
||||||
|
|
||||||
|
pub fn spawn(self: SpawnSphere) struct {Vec3d, Vec3f} {
|
||||||
|
const spawnPos: Vec3f = @splat(self.radius);
|
||||||
|
var offsetPos: Vec3f = undefined;
|
||||||
|
while(true) {
|
||||||
|
offsetPos = random.nextFloatVectorSigned(3, &seed);
|
||||||
|
if(vec.lengthSquare(offsetPos) <= 1) break;
|
||||||
|
}
|
||||||
|
const particlePos = self.position + @as(Vec3d, @floatCast(offsetPos*spawnPos));
|
||||||
|
const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&seed)*ParticleSystem.properties.velMax);
|
||||||
|
const dir: Vec3f = switch(self.mode) {
|
||||||
|
.direction => |dir| dir,
|
||||||
|
.scatter => vec.normalize(random.nextFloatVectorSigned(3, &seed)),
|
||||||
|
.spread => @floatCast(offsetPos),
|
||||||
|
};
|
||||||
|
const particleVel = dir*speed;
|
||||||
|
|
||||||
|
return .{particlePos, particleVel};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SpawnCube = struct {
|
||||||
|
size: Vec3f,
|
||||||
|
mode: DirectionMode,
|
||||||
|
position: Vec3d,
|
||||||
|
|
||||||
|
pub fn spawn(self: SpawnCube) struct {Vec3d, Vec3f} {
|
||||||
|
const spawnPos: Vec3f = self.size;
|
||||||
|
const offsetPos: Vec3f = random.nextFloatVectorSigned(3, &seed);
|
||||||
|
const particlePos = self.position + @as(Vec3d, @floatCast(offsetPos*spawnPos));
|
||||||
|
const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&seed)*ParticleSystem.properties.velMax);
|
||||||
|
const dir: Vec3f = switch(self.mode) {
|
||||||
|
.direction => |dir| dir,
|
||||||
|
.scatter => vec.normalize(random.nextFloatVectorSigned(3, &seed)),
|
||||||
|
.spread => vec.normalize(@as(Vec3f, @floatCast(offsetPos))),
|
||||||
|
};
|
||||||
|
const particleVel = dir*speed;
|
||||||
|
|
||||||
|
return .{particlePos, particleVel};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(id: []const u8, collides: bool) Emitter {
|
||||||
|
const emitter = Emitter{
|
||||||
|
.typ = ParticleManager.particleTypeHashmap.get(id) orelse 0,
|
||||||
|
.collides = collides,
|
||||||
|
};
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawnParticles(self: Emitter, spawnCount: u32, comptime T: type, spawnRules: T) void {
|
||||||
|
const count = @min(spawnCount, ParticleSystem.maxCapacity - ParticleSystem.particleCount);
|
||||||
|
for(0..count) |_| {
|
||||||
|
const particlePos, const particleVel = spawnRules.spawn();
|
||||||
|
|
||||||
|
ParticleSystem.addParticle(self.typ, particlePos, particleVel, self.collides);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ParticleType = struct {
|
||||||
|
frameCount: f32,
|
||||||
|
startFrame: f32,
|
||||||
|
size: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Particle = struct {
|
||||||
|
posAndRotation: Vec4f,
|
||||||
|
lifeRatio: f32 = 1,
|
||||||
|
light: u32 = 0,
|
||||||
|
typ: u32,
|
||||||
|
// 4 bytes left for use
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ParticleLocal = struct {
|
||||||
|
velAndRotationVel: Vec4f,
|
||||||
|
lifeVelocity: f32,
|
||||||
|
collides: bool,
|
||||||
|
};
|
@ -5,6 +5,7 @@ const blocks = @import("blocks.zig");
|
|||||||
const chunk = @import("chunk.zig");
|
const chunk = @import("chunk.zig");
|
||||||
const entity = @import("entity.zig");
|
const entity = @import("entity.zig");
|
||||||
const graphics = @import("graphics.zig");
|
const graphics = @import("graphics.zig");
|
||||||
|
const particles = @import("particles.zig");
|
||||||
const c = graphics.c;
|
const c = graphics.c;
|
||||||
const game = @import("game.zig");
|
const game = @import("game.zig");
|
||||||
const World = game.World;
|
const World = game.World;
|
||||||
@ -240,6 +241,16 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
|
|||||||
itemdrop.ItemDropRenderer.renderItemDrops(game.projectionMatrix, ambientLight, playerPos);
|
itemdrop.ItemDropRenderer.renderItemDrops(game.projectionMatrix, ambientLight, playerPos);
|
||||||
gpu_performance_measuring.stopQuery();
|
gpu_performance_measuring.stopQuery();
|
||||||
|
|
||||||
|
gpu_performance_measuring.startQuery(.particle_rendering);
|
||||||
|
particles.ParticleSystem.render(game.projectionMatrix, game.camera.viewMatrix, ambientLight);
|
||||||
|
gpu_performance_measuring.stopQuery();
|
||||||
|
|
||||||
|
// Rebind block textures back to their original slots
|
||||||
|
c.glActiveTexture(c.GL_TEXTURE0);
|
||||||
|
blocks.meshes.blockTextureArray.bind();
|
||||||
|
c.glActiveTexture(c.GL_TEXTURE1);
|
||||||
|
blocks.meshes.emissionTextureArray.bind();
|
||||||
|
|
||||||
MeshSelection.render(game.projectionMatrix, game.camera.viewMatrix, playerPos);
|
MeshSelection.render(game.projectionMatrix, game.camera.viewMatrix, playerPos);
|
||||||
|
|
||||||
// Render transparent chunk meshes:
|
// Render transparent chunk meshes:
|
||||||
|
@ -71,6 +71,7 @@ pub var commandBuffer: graphics.LargeBuffer(IndirectData) = undefined;
|
|||||||
pub var chunkIDBuffer: graphics.LargeBuffer(u32) = undefined;
|
pub var chunkIDBuffer: graphics.LargeBuffer(u32) = undefined;
|
||||||
pub var quadsDrawn: usize = 0;
|
pub var quadsDrawn: usize = 0;
|
||||||
pub var transparentQuadsDrawn: usize = 0;
|
pub var transparentQuadsDrawn: usize = 0;
|
||||||
|
pub const maxQuadsInIndexBuffer = 3 << (3*chunk.chunkShift); // maximum 3 faces/block
|
||||||
|
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
lighting.init();
|
lighting.init();
|
||||||
@ -119,7 +120,7 @@ pub fn init() void {
|
|||||||
}}},
|
}}},
|
||||||
);
|
);
|
||||||
|
|
||||||
var rawData: [6*3 << (3*chunk.chunkShift)]u32 = undefined; // 6 vertices per face, maximum 3 faces/block
|
var rawData: [6*maxQuadsInIndexBuffer]u32 = undefined;
|
||||||
const lut = [_]u32{0, 2, 1, 1, 2, 3};
|
const lut = [_]u32{0, 2, 1, 1, 2, 3};
|
||||||
for(0..rawData.len) |i| {
|
for(0..rawData.len) |i| {
|
||||||
rawData[i] = @as(u32, @intCast(i))/6*4 + lut[i%6];
|
rawData[i] = @as(u32, @intCast(i))/6*4 + lut[i%6];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user