diff --git a/build.zig b/build.zig index c608e70..417eb5e 100644 --- a/build.zig +++ b/build.zig @@ -25,6 +25,7 @@ pub fn build(b: *std.Build) void { exe.linkLibrary(rl.artifact("raylib")); exe.root_module.addImport("znoise", znoise.module("root")); + exe.root_module.addIncludePath(b.path("src/c")); exe.linkLibrary(znoise.artifact("FastNoiseLite")); b.installArtifact(exe); @@ -47,6 +48,7 @@ pub fn build(b: *std.Build) void { exe_unit_tests.linkLibrary(rl.artifact("raylib")); exe_unit_tests.root_module.addImport("znoise", znoise.module("root")); exe_unit_tests.linkLibrary(znoise.artifact("FastNoiseLite")); + exe_unit_tests.root_module.addIncludePath(b.path("src/c")); const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); diff --git a/resources/shaders/tiling.fs b/resources/shaders/tiling.fs index f684a1c..cf89965 100644 --- a/resources/shaders/tiling.fs +++ b/resources/shaders/tiling.fs @@ -4,8 +4,8 @@ uniform sampler2D diffuseMap; uniform vec2 textureTiling; in vec2 fragTexCoord; -in vec2 fragTexCoord2; -in vec4 fragColor; +in vec3 fragNormal; +in vec2 fragTileTexCoord; flat in int ambientOcclusionSide1; flat in int ambientOcclusionSide2; flat in int ambientOcclusionCorner1; @@ -16,14 +16,28 @@ flat in int quadWidth; out vec4 outColor; +// this is a shitty approximation of arcsin(x)/pi that gets the tails right and is reasonably close to the actual arcsin(x)/pi curve. +float fakeArcsin(float x) { + x = clamp(x, -1.0, 1.0); + float ax = abs(x); + float sqrtPart = sqrt(1.0 - ax); + float result = 0.5 - sqrtPart * (0.5 - 0.06667 * ax); + return x < 0.0 ? -result : result; +} + void main() { - vec2 texCoord = (floor(fragTexCoord*textureTiling) + fract(fragTexCoord2)) / textureTiling; + vec2 texCoord = (floor(fragTexCoord*textureTiling) + fract(fragTileTexCoord)) / textureTiling; outColor = texture(diffuseMap, texCoord); - ivec2 floorFragTexCoord2 = ivec2(fragTexCoord2); - if ((fragTexCoord2.x < 0.05 || fragTexCoord2.x > quadWidth-0.05) && (((ambientOcclusionSide1 >> floorFragTexCoord2.y) & 1) == 1)) outColor *= 0.5; - else if((fragTexCoord2.y < 0.05 || fragTexCoord2.y > quadHeight-0.05) && (((ambientOcclusionSide2 >> floorFragTexCoord2.y) & 1) == 1)) outColor *= 0.5; + ivec2 floorFragTileTexCoord = ivec2(fragTileTexCoord); + + if(fragTileTexCoord.x < 1 && (((ambientOcclusionSide1 >> floorFragTileTexCoord.y) & 1) == 1)) outColor *= 0.5 + fakeArcsin(fragTileTexCoord.x); + + // if(fragTileTexCoord.x < 0.125 && fragTileTexCoord.x < fract(fragTileTexCoord.y) && fragTileTexCoord.x + fract(fragTileTexCoord.y) < 1.0 && ((ambientOcclusionSide1 >> floorFragTileTexCoord.y) & 1) == 1) outColor *= 0.5; + + //if((fragTileTexCoord.x < 0.25 || fragTileTexCoord.x > quadWidth-0.25) && (((ambientOcclusionSide1 >> floorFragTileTexCoord.y) & 1) == 1)) outColor *= 0.5; + //if((fragTileTexCoord.y < 0.25 || fragTileTexCoord.y > quadHeight-0.25) && (((ambientOcclusionSide2 >> floorFragTileTexCoord.y) & 1) == 1)) outColor *= 0.5; outColor.a = 1; } diff --git a/resources/shaders/tiling.vs b/resources/shaders/tiling.vs index 29676f5..6148ca7 100644 --- a/resources/shaders/tiling.vs +++ b/resources/shaders/tiling.vs @@ -2,13 +2,13 @@ in vec3 vertexPosition; in vec2 vertexTexCoord; -in vec2 vertexTexCoord2; -in vec4 vertexColor; -in vec4 vertexTangent; // metadata1 +in vec3 vertexNormal; +in vec2 vertexTileTexCoord; +in vec4 vertexMetadata1; out vec2 fragTexCoord; -out vec2 fragTexCoord2; -out vec4 fragColor; +out vec3 fragNormal; +out vec2 fragTileTexCoord; flat out int ambientOcclusionSide1; flat out int ambientOcclusionSide2; @@ -22,18 +22,18 @@ uniform mat4 mvp; void main() { fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - fragTexCoord2 = vertexTexCoord2; + fragTileTexCoord = vertexTileTexCoord; + fragNormal = vertexNormal; gl_Position = mvp*vec4(vertexPosition, 1.0); - // metadata 1 processing - ambientOcclusionSide1 = floatBitsToInt(vertexTangent.x); - ambientOcclusionSide2 = floatBitsToInt(vertexTangent.y); - int metadata1Z = floatBitsToInt(vertexTangent.z); - int metadata1W = floatBitsToInt(vertexTangent.w); - ambientOcclusionCorner1 = (metadata1Z & 0x1) >> 0; - ambientOcclusionCorner2 = (metadata1Z & 0x2) >> 1; - ambientOcclusionCorner3 = (metadata1Z & 0x4) >> 2; - quadHeight = (metadata1Z & 0x1f8) >> 3; - quadWidth = (metadata1Z & 0x7e00) >> 9; + // metadata 1 parsing + ambientOcclusionSide1 = floatBitsToInt(vertexMetadata1.x); + ambientOcclusionSide2 = floatBitsToInt(vertexMetadata1.y); + int metadata1Z = floatBitsToInt(vertexMetadata1.z); + int metadata1W = floatBitsToInt(vertexMetadata1.w); + ambientOcclusionCorner1 = (metadata1Z & 0x1) >> 0; // Take 0th bit. + ambientOcclusionCorner2 = (metadata1Z & 0x2) >> 1; // Take 1st bit. + ambientOcclusionCorner3 = (metadata1Z & 0x4) >> 2; // Take 2nd bit. + quadHeight = (metadata1Z & 0x1f8) >> 3; // Take 3rd-8th bits. + quadWidth = (metadata1Z & 0x7e00) >> 9; // Take 9th-14th bits. } diff --git a/src/c/raylib_extension.h b/src/c/raylib_extension.h new file mode 100644 index 0000000..1cb8338 --- /dev/null +++ b/src/c/raylib_extension.h @@ -0,0 +1,675 @@ +/********************************************************************************************** +* This file contains modified pieces of code from the Raylib library. +* Original license follows below. +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +**********************************************************************************************/ + +#include "raylib.h" +#include "rlgl.h" +#include "raymath.h" + +#include +#include +#include + +#define CHUNK_MAX_MESH_VERTEX_BUFFERS 5 +#define CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0 +#define CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1 +#define CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2 +#define CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TILETEXCOORD 3 +#define CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_METADATA1 4 + +#define CHUNK_MAX_MATERIAL_MAPS 12 +#define CHUNK_MAX_SHADER_LOCATIONS 32 + +typedef enum { + CHUNK_SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position + CHUNK_SHADER_LOC_VERTEX_TEXCOORD, // Shader location: vertex attribute: texcoord + CHUNK_SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal + CHUNK_SHADER_LOC_VERTEX_TILETEXCOORD, // Shader location: vertex attribute: tiletexcoord + CHUNK_SHADER_LOC_VERTEX_METADATA1, // Shader location: vertex attribute: metadata1 + CHUNK_SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection + CHUNK_SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) + CHUNK_SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection + CHUNK_SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) + CHUNK_SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal + CHUNK_SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view + CHUNK_SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color + CHUNK_SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color + CHUNK_SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color + CHUNK_SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: CHUNK_SHADER_LOC_MAP_DIFFUSE) + CHUNK_SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: CHUNK_SHADER_LOC_MAP_SPECULAR) + CHUNK_SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal + CHUNK_SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness + CHUNK_SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion + CHUNK_SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission + CHUNK_SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height + CHUNK_SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap + CHUNK_SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance + CHUNK_SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter + CHUNK_SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf +} chunkShaderLocationIndex; + +#define CHUNK_SHADER_LOC_MAP_DIFFUSE CHUNK_SHADER_LOC_MAP_ALBEDO +#define CHUNK_SHADER_LOC_MAP_SPECULAR CHUNK_SHADER_LOC_MAP_METALNESS + +#define CHUNK_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION +#define CHUNK_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD +#define CHUNK_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL +#define CHUNK_DEFAULT_SHADER_ATTRIB_NAME_TILETEXCOORD "vertexTileTexCoord" // Bound by default to shader location: CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TILETEXCOORD +#define CHUNK_DEFAULT_SHADER_ATTRIB_NAME_METADATA1 "vertexMetadata1" // Bound by default to shader location: CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_METADATA1 +#define CHUNK_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +#define CHUNK_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +#define CHUNK_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +#define CHUNK_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +#define CHUNK_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))) +#define CHUNK_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +#define CHUNK_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices +#define CHUNK_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +#define CHUNK_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) +#define CHUNK_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) + +typedef struct ChunkMesh { + int vertexCount; // Number of vertices stored in arrays + int triangleCount; // Number of triangles stored (indexed or not) + + // Vertex attributes data + float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) + float *tiletexcoords; // Vertex tile tex coordinates (UV - 2 components per vertex) (shader-location = 3) + float *metadata1; // Vertex metadata 1 (ABCD - 4 components per vertex) (shader-location = 4) + + // OpenGL identifiers + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) +} ChunkMesh; + +// Model, meshes, materials and animation data +typedef struct ChunkModel { + Matrix transform; // Local transform matrix + + int meshCount; // Number of meshes + int materialCount; // Number of materials + ChunkMesh *meshes; // Meshes array + Material *materials; // Materials array + int *meshMaterial; // Mesh material number + + // Animation data + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + Transform *bindPose; // Bones base transformation (pose) +} ChunkModel; + +RLAPI void UploadChunkMesh(ChunkMesh *mesh, bool dynamic); // Upload mesh vertex data in GPU and provide VAO/VBO ids +RLAPI ChunkModel LoadChunkModelFromMesh(ChunkMesh mesh); // Load model from generated mesh (default material) +RLAPI void UnloadChunkModel(ChunkModel model); // Unload model (including meshes) from memory (RAM and/or VRAM) +RLAPI void UnloadChunkMesh(ChunkMesh mesh); // Unload mesh data from CPU and GPU +RLAPI void DrawChunkModel(ChunkModel model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) +RLAPI void DrawChunkModelEx(ChunkModel model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters +RLAPI void DrawChunkMesh(ChunkMesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform +RLAPI Shader LoadChunkShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations +RLAPI Shader LoadChunkShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations + +RLAPI unsigned int chunkLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings +RLAPI unsigned int chunkLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program + +// Upload vertex data into a VAO (if supported) and VBO +void UploadChunkMesh(ChunkMesh *mesh, bool dynamic) { + if (mesh->vaoId > 0) + { + // Check if mesh has already been loaded in GPU + printf("CHUNK VAO: [ID %i] Trying to re-load an already loaded mesh\n", mesh->vaoId); + return; + } + + mesh->vboId = (unsigned int *)RL_CALLOC(CHUNK_MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); + + mesh->vaoId = 0; // Vertex Array Object + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = 0; + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = 0; + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = 0; + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TILETEXCOORD] = 0; + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_METADATA1] = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + mesh->vaoId = rlLoadVertexArray(); + rlEnableVertexArray(mesh->vaoId); + + // NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data + + // Enable vertex attributes: position (shader-location = 0) + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = rlLoadVertexBuffer(mesh->vertices, mesh->vertexCount*3*sizeof(float), dynamic); + rlSetVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION); + + // Enable vertex attributes: texcoords (shader-location = 1) + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic); + rlSetVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + + // Enable vertex attributes: normals (shader-location = 2) + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = rlLoadVertexBuffer(mesh->normals, mesh->vertexCount*3*sizeof(float), dynamic); + rlSetVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, 3, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL); + + // Enable vertex attribute: tiletexcoord (shader-location = 3) + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TILETEXCOORD] = rlLoadVertexBuffer(mesh->tiletexcoords, mesh->vertexCount*2*sizeof(float), dynamic); + rlSetVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TILETEXCOORD, 2, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TILETEXCOORD); + + // Enable vertex attribute: metadata1 (shader-location = 4) + mesh->vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_METADATA1] = rlLoadVertexBuffer(mesh->metadata1, mesh->vertexCount*4*sizeof(float), dynamic); + rlSetVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_METADATA1, 4, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_METADATA1); + + if (mesh->vaoId > 0) printf("CHUNK VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU)\n", mesh->vaoId); + else printf("CHUNK VBO: Mesh uploaded successfully to VRAM (GPU)"); + + rlDisableVertexArray(); +#endif +} + + +// Load model from generated mesh +// WARNING: A shallow copy of mesh is generated, passed by value, +// as long as struct contains pointers to data and some values, we get a copy +// of mesh pointing to same data as original version... be careful! +ChunkModel LoadChunkModelFromMesh(ChunkMesh mesh) +{ + ChunkModel model = { 0 }; + + model.transform = MatrixIdentity(); + + model.meshCount = 1; + model.meshes = (ChunkMesh *)RL_CALLOC(model.meshCount, sizeof(ChunkMesh)); + model.meshes[0] = mesh; + + model.materialCount = 1; + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); + model.materials[0] = LoadMaterialDefault(); + + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); + model.meshMaterial[0] = 0; // First material index + + return model; +} + + +// Unload model (meshes/materials) from memory (RAM and/or VRAM) +// NOTE: This function takes care of all model elements, for a detailed control +// over them, use UnloadMesh() and UnloadMaterial() +void UnloadChunkModel(ChunkModel model) +{ + // Unload meshes + for (int i = 0; i < model.meshCount; i++) UnloadChunkMesh(model.meshes[i]); + + // Unload materials maps + // NOTE: As the user could be sharing shaders and textures between models, + // we don't unload the material but just free its maps, + // the user is responsible for freeing models shaders and textures + for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps); + + // Unload arrays + RL_FREE(model.meshes); + RL_FREE(model.materials); + RL_FREE(model.meshMaterial); + + TRACELOG(LOG_INFO, "CHUNK MODEL: Unloaded model (and meshes) from RAM and VRAM"); +} + +// Unload mesh from memory (RAM and VRAM) +void UnloadChunkMesh(ChunkMesh mesh) +{ + // Unload rlgl mesh vboId data + rlUnloadVertexArray(mesh.vaoId); + + if (mesh.vboId != NULL) for (int i = 0; i < CHUNK_MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]); + RL_FREE(mesh.vboId); + + RL_FREE(mesh.vertices); + RL_FREE(mesh.texcoords); + RL_FREE(mesh.normals); + RL_FREE(mesh.tiletexcoords); +} + +// Draw a model (with texture if set) +void DrawChunkModel(ChunkModel model, Vector3 position, float scale, Color tint) +{ + Vector3 vScale = { scale, scale, scale }; + Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f }; + + DrawChunkModelEx(model, position, rotationAxis, 0.0f, vScale, tint); +} + +// Draw a model with extended parameters +void DrawChunkModelEx(ChunkModel model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) +{ + // Calculate transformation matrix from function parameters + // Get transform matrix (rotation -> scale -> translation) + Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); + Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); + Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); + + Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); + + // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) + model.transform = MatrixMultiply(model.transform, matTransform); + + for (int i = 0; i < model.meshCount; i++) + { + Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color; + + Color colorTint = WHITE; + colorTint.r = (unsigned char)(((int)color.r*(int)tint.r)/255); + colorTint.g = (unsigned char)(((int)color.g*(int)tint.g)/255); + colorTint.b = (unsigned char)(((int)color.b*(int)tint.b)/255); + colorTint.a = (unsigned char)(((int)color.a*(int)tint.a)/255); + + model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint; + DrawChunkMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); + model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color; + } +} + +// Draw a 3d mesh with material and transform +void DrawChunkMesh(ChunkMesh mesh, Material material, Matrix transform) +{ +#if defined(GRAPHICS_API_OPENGL_11) + #define GL_VERTEX_ARRAY 0x8074 + #define GL_NORMAL_ARRAY 0x8075 + #define GL_COLOR_ARRAY 0x8076 + #define GL_TEXTURE_COORD_ARRAY 0x8078 + + rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id); + + rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); + rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); + rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); + rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); + + rlPushMatrix(); + rlMultMatrixf(MatrixToFloat(transform)); + rlColor4ub(material.maps[MATERIAL_MAP_DIFFUSE].color.r, + material.maps[MATERIAL_MAP_DIFFUSE].color.g, + material.maps[MATERIAL_MAP_DIFFUSE].color.b, + material.maps[MATERIAL_MAP_DIFFUSE].color.a); + + if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, mesh.indices); + else rlDrawVertexArray(0, mesh.vertexCount); + rlPopMatrix(); + + rlDisableStatePointer(GL_VERTEX_ARRAY); + rlDisableStatePointer(GL_TEXTURE_COORD_ARRAY); + rlDisableStatePointer(GL_NORMAL_ARRAY); + rlDisableStatePointer(GL_COLOR_ARRAY); + + rlDisableTexture(); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Bind shader program + rlEnableShader(material.shader.id); + + // Send required data to shader (matrices, values) + //----------------------------------------------------- + // Upload to shader material.colDiffuse + if (material.shader.locs[CHUNK_SHADER_LOC_COLOR_DIFFUSE] != -1) + { + float values[4] = { + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f + }; + + rlSetUniform(material.shader.locs[CHUNK_SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1); + } + + // Upload to shader material.colSpecular (if location available) + if (material.shader.locs[CHUNK_SHADER_LOC_COLOR_SPECULAR] != -1) + { + float values[4] = { + (float)material.maps[MATERIAL_MAP_SPECULAR].color.r/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.g/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.b/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.a/255.0f + }; + + rlSetUniform(material.shader.locs[CHUNK_SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1); + } + + // Get a copy of current matrices to work with, + // just in case stereo render is required, and we need to modify them + // NOTE: At this point the modelview matrix just contains the view matrix (camera) + // That's because BeginMode3D() sets it and there is no model-drawing function + // that modifies it, all use rlPushMatrix() and rlPopMatrix() + Matrix matModel = MatrixIdentity(); + Matrix matView = rlGetMatrixModelview(); + Matrix matModelView = MatrixIdentity(); + Matrix matProjection = rlGetMatrixProjection(); + + // Upload view and projection matrices (if locations available) + if (material.shader.locs[CHUNK_SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[CHUNK_SHADER_LOC_MATRIX_VIEW], matView); + if (material.shader.locs[CHUNK_SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[CHUNK_SHADER_LOC_MATRIX_PROJECTION], matProjection); + + // Accumulate several model transformations: + // transform: model transformation provided (includes DrawModel() params combined with model.transform) + // rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack + matModel = MatrixMultiply(transform, rlGetMatrixTransform()); + + // Model transformation matrix is sent to shader uniform location: SHADER_LOC_MATRIX_MODEL + if (material.shader.locs[CHUNK_SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[CHUNK_SHADER_LOC_MATRIX_MODEL], matModel); + + // Get model-view matrix + matModelView = MatrixMultiply(matModel, matView); + + // Upload model normal matrix (if locations available) + if (material.shader.locs[CHUNK_SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[CHUNK_SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel))); + //----------------------------------------------------- + + // Bind active texture maps (if available) + for (int i = 0; i < CHUNK_MAX_MATERIAL_MAPS; i++) + { + if (material.maps[i].texture.id > 0) + { + // Select current shader texture slot + rlActiveTextureSlot(i); + + // Enable texture for active slot + if ((i == MATERIAL_MAP_IRRADIANCE) || + (i == MATERIAL_MAP_PREFILTER) || + (i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id); + else rlEnableTexture(material.maps[i].texture.id); + + rlSetUniform(material.shader.locs[CHUNK_SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1); + } + } + + // Try binding vertex array objects (VAO) or use VBOs if not possible + // WARNING: UploadMesh() enables all vertex attributes available in mesh and sets default attribute values + // for shader expected vertex attributes that are not provided by the mesh (i.e. colors) + // This could be a dangerous approach because different meshes with different shaders can enable/disable some attributes + if (!rlEnableVertexArray(mesh.vaoId)) + { + // Bind mesh VBO data: vertex position (shader-location = 0) + rlEnableVertexBuffer(mesh.vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]); + rlSetVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_POSITION]); + + // Bind mesh VBO data: vertex texcoords (shader-location = 1) + rlEnableVertexBuffer(mesh.vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]); + rlSetVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_TEXCOORD], 2, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_TEXCOORD]); + + // Bind mesh VBO data: vertex normal (shader-location = 2) + rlEnableVertexBuffer(mesh.vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]); + rlSetVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_NORMAL]); + + // Bind mesh VBO data: vertex tiletexcoords (shader-location = 3) + rlEnableVertexBuffer(mesh.vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TILETEXCOORD]); + rlSetVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_TILETEXCOORD], 2, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_TILETEXCOORD]); + + // Bind mesh VBO data: vertex metadata1 (shader-location = 4) + rlEnableVertexBuffer(mesh.vboId[CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_METADATA1]); + rlSetVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_METADATA1], 4, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(material.shader.locs[CHUNK_SHADER_LOC_VERTEX_METADATA1]); + } + + int eyeCount = 1; + if (rlIsStereoRenderEnabled()) eyeCount = 2; + + for (int eye = 0; eye < eyeCount; eye++) + { + // Calculate model-view-projection matrix (MVP) + Matrix matModelViewProjection = MatrixIdentity(); + if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection); + else + { + // Setup current eye viewport (half screen width) + rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight()); + matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye)); + } + + // Send combined model-view-projection matrix to shader + rlSetUniformMatrix(material.shader.locs[CHUNK_SHADER_LOC_MATRIX_MVP], matModelViewProjection); + + // Draw mesh + rlDrawVertexArray(0, mesh.vertexCount); + } + + // Unbind all bound texture maps + for (int i = 0; i < CHUNK_MAX_MATERIAL_MAPS; i++) + { + if (material.maps[i].texture.id > 0) + { + // Select current shader texture slot + rlActiveTextureSlot(i); + + // Disable texture for active slot + if ((i == MATERIAL_MAP_IRRADIANCE) || + (i == MATERIAL_MAP_PREFILTER) || + (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap(); + else rlDisableTexture(); + } + } + + // Disable all possible vertex array objects (or VBOs) + rlDisableVertexArray(); + rlDisableVertexBuffer(); + rlDisableVertexBufferElement(); + + // Disable shader program + rlDisableShader(); + + // Restore rlgl internal modelview and projection matrices + rlSetMatrixModelview(matView); + rlSetMatrixProjection(matProjection); +#endif +} + +// Load shader from files and bind default locations +// NOTE: If shader string is NULL, using default vertex/fragment shaders +Shader LoadChunkShader(const char *vsFileName, const char *fsFileName) +{ + Shader shader = { 0 }; + + char *vShaderStr = NULL; + char *fShaderStr = NULL; + + if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName); + if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName); + + if ((vShaderStr == NULL) && (fShaderStr == NULL)) printf("CHUNK SHADER: Shader files provided are not valid, using default shader\n"); + + shader = LoadChunkShaderFromMemory(vShaderStr, fShaderStr); + + UnloadFileText(vShaderStr); + UnloadFileText(fShaderStr); + + printf("CHUNK SHADER: Shader loaded successfully from shader files\n"); + return shader; +} + +// Load shader from code strings and bind default locations +Shader LoadChunkShaderFromMemory(const char *vsCode, const char *fsCode) +{ + Shader shader = { 0 }; + + shader.id = rlLoadShaderCode(vsCode, fsCode); + + if (shader.id == rlGetShaderIdDefault()) shader.locs = rlGetShaderLocsDefault(); + else if (shader.id > 0) + { + // After custom shader loading, we TRY to set default location names + // Default shader attribute locations have been binded before linking: + // vertex position location = 0 + // vertex texcoord location = 1 + // vertex normal location = 2 + // vertex tiletexcoord location = 3 + // vertex metadata1 location = 4 + + // NOTE: If any location is not found, loc point becomes -1 + + shader.locs = (int *)RL_CALLOC(CHUNK_MAX_SHADER_LOCATIONS, sizeof(int)); + + // All locations reset to -1 (no location) + for (int i = 0; i < CHUNK_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; + + // Get handles to GLSL input attribute locations + shader.locs[CHUNK_SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_POSITION); + shader.locs[CHUNK_SHADER_LOC_VERTEX_TEXCOORD] = rlGetLocationAttrib(shader.id, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); + shader.locs[CHUNK_SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_NORMAL); + shader.locs[CHUNK_SHADER_LOC_VERTEX_TILETEXCOORD] = rlGetLocationAttrib(shader.id, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_TILETEXCOORD); + shader.locs[CHUNK_SHADER_LOC_VERTEX_METADATA1] = rlGetLocationAttrib(shader.id, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_METADATA1); + + // Get handles to GLSL uniform locations (vertex shader) + shader.locs[CHUNK_SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_UNIFORM_NAME_MVP); + shader.locs[CHUNK_SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_UNIFORM_NAME_VIEW); + shader.locs[CHUNK_SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION); + shader.locs[CHUNK_SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_UNIFORM_NAME_MODEL); + shader.locs[CHUNK_SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_UNIFORM_NAME_NORMAL); + + // Get handles to GLSL uniform locations (fragment shader) + shader.locs[CHUNK_SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_UNIFORM_NAME_COLOR); + shader.locs[CHUNK_SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0); // SHADER_LOC_MAP_ALBEDO + shader.locs[CHUNK_SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1); // SHADER_LOC_MAP_METALNESS + shader.locs[CHUNK_SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, CHUNK_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2); + } + + return shader; +} + +#ifdef RLGL_IMPLEMENTATION + +// Load custom shader strings and return program id +unsigned int chunkLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) +{ + unsigned int program = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + GLint success = 0; + program = glCreateProgram(); + + glAttachShader(program, vShaderId); + glAttachShader(program, fShaderId); + + // NOTE: Default attribute shader locations must be Bound before linking + glBindAttribLocation(program, CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_POSITION); + glBindAttribLocation(program, CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); + glBindAttribLocation(program, CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_NORMAL); + glBindAttribLocation(program, CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_TILETEXCOORD, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_TILETEXCOORD); + glBindAttribLocation(program, CHUNK_DEFAULT_SHADER_ATTRIB_LOCATION_METADATA1, CHUNK_DEFAULT_SHADER_ATTRIB_NAME_METADATA1); + + // NOTE: If some attrib name is no found on the shader, it locations becomes -1 + + glLinkProgram(program); + + // NOTE: All uniform variables are intitialised to 0 when a program links + + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + printf("SHADER: [ID %i] Failed to link shader program\n", program); + + int maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + if (maxLength > 0) + { + int length = 0; + char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); + glGetProgramInfoLog(program, maxLength, &length, log); + printf("SHADER: [ID %i] Link error: %s\n", program, log); + RL_FREE(log); + } + + glDeleteProgram(program); + + program = 0; + } + else + { + // Get the size of compiled shader program (not available on OpenGL ES 2.0) + // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero + //GLint binarySize = 0; + //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); + + printf(RL_LOG_INFO, "SHADER: [ID %i] Program shader loaded successfully\n", program); + } +#endif +} + + +// Load shader from code strings +// NOTE: If shader string is NULL, using default vertex/fragment shaders +unsigned int chunkLoadShaderCode(const char *vsCode, const char *fsCode) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int vertexShaderId = 0; + unsigned int fragmentShaderId = 0; + + // Compile vertex shader (if provided) + // NOTE: If not vertex shader is provided, use default one + if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER); + else { + printf("vertex shader is null!\n"); + exit(1); + } + + // Compile fragment shader (if provided) + // NOTE: If not vertex shader is provided, use default one + if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); + else { + printf("fragment shader is null!\n"); + exit(1); + } + + if ((vertexShaderId > 0) && (fragmentShaderId > 0)) + { + // One of or both shader are new, we need to compile a new shader program + id = chunkLoadShaderProgram(vertexShaderId, fragmentShaderId); + + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, vertexShaderId); + glDeleteShader(vertexShaderId); + + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, fragmentShaderId); + glDeleteShader(fragmentShaderId); + + // In case shader program loading failed + if (id == 0) + { + printf("failed to load custom shaders!\n"); + exit(1); + } + } +#endif + + return id; +} +#endif // RLGL_IMPLEMENTATION diff --git a/src/lib_helpers/raylib_helper.zig b/src/lib_helpers/raylib_helper.zig index 6071cb7..3e5b951 100644 --- a/src/lib_helpers/raylib_helper.zig +++ b/src/lib_helpers/raylib_helper.zig @@ -1,6 +1,5 @@ pub const raylib = @cImport({ - @cInclude("raylib.h"); - @cInclude("raymath.h"); + @cInclude("raylib_extension.h"); }); pub const v3 = struct { diff --git a/src/main.zig b/src/main.zig index bdf5a56..ea51aba 100644 --- a/src/main.zig +++ b/src/main.zig @@ -50,9 +50,9 @@ pub fn main() !void { raylib.SetWindowState(raylib.FLAG_FULLSCREEN_MODE); var camera = raylib.Camera3D{ - .position = raylib.Vector3{ .x = 0, .y = 0, .z = 0 }, + .position = raylib.Vector3{ .x = 0, .y = 8, .z = 20 }, .up = raylib.Vector3{ .x = 0, .y = 1, .z = 0 }, - .target = raylib.Vector3{ .x = 0, .y = 0, .z = -1 }, + .target = raylib.Vector3{ .x = 0, .y = 8, .z = 19 }, .fovy = 45, .projection = raylib.CAMERA_PERSPECTIVE, }; @@ -64,8 +64,17 @@ pub fn main() !void { defer raylib.UnloadTexture(texture); raylib.UnloadImage(tiles); - const shader = raylib.LoadShader("resources/shaders/tiling.vs", "resources/shaders/tiling.fs"); + const shader = raylib.LoadChunkShader("resources/shaders/tiling.vs", "resources/shaders/tiling.fs"); defer raylib.UnloadShader(shader); + //std.debug.print("shader id: {}\n", .{shader.id}); + //std.debug.print("shader attrib position loc: {}\n", .{raylib.GetShaderLocationAttrib(shader, "vertexPosition")}); + //std.debug.print("shader attrib texcoord loc: {}\n", .{raylib.GetShaderLocationAttrib(shader, "vertexTexCoord")}); + //std.debug.print("shader attrib normal loc: {}\n", .{raylib.GetShaderLocationAttrib(shader, "vertexNormal")}); + //std.debug.print("shader attrib tiletexcoord loc: {}\n", .{raylib.GetShaderLocationAttrib(shader, "vertexTileTexCoord")}); + //std.debug.print("shader attrib metadata1 loc: {}\n", .{raylib.GetShaderLocationAttrib(shader, "vertexMetadata1")}); + //for (0..32) |i|{ + // std.debug.print("shader loc {}: {}\n", .{i, shader.locs[i]}); + //} raylib.SetShaderValue(shader, raylib.GetShaderLocation(shader, "textureTiling"), &.{ @as(f32, @floatFromInt(tile_columns)), @as(f32, @floatFromInt(tile_rows)) }, raylib.SHADER_UNIFORM_VEC2); var chunk = try chunks.Chunk.init(a7r); @@ -80,7 +89,7 @@ pub fn main() !void { const xf: f32 = @floatFromInt(raw_x); const yf: f32 = @floatFromInt(raw_y); const zf: f32 = @floatFromInt(raw_z); - const height: f32 = (height_generator.noise2(xf, zf) + 1) * 16; + const height: f32 = (height_generator.noise2(xf, zf) + 1) * 16 + @as(f32, if(x > 24) 4.0 else 0.0) + @as(f32, if(z < 8) 4.0 else 0.0); const tile_type: u32 = if (tile_type_generator.noise3(xf, yf, zf) > 0) 1 else 2; if (height >= yf) chunk.setTile(x, y, z, tile_type); }; @@ -89,8 +98,8 @@ pub fn main() !void { var tmp: u64 = 0; for (0..500) |_| { const start = try std.time.Instant.now(); - const model = raylib.LoadModelFromMesh(try chunk.createMesh(tile_rows, tile_columns)); - defer raylib.UnloadModel(model); + const model = raylib.LoadChunkModelFromMesh(try chunk.createMesh(tile_rows, tile_columns)); + defer raylib.UnloadChunkModel(model); const end = try std.time.Instant.now(); tmp += end.since(start); } @@ -99,8 +108,8 @@ pub fn main() !void { }); } - const model = raylib.LoadModelFromMesh(try chunk.createMesh(tile_rows, tile_columns)); - defer raylib.UnloadModel(model); + const model = raylib.LoadChunkModelFromMesh(try chunk.createMesh(tile_rows, tile_columns)); + defer raylib.UnloadChunkModel(model); model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE].texture = texture; model.materials[0].shader = shader; @@ -108,7 +117,7 @@ pub fn main() !void { raylib.ClearBackground(raylib.BLACK); const right = v3.neg(v3.nor(v3.cross(camera.up, v3.sub(camera.target, camera.position)))); - const forward = v3.cross(right, v3.new(0, -1, 0)); + const forward = v3.cross(right, v3.neg(camera.up)); const speed = @as(f32, if (raylib.IsKeyDown(raylib.KEY_LEFT_CONTROL)) 25 else 5) * raylib.GetFrameTime(); var movement = v3.new(0, 0, 0); @@ -124,8 +133,11 @@ pub fn main() !void { moveCamera(&camera, v3.scl(v3.nor(movement), speed)); const delta = raylib.GetMouseDelta(); - camera.target = v3.add(camera.position, v3.rotate(v3.sub(camera.target, camera.position), v3.new(0, 1, 0), -0.005 * delta.x)); - camera.target = v3.add(camera.position, v3.rotate(v3.sub(camera.target, camera.position), right, -0.005 * delta.y)); + // on the first mouse movement, for some reason mouse delta is completely insane, so we just ignore too large deltas + if(delta.x < 1000 and delta.y < 1000 and delta.x > -1000 and delta.y > -1000){ + camera.target = v3.add(camera.position, v3.rotate(v3.sub(camera.target, camera.position), camera.up, -0.005 * delta.x)); + camera.target = v3.add(camera.position, v3.rotate(v3.sub(camera.target, camera.position), right, -0.005 * delta.y)); + } raylib.BeginDrawing(); defer raylib.EndDrawing(); @@ -138,7 +150,7 @@ pub fn main() !void { raylib.BeginShaderMode(shader); defer raylib.EndShaderMode(); - raylib.DrawModel(model, model_position, 0.5, raylib.WHITE); + raylib.DrawChunkModel(model, model_position, 0.5, raylib.WHITE); } raylib.DrawFPS(10, 10); diff --git a/src/world/chunk.zig b/src/world/chunk.zig index 640f537..446a765 100644 --- a/src/world/chunk.zig +++ b/src/world/chunk.zig @@ -4,7 +4,6 @@ const raylib = raylib_helper.raylib; const v3 = raylib_helper.v3; const A7r = std.mem.Allocator; const comptimePrint = std.fmt.comptimePrint; - const VERTICES_BLOCK_SIZE = 2 * 3 * 3; const TEXCOORDS_BLOCK_SIZE = 2 * 2 * 3; @@ -43,7 +42,7 @@ const Metadata1 = packed struct { comptime { if (@bitSizeOf(Metadata1) != 128) { - @compileError(comptimePrint("Metadata 1 has wrong size. Expected 128 bits, found {}", .{@bitSizeOf(Metadata1)})); + @compileError(comptimePrint("Metadata 1 has wrong size. Expected 128 bytes, found {}", .{@bitSizeOf(Metadata1)})); } } @@ -91,7 +90,18 @@ pub const Chunk = struct { } // Create a raw quad with specified parameters and surface, accounting for dimension and sign. Surface is the block ID. - fn pack_raw_quad(x: f32, y_start: usize, y_end: usize, z_start: usize, z_end: usize, sign: comptime_int, d: comptime_int, surface: u32) RawQuad { + fn pack_raw_quad( + x: f32, + y_start: usize, y_end: usize, + z_start: usize, z_end: usize, + sign: comptime_int, + d: comptime_int, + surface: u32, + y_minus_obscuring_pattern: u32, + y_plus_obscuring_pattern: u32, + z_minus_obscuring_pattern: u32, + z_plus_obscuring_pattern: u32, + ) RawQuad { const ymin: f32 = @as(f32, @floatFromInt(y_start)) - 0.5; const ymax: f32 = @as(f32, @floatFromInt(y_end)) - 0.5; const zmin: f32 = @as(f32, @floatFromInt(z_start)) - 0.5; @@ -113,10 +123,10 @@ pub const Chunk = struct { .width = zmax - zmin, .height = ymax - ymin, - .top_obscuring_pattern = 0, - .left_obscuring_pattern = 0, - .right_obscuring_pattern = 0, - .bottom_obscuring_pattern = 0, + .top_obscuring_pattern = z_plus_obscuring_pattern, + .left_obscuring_pattern = z_minus_obscuring_pattern, + .right_obscuring_pattern = y_plus_obscuring_pattern, + .bottom_obscuring_pattern = y_minus_obscuring_pattern, .top_left_obscured = false, .top_right_obscured = false, .bottom_right_obscured = false, @@ -155,10 +165,10 @@ pub const Chunk = struct { .width = zmax - zmin, .height = ymax - ymin, - .top_obscuring_pattern = 0xffffffff, - .left_obscuring_pattern = 0xffffffff, - .right_obscuring_pattern = 0xffffffff, - .bottom_obscuring_pattern = 0xffffffff, + .top_obscuring_pattern = 0, + .left_obscuring_pattern = 0, + .right_obscuring_pattern = 0, + .bottom_obscuring_pattern = 0, .top_left_obscured = false, .top_right_obscured = false, .bottom_right_obscured = false, @@ -171,7 +181,7 @@ pub const Chunk = struct { } // Create mesh of a chunk. tile_rows and tile_columns are the dimensions of the tiles.png file, in terms of individual tile textures. - pub fn createMesh(chunk: Chunk, tile_rows: u32, tile_columns: u32) !raylib.Mesh { + pub fn createMesh(chunk: Chunk, tile_rows: u32, tile_columns: u32) !raylib.ChunkMesh { var raw_quads = try std.ArrayList(RawQuad).initCapacity(chunk.a7r, 4096); defer raw_quads.deinit(); @@ -200,6 +210,7 @@ pub const Chunk = struct { // The end coordinates of the quad. The quad is therefore covers rectangle from start coordinates (inclusive) to end coordinates (exclusive). var y_end = y_start + 1; var z_end = z_start + 1; + // todo: meshing can be optimized with SIMD stuff!! // Greedy meshing: Extend the quad in the +y direction, until we hit a tile of a different type or the end of the chunk. while (y_end <= 31 and tile_surfaces[y_end][z_start] == surface) : (y_end += 1) { tile_surfaces[y_end][z_start] = 0; @@ -209,8 +220,41 @@ pub const Chunk = struct { for (y_start..y_end) |y| if (tile_surfaces[y][z_end] != surface) break :zloop; // Stop extending if we hit a tile of incorrect type. for (y_start..y_end) |y| tile_surfaces[y][z_end] = 0; } - // todo: scan tiles around quad surface for ambient occlusion - const raw_quad = pack_raw_quad(@floatFromInt(raw_x), y_start, y_end, z_start, z_end, sign, d, surface); + // Scan the line of tiles adjacent to the quad for ambient occlusion + var z_minus_obscuring_pattern: u32 = 0; + if (x != (if (sign == 1) 31 else 0) and z_start != 0) { + for (y_start..y_end) |raw_y| { + const y: u5 = @intCast(raw_y); + z_minus_obscuring_pattern <<= 1; + if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, y, @intCast(z_start - 1), d) != 0) z_minus_obscuring_pattern |= 1; + } + } + var z_plus_obscuring_pattern: u32 = 0; + if (x != (if (sign == 1) 31 else 0) and z_end != 32) { + for (y_start..y_end) |raw_y| { + const y: u5 = @intCast(raw_y); + z_plus_obscuring_pattern <<= 1; + if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, y, @intCast(z_end), d) != 0) z_plus_obscuring_pattern |= 1; + } + } + var y_minus_obscuring_pattern: u32 = 0; + if (x != (if (sign == 1) 31 else 0) and y_start != 0) { + for (z_start..z_end) |raw_z| { + const z: u5 = @intCast(raw_z); + y_minus_obscuring_pattern <<= 1; + if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, @intCast(y_start - 1), z, d) != 0) y_minus_obscuring_pattern |= 1; + } + } + var y_plus_obscuring_pattern: u32 = 0; + if (x != (if (sign == 1) 31 else 0) and y_end != 32) { + for (z_start..z_end) |raw_z| { + const z: u5 = @intCast(raw_z); + y_plus_obscuring_pattern <<= 1; + if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, @intCast(y_end), z, d) != 0) y_plus_obscuring_pattern |= 1; + } + } + // std.debug.print("{}\n", .{z_minus_obscuring_pattern}); + const raw_quad = pack_raw_quad(@floatFromInt(raw_x), y_start, y_end, z_start, z_end, sign, d, surface, y_minus_obscuring_pattern, y_plus_obscuring_pattern, z_minus_obscuring_pattern, z_plus_obscuring_pattern); try raw_quads.append(raw_quad); }; } @@ -224,7 +268,7 @@ pub const Chunk = struct { const vertices: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 3))); const texcoords: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 2))); - const texcoords2: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 2))); + const tiletexcoords: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 2))); const normals: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 3))); const metadata1_packed: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 4))); @@ -249,8 +293,8 @@ pub const Chunk = struct { const vertex_corners = .{ raw_quad.top_left, raw_quad.bottom_left, raw_quad.top_right, raw_quad.bottom_right, raw_quad.top_right, raw_quad.bottom_left }; const texcoords_x = .{ left_uv, left_uv, right_uv, right_uv, right_uv, left_uv }; const texcoords_y = .{ top_uv, bottom_uv } ** 3; - const texcoords2_x = .{ 0.0, 0.0, raw_quad.width, raw_quad.width, raw_quad.width, 0.0 }; - const texcoords2_y = .{ 0.0, raw_quad.height } ** 3; + const tiletexcoords_x = .{ 0.0, 0.0, raw_quad.width, raw_quad.width, raw_quad.width, 0.0 }; + const tiletexcoords_y = .{ 0.0, raw_quad.height } ** 3; inline for (0..6) |corner_id| { vertices[VERTICES_BLOCK_SIZE * i + corner_id * 3 + 0] = vertex_corners[corner_id].x; @@ -258,8 +302,8 @@ pub const Chunk = struct { vertices[VERTICES_BLOCK_SIZE * i + corner_id * 3 + 2] = vertex_corners[corner_id].z; texcoords[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 0] = texcoords_x[corner_id]; texcoords[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 1] = texcoords_y[corner_id]; - texcoords2[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 0] = texcoords2_x[corner_id]; - texcoords2[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 1] = texcoords2_y[corner_id]; + tiletexcoords[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 0] = tiletexcoords_x[corner_id]; + tiletexcoords[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 1] = tiletexcoords_y[corner_id]; } // Store metadata into OpenGL buffers. @@ -296,21 +340,21 @@ pub const Chunk = struct { } // Create mesh using the buffers. - var mesh = raylib.Mesh{ + var mesh = raylib.ChunkMesh{ .triangleCount = triangle_count, .vertexCount = triangle_count * 3, .vertices = vertices, .texcoords = texcoords, - .texcoords2 = texcoords2, + .tiletexcoords = tiletexcoords, .normals = normals, - .tangents = metadata1_packed, + .metadata1 = metadata1_packed, .vaoId = 0, .vboId = null, }; - raylib.UploadMesh(@ptrCast(&mesh), false); + raylib.UploadChunkMesh(@ptrCast(&mesh), false); return mesh; }