transfered from codeberg
This commit is contained in:
66
source_code/block_module/block/_internal/private_block.c
Normal file
66
source_code/block_module/block/_internal/private_block.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "private_block.h"
|
||||
|
||||
#include "texture_atlas_mapper.h"
|
||||
|
||||
/**
|
||||
* The global registry of block properties and rendering data.
|
||||
* * This array serves as the "Source of Truth" for every block type in the game.
|
||||
* It defines the physical properties (transparency), the vertex colors,
|
||||
* and the specific texture indices for each of the six cube faces.
|
||||
* * The array is indexed using the 'block_id_struct' enum values to ensure
|
||||
* O(1) look-up time for block data during mesh generation and lighting.
|
||||
*/
|
||||
const block_render_data_struct _block_library[] =
|
||||
{
|
||||
[block_air] = {
|
||||
.color = { 0, 0, 0, 0 },
|
||||
.is_transparent = true,
|
||||
.texture = {
|
||||
.top = 0, .bottom = 0,
|
||||
.front = 0, .back = 0,
|
||||
.left = 0, .right = 0
|
||||
}
|
||||
},
|
||||
|
||||
[block_dirt] = {
|
||||
.color = { 255, 255, 255, 255 },
|
||||
.is_transparent = false,
|
||||
.texture = {
|
||||
.top = dirt_texture, .bottom = dirt_texture,
|
||||
.front = dirt_texture, .back = dirt_texture,
|
||||
.left = dirt_texture, .right = dirt_texture
|
||||
}
|
||||
},
|
||||
|
||||
[block_grass] = {
|
||||
.color = { 255, 255, 255, 255 },
|
||||
.is_transparent = false,
|
||||
.texture = {
|
||||
.top = grass_top_texture, .bottom = dirt_texture,
|
||||
.front = grass_side_texture, .back = grass_side_texture,
|
||||
.left = grass_side_texture, .right = grass_side_texture
|
||||
}
|
||||
},
|
||||
|
||||
[block_stone] = {
|
||||
.color = { 255, 255, 255, 255 },
|
||||
.is_transparent = false,
|
||||
.texture = {
|
||||
.top = stone_texture, .bottom = stone_texture,
|
||||
.front = stone_texture, .back = stone_texture,
|
||||
.left = stone_texture, .right = stone_texture
|
||||
}
|
||||
},
|
||||
|
||||
[block_wood] = {
|
||||
.color = { 255, 255, 255, 255 },
|
||||
.is_transparent = false,
|
||||
.texture = {
|
||||
.top = wood_texture, .bottom = wood_texture,
|
||||
.front = wood_texture, .back = wood_texture,
|
||||
.left = wood_texture, .right = wood_texture
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
8
source_code/block_module/block/_internal/private_block.h
Normal file
8
source_code/block_module/block/_internal/private_block.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef private_block_h
|
||||
#define private_block_h
|
||||
|
||||
#include "block.h"
|
||||
|
||||
extern const block_render_data_struct _block_library[];
|
||||
|
||||
#endif
|
||||
94
source_code/block_module/block/block.c
Normal file
94
source_code/block_module/block/block.c
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "block.h"
|
||||
#include "_internal/private_block.h"
|
||||
|
||||
#include "texture_atlas_mapper.h"
|
||||
|
||||
/**
|
||||
* Retrieves the rendering data and metadata for a specific block identifier.
|
||||
* * This function acts as a safe accessor for the global block library. It
|
||||
* performs bounds checking on the provided identifier to ensure it exists
|
||||
* within the library's memory range. If the identifier is invalid or
|
||||
* out of bounds, it returns the data for 'block_air' as a safe default
|
||||
* to prevent segmentation faults or undefined rendering behavior.
|
||||
* * @param id_to_look_up The unique identifier of the block to retrieve.
|
||||
* @return The rendering data structure corresponding to the block identifier.
|
||||
*/
|
||||
block_render_data_struct get_block_data(block_id_struct id_to_look_up)
|
||||
{
|
||||
if (id_to_look_up < 0 ||
|
||||
id_to_look_up >= block_type_count)
|
||||
{
|
||||
return _block_library[block_air];
|
||||
}
|
||||
|
||||
return _block_library[id_to_look_up];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the texture coordinates (UVs) for a specific face of a block.
|
||||
* This function maps a block's texture index to a 2D position within a 16x16
|
||||
* texture atlas and handles the V-axis inversion required for OpenGL.
|
||||
*/
|
||||
void get_block_face_texture_coordinates(block_id_struct block_id,
|
||||
int32_t face_direction,
|
||||
float texture_coordinates[4][2])
|
||||
{
|
||||
const int32_t texture_atlas_size = 16;
|
||||
int32_t texture_index = 0;
|
||||
|
||||
/* Retrieve the specific texture index for the requested face */
|
||||
switch (face_direction)
|
||||
{
|
||||
case face_top:
|
||||
texture_index = get_block_data(block_id).texture.top;
|
||||
break;
|
||||
case face_bottom:
|
||||
texture_index = get_block_data(block_id).texture.bottom;
|
||||
break;
|
||||
case face_front:
|
||||
texture_index = get_block_data(block_id).texture.front;
|
||||
break;
|
||||
case face_back:
|
||||
texture_index = get_block_data(block_id).texture.back;
|
||||
break;
|
||||
case face_left:
|
||||
texture_index = get_block_data(block_id).texture.left;
|
||||
break;
|
||||
case face_right:
|
||||
texture_index = get_block_data(block_id).texture.right;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine the grid position within the atlas */
|
||||
const int32_t atlas_column = texture_index % texture_atlas_size;
|
||||
const int32_t atlas_row = texture_index / texture_atlas_size;
|
||||
|
||||
/* Calculate the normalized coordinate steps */
|
||||
const float coordinate_step = 1.0f / (float)texture_atlas_size;
|
||||
|
||||
const float horizontal_start = (float)atlas_column * coordinate_step;
|
||||
const float vertical_start = (float)atlas_row * coordinate_step;
|
||||
|
||||
/* Invert the vertical axis to align with OpenGL's bottom-left origin */
|
||||
const float vertical_minimum = 1.0f - (vertical_start + coordinate_step);
|
||||
const float vertical_maximum = 1.0f - vertical_start;
|
||||
|
||||
const float horizontal_end = horizontal_start + coordinate_step;
|
||||
|
||||
/* Assign coordinates to the four vertices of the face quad */
|
||||
// Top-Left vertex
|
||||
texture_coordinates[0][0] = horizontal_start;
|
||||
texture_coordinates[0][1] = vertical_maximum;
|
||||
|
||||
// Top-Right vertex
|
||||
texture_coordinates[1][0] = horizontal_end;
|
||||
texture_coordinates[1][1] = vertical_maximum;
|
||||
|
||||
// Bottom-Right vertex
|
||||
texture_coordinates[2][0] = horizontal_end;
|
||||
texture_coordinates[2][1] = vertical_minimum;
|
||||
|
||||
// Bottom-Left vertex
|
||||
texture_coordinates[3][0] = horizontal_start;
|
||||
texture_coordinates[3][1] = vertical_minimum;
|
||||
}
|
||||
51
source_code/block_module/block/block.h
Normal file
51
source_code/block_module/block/block.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef block_h
|
||||
#define block_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <raylib.h>
|
||||
|
||||
typedef enum block_id_struct {
|
||||
block_air = 0,
|
||||
block_dirt = 1,
|
||||
block_grass = 2,
|
||||
block_stone = 3,
|
||||
block_wood = 4,
|
||||
block_type_count
|
||||
} block_id_struct;
|
||||
|
||||
typedef struct block_render_data_struct {
|
||||
struct Color color;
|
||||
bool is_transparent;
|
||||
|
||||
struct {
|
||||
uint8_t top;
|
||||
uint8_t bottom;
|
||||
uint8_t front;
|
||||
uint8_t back;
|
||||
uint8_t left;
|
||||
uint8_t right;
|
||||
} texture;
|
||||
} block_render_data_struct;
|
||||
|
||||
/**
|
||||
* Retrieves the rendering data and metadata for a specific block identifier.
|
||||
* * This function acts as a safe accessor for the global block library. It
|
||||
* performs bounds checking on the provided identifier to ensure it exists
|
||||
* within the library's memory range. If the identifier is invalid or
|
||||
* out of bounds, it returns the data for 'block_air' as a safe default
|
||||
* to prevent segmentation faults or undefined rendering behavior.
|
||||
* * @param id_to_look_up The unique identifier of the block to retrieve.
|
||||
* @return The rendering data structure corresponding to the block identifier.
|
||||
*/
|
||||
block_render_data_struct get_block_data(block_id_struct id_to_look_up);
|
||||
|
||||
/**
|
||||
* Calculates the texture coordinates (UVs) for a specific face of a block.
|
||||
* This function maps a block's texture index to a 2D position within a 16x16
|
||||
* texture atlas and handles the V-axis inversion required for OpenGL.
|
||||
*/
|
||||
void get_block_face_texture_coordinates(block_id_struct block_id,
|
||||
int32_t face_direction,
|
||||
float texture_coordinates[4][2]);
|
||||
|
||||
#endif
|
||||
120
source_code/block_module/block/tests/test_block.c
Normal file
120
source_code/block_module/block/tests/test_block.c
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "test_block.h"
|
||||
#include "../block.h"
|
||||
|
||||
#include "texture_atlas_mapper.h"
|
||||
|
||||
#include "unity.h"
|
||||
|
||||
void test_get_block_data_bounds_checking(void)
|
||||
{
|
||||
// Test way too high
|
||||
block_render_data_struct data = get_block_data(999);
|
||||
TEST_ASSERT_TRUE(data.is_transparent);
|
||||
TEST_ASSERT_EQUAL_INT(0, data.texture.top);
|
||||
|
||||
// Test negative
|
||||
data = get_block_data(-1);
|
||||
TEST_ASSERT_TRUE(data.is_transparent);
|
||||
}
|
||||
|
||||
/* Verify that Grass has different textures for top and bottom */
|
||||
void test_grass_block_texture_indices(void)
|
||||
{
|
||||
block_render_data_struct grass = get_block_data(block_grass);
|
||||
|
||||
// Assert that the top is grass, but the bottom is dirt
|
||||
TEST_ASSERT_EQUAL_INT(grass_top_texture, grass.texture.top);
|
||||
TEST_ASSERT_EQUAL_INT(dirt_texture, grass.texture.bottom);
|
||||
TEST_ASSERT_EQUAL_INT(grass_side_texture, grass.texture.front);
|
||||
}
|
||||
|
||||
void test_get_block_face_texture_coordinates_math(void)
|
||||
{
|
||||
float uvs[4][2];
|
||||
|
||||
// Let's test Dirt.
|
||||
// If dirt_texture is index 2 in your texture_atlas_mapper.h:
|
||||
// atlas_column = 2 % 16 = 2
|
||||
// horizontal_start = 2 * (1.0/16.0) = 0.125
|
||||
|
||||
get_block_face_texture_coordinates(block_dirt, face_front, uvs);
|
||||
|
||||
float expected_start_x = (float)(dirt_texture % 16) * (1.0f / 16.0f);
|
||||
|
||||
// Top-Left X should be the start of the tile
|
||||
TEST_ASSERT_FLOAT_WITHIN(0.0001f, expected_start_x, uvs[0][0]);
|
||||
// Top-Right X should be start + 1/16th
|
||||
TEST_ASSERT_FLOAT_WITHIN(0.0001f, expected_start_x + 0.0625f, uvs[1][0]);
|
||||
}
|
||||
|
||||
void test_block_library_integrity(void)
|
||||
{
|
||||
for (int i = 0; i < block_type_count; i++)
|
||||
{
|
||||
block_render_data_struct data = get_block_data(i);
|
||||
|
||||
if (i == block_air)
|
||||
{
|
||||
TEST_ASSERT_TRUE_MESSAGE(data.is_transparent,
|
||||
"Air must be transparent");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure solid blocks have full opacity
|
||||
if (!data.is_transparent)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_UINT8_MESSAGE(255, data.color.a,
|
||||
"Solid block has invalid alpha");
|
||||
}
|
||||
|
||||
// Ensure texture indices are within a reasonable range (e.g., 256 for a 16x16 atlas)
|
||||
TEST_ASSERT_LESS_THAN_INT_MESSAGE(256, data.texture.top,
|
||||
"Texture index out of atlas range");
|
||||
}
|
||||
}
|
||||
|
||||
void test_get_block_face_uv_winding_and_size(void)
|
||||
{
|
||||
float uvs[4][2];
|
||||
get_block_face_texture_coordinates(block_dirt, face_front, uvs);
|
||||
|
||||
// Verify the width of the UV quad is exactly 1/16th (0.0625)
|
||||
float width = uvs[1][0] - uvs[0][0]; // TopRight.x - TopLeft.x
|
||||
TEST_ASSERT_FLOAT_WITHIN(0.0001f, 0.0625f, width);
|
||||
|
||||
// Verify the height of the UV quad is exactly 1/16th
|
||||
// Note: vertical_maximum - vertical_minimum
|
||||
float height = uvs[0][1] - uvs[3][1]; // TopLeft.y - BottomLeft.y
|
||||
TEST_ASSERT_FLOAT_WITHIN(0.0001f, 0.0625f, height);
|
||||
|
||||
// Verify Winding: Top should be Y-greater than Bottom
|
||||
TEST_ASSERT_MESSAGE(uvs[0][1] > uvs[3][1], "UV mapping is upside down!");
|
||||
|
||||
// Verify Winding: Right should be X-greater than Left
|
||||
TEST_ASSERT_MESSAGE(uvs[1][0] > uvs[0][0], "UV mapping is mirrored horizontally!");
|
||||
}
|
||||
|
||||
void test_stone_faces_are_consistent(void)
|
||||
{
|
||||
float top_uvs[4][2];
|
||||
float front_uvs[4][2];
|
||||
|
||||
get_block_face_texture_coordinates(block_stone, face_top, top_uvs);
|
||||
get_block_face_texture_coordinates(block_stone, face_front, front_uvs);
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_FLOAT(top_uvs[i][0], front_uvs[i][0]);
|
||||
TEST_ASSERT_EQUAL_FLOAT(top_uvs[i][1], front_uvs[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
void run_block_tests(void)
|
||||
{
|
||||
RUN_TEST(test_get_block_data_bounds_checking);
|
||||
RUN_TEST(test_grass_block_texture_indices);
|
||||
RUN_TEST(test_get_block_face_texture_coordinates_math);
|
||||
RUN_TEST(test_block_library_integrity);
|
||||
RUN_TEST(test_get_block_face_uv_winding_and_size);
|
||||
RUN_TEST(test_stone_faces_are_consistent);
|
||||
}
|
||||
18
source_code/block_module/block/tests/test_block.h
Normal file
18
source_code/block_module/block/tests/test_block.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef test_block_h
|
||||
#define test_block_h
|
||||
|
||||
void test_get_block_data_bounds_checking(void);
|
||||
|
||||
void test_grass_block_texture_indices(void);
|
||||
|
||||
void test_get_block_face_texture_coordinates_math(void);
|
||||
|
||||
void test_block_library_integrity(void);
|
||||
|
||||
void test_get_block_face_uv_winding_and_size(void);
|
||||
|
||||
void test_stone_faces_are_consistent(void);
|
||||
|
||||
void run_block_tests(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef texture_atlas_mapper_h
|
||||
#define texture_atlas_mapper_h
|
||||
|
||||
#define dirt_texture 242
|
||||
#define grass_top_texture 240
|
||||
#define grass_side_texture 243
|
||||
#define wood_texture 229
|
||||
#define stone_texture 241
|
||||
|
||||
#define face_top 0
|
||||
#define face_bottom 1
|
||||
#define face_front 2
|
||||
#define face_back 3
|
||||
#define face_left 4
|
||||
#define face_right 5
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user