Merge branch 'master' into master

This commit is contained in:
Ray 2026-02-10 18:10:38 +01:00 • committed by GitHub
commit a81828e910
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 35143 additions and 8199 deletions

View file

@ -4,7 +4,7 @@
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 5.6-dev
* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
*
* Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5)
* Based on previous work by @mrdoob
@ -43,8 +43,10 @@ typedef struct MeshBuilder {
static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 vertices[3]);
static void FreeMeshBuilder(MeshBuilder *mb);
static Mesh BuildMesh(MeshBuilder *mb);
static Mesh GenMeshDecal(Mesh inputMesh, Ray ray);
static Mesh GenMeshDecal(Model inputModel, Matrix projection, float decalSize, float decalOffset);
static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s);
static void FreeDecalMeshData(void) { GenMeshDecal((Model){ .meshCount = -1 }, (Matrix){ 0 }, 0.0f, 0.0f); }
static bool GuiButton(Rectangle rec, const char *label);
//------------------------------------------------------------------------------------
// Program main entry point
@ -69,50 +71,46 @@ int main(void)
// Load character model
Model model = LoadModel("resources/models/obj/character.obj");
// Apply character skin
Texture2D modelTexture = LoadTexture("resources/models/obj/character_diffuse.png");
SetTextureFilter(modelTexture, TEXTURE_FILTER_BILINEAR);
model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = modelTexture;
BoundingBox modelBBox = GetMeshBoundingBox(model.meshes[0]); // Get mesh bounding box
camera.target = Vector3Lerp(modelBBox.min, modelBBox.max, 0.5f);
camera.position = Vector3Scale(modelBBox.max, 1.0f);
camera.position.x *= 0.1f;
float modelSize = fminf(
fminf(fabsf(modelBBox.max.x - modelBBox.min.x), fabsf(modelBBox.max.y - modelBBox.min.y)),
fabsf(modelBBox.max.z - modelBBox.min.z));
camera.position = (Vector3){ 0.0f, modelBBox.max.y*1.2f, modelSize*3.0f };
float decalSize = modelSize*0.25f;
float decalOffset = 0.01f;
Model placementCube = LoadModelFromMesh(GenMeshCube(decalSize, decalSize, decalSize));
placementCube.materials[0].maps[0].color = LIME;
Material decalMaterial = LoadMaterialDefault();
decalMaterial.maps[0].color = YELLOW;
Image decalImage = LoadImage("resources/raylib_logo.png");
ImageResizeNN(&decalImage, decalImage.width/4, decalImage.height/4);
Texture decalTexture = LoadTextureFromImage(decalImage);
UnloadImage(decalImage);
SetTextureFilter(decalTexture, TEXTURE_FILTER_BILINEAR);
decalMaterial.maps[MATERIAL_MAP_DIFFUSE].texture = decalTexture;
decalMaterial.maps[MATERIAL_MAP_DIFFUSE].color = RAYWHITE;
// We're going to use these to build up our decal meshes
// They'll resize automatically as we go, we'll free them at the end
MeshBuilder meshBuilders[2] = { 0 };
bool showModel = true;
Model decalModels[MAX_DECALS] = { 0 };
int decalCount = 0;
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@ -123,8 +121,6 @@ int main(void)
//----------------------------------------------------------------------------------
if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) UpdateCamera(&camera, CAMERA_THIRD_PERSON);
if (IsKeyPressed(KEY_SPACE)) showModel = !showModel;
// Display information about closest hit
RayCollision collision = { 0 };
collision.distance = FLT_MAX;
@ -155,216 +151,24 @@ int main(void)
if (meshHitInfo.hit) collision = meshHitInfo;
}
// Add decal to mesh on hit point
if (collision.hit && IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (decalCount < MAX_DECALS))
{
// Create the transformation to project the decal
Vector3 origin = Vector3Add(collision.point, Vector3Scale(collision.normal, 1.0f));
Matrix splat = MatrixLookAt(collision.point, origin, (Vector3){ 0.0f, 1.0f, 0.0f });
// Spin the placement around a bit
splat = MatrixMultiply(splat, MatrixRotateZ(DEG2RAD*((float)GetRandomValue(-180, 180))));
Matrix splatInv = MatrixInvert(splat);
// Reset the mesh builders
meshBuilders[0].vertexCount = 0;
meshBuilders[1].vertexCount = 0;
// We'll be flip-flopping between the two mesh builders
// Reading from one and writing to the other, then swapping
int mbIndex = 0;
// First pass, just get any triangle inside the bounding box (for each mesh of the model)
for (int meshIndex = 0; meshIndex < model.meshCount; meshIndex++)
Mesh decalMesh = GenMeshDecal(model, splat, decalSize, decalOffset);
if (decalMesh.vertexCount > 0)
{
Mesh mesh = model.meshes[meshIndex];
for (int tri = 0; tri < mesh.triangleCount; tri++)
{
Vector3 vertices[3] = { 0 };
// The way we calculate the vertices of the mesh triangle
// depend on whether the mesh vertices are indexed or not
if (mesh.indices == 0)
{
for (int v = 0; v < 3; v++)
{
vertices[v] = (Vector3){
mesh.vertices[3*3*tri + 3*v + 0],
mesh.vertices[3*3*tri + 3*v + 1],
mesh.vertices[3*3*tri + 3*v + 2]
};
}
}
else
{
for (int v = 0; v < 3; v++)
{
vertices[v] = (Vector3){
mesh.vertices[ 3*mesh.indices[3*tri+0] + v],
mesh.vertices[ 3*mesh.indices[3*tri+1] + v],
mesh.vertices[ 3*mesh.indices[3*tri+2] + v]
};
}
}
// Transform all 3 vertices of the triangle
// and check if they are inside our decal box
int insideCount = 0;
for (int i = 0; i < 3; i++)
{
// To splat space
Vector3 v = Vector3Transform(vertices[i], splat);
if ((fabsf(v.x) < decalSize) || (fabsf(v.y) <= decalSize) || (fabsf(v.z) <= decalSize)) insideCount++;
// We need to keep the transformed vertex
vertices[i] = v;
}
// If any of them are inside, we add the triangle - we'll clip it later
if (insideCount > 0) AddTriangleToMeshBuilder(&meshBuilders[mbIndex], vertices);
}
}
// Clipping time! We need to clip against all 6 directions
Vector3 planes[6] = {
{ 1, 0, 0 },
{ -1, 0, 0 },
{ 0, 1, 0 },
{ 0, -1, 0 },
{ 0, 0, 1 },
{ 0, 0, -1 }
};
for (int face = 0; face < 6; face++)
{
// Swap current model builder (so we read from the one we just wrote to)
mbIndex = 1 - mbIndex;
MeshBuilder *inMesh = &meshBuilders[1 - mbIndex];
MeshBuilder *outMesh = &meshBuilders[mbIndex];
// Reset write builder
outMesh->vertexCount = 0;
float s = 0.5f*decalSize;
for (int i = 0; i < inMesh->vertexCount; i += 3)
{
Vector3 nV1, nV2, nV3, nV4;
float d1 = Vector3DotProduct(inMesh->vertices[ i + 0 ], planes[face] ) - s;
float d2 = Vector3DotProduct(inMesh->vertices[ i + 1 ], planes[face] ) - s;
float d3 = Vector3DotProduct(inMesh->vertices[ i + 2 ], planes[face] ) - s;
int v1Out = (d1 > 0);
int v2Out = (d2 > 0);
int v3Out = (d3 > 0);
// Calculate, how many vertices of the face lie outside of the clipping plane
int total = v1Out + v2Out + v3Out;
switch (total)
{
case 0:
{
// The entire face lies inside of the plane, no clipping needed
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){inMesh->vertices[i], inMesh->vertices[i+1], inMesh->vertices[i+2]});
} break;
case 1:
{
// One vertex lies outside of the plane, perform clipping
if (v1Out)
{
nV1 = inMesh->vertices[i + 1];
nV2 = inMesh->vertices[i + 2];
nV3 = ClipSegment(inMesh->vertices[i], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i], nV2, planes[face], s);
}
if (v2Out)
{
nV1 = inMesh->vertices[i];
nV2 = inMesh->vertices[i + 2];
nV3 = ClipSegment(inMesh->vertices[i + 1], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i + 1], nV2, planes[face], s);
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV3, nV2, nV1});
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV2, nV3, nV4});
break;
}
if (v3Out)
{
nV1 = inMesh->vertices[i];
nV2 = inMesh->vertices[i + 1];
nV3 = ClipSegment(inMesh->vertices[i + 2], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i + 2], nV2, planes[face], s);
}
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV4, nV3, nV2});
} break;
case 2:
{
// Two vertices lies outside of the plane, perform clipping
if (!v1Out)
{
nV1 = inMesh->vertices[i];
nV2 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
}
if (!v2Out)
{
nV1 = inMesh->vertices[i + 1];
nV2 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
}
if (!v3Out)
{
nV1 = inMesh->vertices[i + 2];
nV2 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
}
} break;
case 3: // The entire face lies outside of the plane, so let's discard the corresponding vertices
default: break;
}
}
}
// Now we just need to re-transform the vertices
MeshBuilder *theMesh = &meshBuilders[mbIndex];
// Allocate room for UVs
if (theMesh->vertexCount > 0)
{
theMesh->uvs = (Vector2 *)MemAlloc(sizeof(Vector2)*theMesh->vertexCount);
for (int i = 0; i < theMesh->vertexCount; i++)
{
// Calculate the UVs based on the projected coords
// They are clipped to (-decalSize .. decalSize) and we want them (0..1)
theMesh->uvs[i].x = (theMesh->vertices[i].x/decalSize + 0.5f);
theMesh->uvs[i].y = (theMesh->vertices[i].y/decalSize + 0.5f);
// From splat space to world space
theMesh->vertices[i] = Vector3Transform(theMesh->vertices[i], splatInv);
// Tiny nudge in the normal direction so it renders properly over the mesh
theMesh->vertices[i] = Vector3Add(theMesh->vertices[i], Vector3Scale(collision.normal, decalOffset));
}
// Decal model data ready, create it and add it
int decalIndex = decalCount++;
decalModels[decalIndex] = LoadModelFromMesh(BuildMesh(theMesh));
decalModels[decalIndex].materials[0] = decalMaterial;
decalModels[decalIndex] = LoadModelFromMesh(decalMesh);
decalModels[decalIndex].materials[0].maps[0] = decalMaterial.maps[0];
}
}
//----------------------------------------------------------------------------------
@ -377,64 +181,83 @@ int main(void)
BeginMode3D(camera);
// Draw the model at the origin and default scale
if (showModel) DrawModel(model, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE);
// Draw the decal models
for (int i = 0; i < decalCount; i++) DrawModel(decalModels[i], (Vector3){0}, 1.0f, WHITE);
// If we hit the mesh, draw the box for the decal
// Draw the decal models
for (int i = 0; i < decalCount; i++) DrawModel(decalModels[i], (Vector3){ 0 }, 1.0f, WHITE);
// If we hit the mesh, draw the box for the decal
if (collision.hit)
{
Vector3 origin = Vector3Add(collision.point, Vector3Scale(collision.normal, 1.0f));
Matrix splat = MatrixLookAt(collision.point, origin, (Vector3){0,1,0});
placementCube.transform = MatrixInvert(splat);
DrawModel(placementCube, (Vector3){0}, 1.0f, Fade(WHITE, 0.5f));
DrawModel(placementCube, (Vector3){ 0 }, 1.0f, Fade(WHITE, 0.5f));
}
DrawGrid(10, 10.0f);
EndMode3D();
float yPos = 10;
float x0 = GetScreenWidth() - 300;
float x0 = GetScreenWidth() - 300.0f;
float x1 = x0 + 100;
float x2 = x1 + 100;
DrawText("Vertices", x1, yPos, 10, LIME);
DrawText("Triangles", x2, yPos, 10, LIME);
DrawText("Vertices", (int)x1, (int)yPos, 10, LIME);
DrawText("Triangles", (int)x2, (int)yPos, 10, LIME);
yPos += 15;
int vertexCount = 0;
int triangleCount = 0;
for (int i = 0; i < model.meshCount; i++)
{
vertexCount += model.meshes[i].vertexCount;
triangleCount += model.meshes[i].triangleCount;
}
DrawText("Main model", x0, yPos, 10, LIME);
DrawText(TextFormat("%d", vertexCount), x1, yPos, 10, LIME);
DrawText(TextFormat("%d", triangleCount), x2, yPos, 10, LIME);
DrawText("Main model", (int)x0, (int)yPos, 10, LIME);
DrawText(TextFormat("%d", vertexCount), (int)x1, (int)yPos, 10, LIME);
DrawText(TextFormat("%d", triangleCount), (int)x2, (int)yPos, 10, LIME);
yPos += 15;
for (int i = 0; i < decalCount; i++)
{
DrawText(TextFormat("Decal #%d", i+1), x0, yPos, 10, LIME);
DrawText(TextFormat("%d", decalModels[i].meshes[0].vertexCount), x1, yPos, 10, LIME);
DrawText(TextFormat("%d", decalModels[i].meshes[0].triangleCount), x2, yPos, 10, LIME);
if (i == 20)
{
DrawText("...", (int)x0, (int)yPos, 10, LIME);
yPos += 15;
}
if (i < 20)
{
DrawText(TextFormat("Decal #%d", i+1), (int)x0, (int)yPos, 10, LIME);
DrawText(TextFormat("%d", decalModels[i].meshes[0].vertexCount), (int)x1, (int)yPos, 10, LIME);
DrawText(TextFormat("%d", decalModels[i].meshes[0].triangleCount), (int)x2, (int)yPos, 10, LIME);
yPos += 15;
}
vertexCount += decalModels[i].meshes[0].vertexCount;
triangleCount += decalModels[i].meshes[0].triangleCount;
yPos += 15;
}
DrawText("TOTAL", x0, yPos, 10, LIME);
DrawText(TextFormat("%d", vertexCount), x1, yPos, 10, LIME);
DrawText(TextFormat("%d", triangleCount), x2, yPos, 10, LIME);
DrawText("TOTAL", (int)x0, (int)yPos, 10, LIME);
DrawText(TextFormat("%d", vertexCount), (int)x1, (int)yPos, 10, LIME);
DrawText(TextFormat("%d", triangleCount), (int)x2, (int)yPos, 10, LIME);
yPos += 15;
DrawText("Hold RMB to move camera", 10, 430, 10, GRAY);
DrawText("(c) Character model and texture from kenney.nl", screenWidth - 260, screenHeight - 20, 10, GRAY);
// UI elements
if (GuiButton((Rectangle){ 10, screenHeight - 1000.f, 100, 60 }, showModel ? "Hide Model" : "Show Model")) showModel = !showModel;
if (GuiButton((Rectangle){ 10 + 110, screenHeight - 100.0f, 100, 60 }, "Clear Decals"))
{
// Clear decals, unload all decal models
for (int i = 0; i < decalCount; i++) UnloadModel(decalModels[i]);
decalCount = 0;
}
DrawFPS(10, 10);
EndDrawing();
@ -445,14 +268,13 @@ int main(void)
//--------------------------------------------------------------------------------------
UnloadModel(model);
UnloadTexture(modelTexture);
// TODO: WARNING: This line crashes program on closing
//for (int i = 0; i < decalCount; i++) UnloadModel(decalModels[i]);
// Unload decal models
for (int i = 0; i < decalCount; i++) UnloadModel(decalModels[i]);
UnloadTexture(decalTexture);
FreeMeshBuilder(&meshBuilders[0]);
FreeMeshBuilder(&meshBuilders[1]);
FreeDecalMeshData(); // Free the data for decal generation
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
@ -471,21 +293,21 @@ static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 vertices[3])
{
int newVertexCapacity = (1 + (mb->vertexCapacity/256))*256;
Vector3 *newVertices = (Vector3 *)MemAlloc(newVertexCapacity*sizeof(Vector3));
if (mb->vertexCapacity > 0)
{
memcpy(newVertices, mb->vertices, mb->vertexCount*sizeof(Vector3));
MemFree(mb->vertices);
}
mb->vertices = newVertices;
mb->vertexCapacity = newVertexCapacity;
}
// Add 3 vertices
int index = mb->vertexCount;
mb->vertexCount += 3;
for (int i = 0; i < 3; i++) mb->vertices[index+i] = vertices[i];
}
@ -501,7 +323,7 @@ static void FreeMeshBuilder(MeshBuilder *mb)
static Mesh BuildMesh(MeshBuilder *mb)
{
Mesh outMesh = { 0 };
outMesh.vertexCount = mb->vertexCount;
outMesh.triangleCount = mb->vertexCount/3;
outMesh.vertices = MemAlloc(outMesh.vertexCount*3*sizeof(float));
@ -512,16 +334,16 @@ static Mesh BuildMesh(MeshBuilder *mb)
outMesh.vertices[3*i+0] = mb->vertices[i].x;
outMesh.vertices[3*i+1] = mb->vertices[i].y;
outMesh.vertices[3*i+2] = mb->vertices[i].z;
if (mb->uvs)
{
outMesh.texcoords[2*i+0] = mb->uvs[i].x;
outMesh.texcoords[2*i+1] = mb->uvs[i].y;
}
}
UploadMesh(&outMesh, false);
return outMesh;
}
@ -536,3 +358,248 @@ static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s)
return position;
}
// Generate mesh decals for provided model
static Mesh GenMeshDecal(Model target, Matrix projection, float decalSize, float decalOffset)
{
// We're going to use these to build up our decal meshes
// They'll resize automatically as we go, we'll free them at the end
static MeshBuilder meshBuilders[2] = { 0 };
// Ugly way of telling us to free the static MeshBuilder data
if (target.meshCount == -1)
{
FreeMeshBuilder(&meshBuilders[0]);
FreeMeshBuilder(&meshBuilders[1]);
return (Mesh){ 0 };
}
// We're going to need the inverse matrix
Matrix invProj = MatrixInvert(projection);
// Reset the mesh builders
meshBuilders[0].vertexCount = 0;
meshBuilders[1].vertexCount = 0;
// We'll be flip-flopping between the two mesh builders
// Reading from one and writing to the other, then swapping
int mbIndex = 0;
// First pass, just get any triangle inside the bounding box (for each mesh of the model)
for (int meshIndex = 0; meshIndex < target.meshCount; meshIndex++)
{
Mesh mesh = target.meshes[meshIndex];
for (int tri = 0; tri < mesh.triangleCount; tri++)
{
Vector3 vertices[3] = { 0 };
// The way we calculate the vertices of the mesh triangle
// depend on whether the mesh vertices are indexed or not
if (mesh.indices == 0)
{
for (int v = 0; v < 3; v++)
{
vertices[v] = (Vector3){
mesh.vertices[3*3*tri + 3*v + 0],
mesh.vertices[3*3*tri + 3*v + 1],
mesh.vertices[3*3*tri + 3*v + 2]
};
}
}
else
{
for (int v = 0; v < 3; v++)
{
vertices[v] = (Vector3){
mesh.vertices[ 3*mesh.indices[3*tri+0] + v],
mesh.vertices[ 3*mesh.indices[3*tri+1] + v],
mesh.vertices[ 3*mesh.indices[3*tri+2] + v]
};
}
}
// Transform all 3 vertices of the triangle
// and check if they are inside our decal box
int insideCount = 0;
for (int i = 0; i < 3; i++)
{
// To projection space
Vector3 v = Vector3Transform(vertices[i], projection);
if ((fabsf(v.x) < decalSize) || (fabsf(v.y) <= decalSize) || (fabsf(v.z) <= decalSize)) insideCount++;
// We need to keep the transformed vertex
vertices[i] = v;
}
// If any of them are inside, we add the triangle - we'll clip it later
if (insideCount > 0) AddTriangleToMeshBuilder(&meshBuilders[mbIndex], vertices);
}
}
// Clipping time! We need to clip against all 6 directions
Vector3 planes[6] = {
{ 1, 0, 0 },
{ -1, 0, 0 },
{ 0, 1, 0 },
{ 0, -1, 0 },
{ 0, 0, 1 },
{ 0, 0, -1 }
};
for (int face = 0; face < 6; face++)
{
// Swap current model builder (so we read from the one we just wrote to)
mbIndex = 1 - mbIndex;
MeshBuilder *inMesh = &meshBuilders[1 - mbIndex];
MeshBuilder *outMesh = &meshBuilders[mbIndex];
// Reset write builder
outMesh->vertexCount = 0;
float s = 0.5f*decalSize;
for (int i = 0; i < inMesh->vertexCount; i += 3)
{
Vector3 nV1, nV2, nV3, nV4;
float d1 = Vector3DotProduct(inMesh->vertices[ i + 0 ], planes[face] ) - s;
float d2 = Vector3DotProduct(inMesh->vertices[ i + 1 ], planes[face] ) - s;
float d3 = Vector3DotProduct(inMesh->vertices[ i + 2 ], planes[face] ) - s;
int v1Out = (d1 > 0);
int v2Out = (d2 > 0);
int v3Out = (d3 > 0);
// Calculate, how many vertices of the face lie outside of the clipping plane
int total = v1Out + v2Out + v3Out;
switch (total)
{
case 0:
{
// The entire face lies inside of the plane, no clipping needed
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){inMesh->vertices[i], inMesh->vertices[i+1], inMesh->vertices[i+2]});
} break;
case 1:
{
// One vertex lies outside of the plane, perform clipping
if (v1Out)
{
nV1 = inMesh->vertices[i + 1];
nV2 = inMesh->vertices[i + 2];
nV3 = ClipSegment(inMesh->vertices[i], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i], nV2, planes[face], s);
}
if (v2Out)
{
nV1 = inMesh->vertices[i];
nV2 = inMesh->vertices[i + 2];
nV3 = ClipSegment(inMesh->vertices[i + 1], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i + 1], nV2, planes[face], s);
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV3, nV2, nV1});
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV2, nV3, nV4});
break;
}
if (v3Out)
{
nV1 = inMesh->vertices[i];
nV2 = inMesh->vertices[i + 1];
nV3 = ClipSegment(inMesh->vertices[i + 2], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i + 2], nV2, planes[face], s);
}
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV4, nV3, nV2});
} break;
case 2:
{
// Two vertices lies outside of the plane, perform clipping
if (!v1Out)
{
nV1 = inMesh->vertices[i];
nV2 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
}
if (!v2Out)
{
nV1 = inMesh->vertices[i + 1];
nV2 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
}
if (!v3Out)
{
nV1 = inMesh->vertices[i + 2];
nV2 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
}
} break;
case 3: // The entire face lies outside of the plane, so let's discard the corresponding vertices
default: break;
}
}
}
// Now we just need to re-transform the vertices
MeshBuilder *theMesh = &meshBuilders[mbIndex];
// Allocate room for UVs
if (theMesh->vertexCount > 0)
{
theMesh->uvs = (Vector2 *)MemAlloc(sizeof(Vector2)*theMesh->vertexCount);
for (int i = 0; i < theMesh->vertexCount; i++)
{
// Calculate the UVs based on the projected coords
// They are clipped to (-decalSize .. decalSize) and we want them (0..1)
theMesh->uvs[i].x = (theMesh->vertices[i].x/decalSize + 0.5f);
theMesh->uvs[i].y = (theMesh->vertices[i].y/decalSize + 0.5f);
// Tiny nudge in the normal direction so it renders properly over the mesh
theMesh->vertices[i].z -= decalOffset;
// From projection space to world space
theMesh->vertices[i] = Vector3Transform(theMesh->vertices[i], invProj);
}
// Decal model data ready, create the mesh and return it
return BuildMesh(theMesh);
}
else
{
// Return a blank mesh as there's nothing to add
return (Mesh){ 0 };
}
}
// Button UI element
static bool GuiButton(Rectangle rec, const char *label)
{
Color bgColor = GRAY;
bool pressed = false;
if (CheckCollisionPointRec(GetMousePosition(), rec))
{
bgColor = LIGHTGRAY;
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) pressed = true;
}
DrawRectangleRec(rec, bgColor);
DrawRectangleLinesEx(rec, 2.0f, DARKGRAY);
int fontSize = 10;
int textWidth = MeasureText(label, fontSize);
DrawText(label, (int)(rec.x + rec.width*0.5f - textWidth*0.5f), (int)(rec.y + rec.height*0.5f - fontSize*0.5f), fontSize, DARKGRAY);
return pressed;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Before After
Before After

View file

@ -0,0 +1,116 @@
/*******************************************************************************************
*
* raylib [models] example - directional billboard
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6
*
* Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 Robin (@RobinsAviary)
* Killbot art by patvanmackelberg https://opengameart.org/content/killbot-8-directional under CC0
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include <stdlib.h>
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - directional billboard");
// Set up the camera
Camera camera = { 0 };
camera.position = (Vector3){ 2.0f, 1.0f, 2.0f }; // Starting position
camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Target position
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Up vector
camera.fovy = 45.0f; // FOV
camera.projection = CAMERA_PERSPECTIVE; // Projection type (Standard 3D perspective)
// Load billboard texture
Texture skillbot = LoadTexture("resources/skillbot.png");
// Timer to update animation
float anim_timer = 0.0f;
// Animation frame
unsigned int anim = 0;
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_ORBITAL);
// Update timer with delta time
anim_timer += GetFrameTime();
// Update frame index after a certain amount of time (half a second)
if (anim_timer > 0.5f)
{
anim_timer = 0.0f;
anim += 1;
}
// Reset frame index to zero on overflow
if (anim >= 4) anim = 0;
// Find the current direction frame based on the camera position to the billboard object
float dir = (float)floor(((Vector2Angle((Vector2){ 2.0f, 0.0f }, (Vector2){ camera.position.x, camera.position.z })/PI)*4.0f) + 0.25f);
// Correct frame index if angle is negative
if (dir < 0.0f)
{
dir = 8.0f - (float)abs((int)dir);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawGrid(10, 1.0f);
// Draw billboard pointing straight up to the sky, rotated relative to the camera and offset from the bottom
DrawBillboardPro(camera, skillbot, (Rectangle){ 0.0f + (anim*24.0f), 0.0f + (dir*24.0f), 24.0f, 24.0f }, Vector3Zero(), (Vector3){ 0.0f, 1.0f, 0.0f }, Vector2One(), (Vector2){ 0.5f, 0.0f }, 0, WHITE);
EndMode3D();
// Render various variables for reference
DrawText(TextFormat("animation: %d", anim), 10, 10, 20, DARKGRAY);
DrawText(TextFormat("direction frame: %.0f", dir), 10, 40, 20, DARKGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
// Unload billboard texture
UnloadTexture(skillbot);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -80,18 +80,23 @@ int main(void)
if (playerCellY < 0) playerCellY = 0;
else if (playerCellY >= cubicmap.height) playerCellY = cubicmap.height - 1;
// Check map collisions using image data and player position
// TODO: Improvement: Just check player surrounding cells for collision
for (int y = 0; y < cubicmap.height; y++)
// Check map collisions using image data and player position against surrounding cells only
for (int y = playerCellY - 1; y <= playerCellY + 1; y++)
{
for (int x = 0; x < cubicmap.width; x++)
// Avoid map accessing out of bounds
if ((y >= 0) && (y < cubicmap.height))
{
if ((mapPixels[y*cubicmap.width + x].r == 255) && // Collision: white pixel, only check R channel
(CheckCollisionCircleRec(playerPos, playerRadius,
(Rectangle){ mapPosition.x - 0.5f + x*1.0f, mapPosition.z - 0.5f + y*1.0f, 1.0f, 1.0f })))
for (int x = playerCellX - 1; x <= playerCellX + 1; x++)
{
// Collision detected, reset camera position
camera.position = oldCamPos;
// NOTE: Collision: Only checking R channel for white pixel
if (((x >= 0) && (x < cubicmap.width)) &&
(mapPixels[y*cubicmap.width + x].r == 255) &&
(CheckCollisionCircleRec(playerPos, playerRadius,
(Rectangle){ mapPosition.x - 0.5f + x*1.0f, mapPosition.z - 0.5f + y*1.0f, 1.0f, 1.0f })))
{
// Collision detected, reset camera position
camera.position = oldCamPos;
}
}
}
}

View file

@ -35,171 +35,157 @@
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
const char *voxFileNames[] = {
"resources/models/vox/chr_knight.vox",
"resources/models/vox/chr_sword.vox",
"resources/models/vox/monu9.vox",
"resources/models/vox/fez.vox"
};
const char *voxFileNames[] = {
"resources/models/vox/chr_knight.vox",
"resources/models/vox/chr_sword.vox",
"resources/models/vox/monu9.vox",
"resources/models/vox/fez.vox"
};
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading vox");
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading vox");
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
// Load MagicaVoxel files
Model models[MAX_VOX_FILES] = { 0 };
// Load MagicaVoxel files
Model models[MAX_VOX_FILES] = { 0 };
for (int i = 0; i < MAX_VOX_FILES; i++)
{
// Load VOX file and measure time
double t0 = GetTime()*1000.0;
models[i] = LoadModel(voxFileNames[i]);
double t1 = GetTime()*1000.0;
for (int i = 0; i < MAX_VOX_FILES; i++)
{
// Load VOX file and measure time
double t0 = GetTime()*1000.0;
models[i] = LoadModel(voxFileNames[i]);
double t1 = GetTime()*1000.0;
TraceLog(LOG_WARNING, TextFormat("[%s] File loaded in %.3f ms", voxFileNames[i], t1 - t0));
TraceLog(LOG_INFO, TextFormat("[%s] Model file loaded in %.3f ms", voxFileNames[i], t1 - t0));
// Compute model translation matrix to center model on draw position (0, 0 , 0)
BoundingBox bb = GetModelBoundingBox(models[i]);
Vector3 center = { 0 };
center.x = bb.min.x + (((bb.max.x - bb.min.x)/2));
center.z = bb.min.z + (((bb.max.z - bb.min.z)/2));
// Compute model translation matrix to center model on draw position (0, 0 , 0)
BoundingBox bb = GetModelBoundingBox(models[i]);
Vector3 center = { 0 };
center.x = bb.min.x + (((bb.max.x - bb.min.x)/2));
center.z = bb.min.z + (((bb.max.z - bb.min.z)/2));
Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z);
models[i].transform = matTranslate;
}
Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z);
models[i].transform = matTranslate;
}
int currentModel = 0;
int currentModel = 0;
Vector3 modelpos = { 0 };
Vector3 camerarot = { 0 };
// Load voxel shader
Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/voxel_lighting.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/voxel_lighting.fs", GLSL_VERSION));
// Load voxel shader
Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/voxel_lighting.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/voxel_lighting.fs", GLSL_VERSION));
// Get some required shader locations
shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
// NOTE: "matModel" location name is automatically assigned on shader loading,
// no need to get the location again if using that uniform name
//shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
// Get some required shader locations
shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
// NOTE: "matModel" location name is automatically assigned on shader loading,
// no need to get the location again if using that uniform name
//shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
// Ambient light level (some basic lighting)
int ambientLoc = GetShaderLocation(shader, "ambient");
SetShaderValue(shader, ambientLoc, (float[4]) { 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4);
// Ambient light level (some basic lighting)
int ambientLoc = GetShaderLocation(shader, "ambient");
SetShaderValue(shader, ambientLoc, (float[4]) { 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4);
// Assign out lighting shader to model
for (int i = 0; i < MAX_VOX_FILES; i++)
{
Model m = models[i];
for (int j = 0; j < m.materialCount; j++)
{
m.materials[j].shader = shader;
}
}
// Assign out lighting shader to model
for (int i = 0; i < MAX_VOX_FILES; i++)
{
for (int j = 0; j < models[i].materialCount; j++) models[i].materials[j].shader = shader;
}
// Create lights
Light lights[MAX_LIGHTS] = { 0 };
lights[0] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, -20 }, Vector3Zero(), GRAY, shader);
lights[1] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, 20 }, Vector3Zero(), GRAY, shader);
lights[2] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, 20 }, Vector3Zero(), GRAY, shader);
lights[3] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, -20 }, Vector3Zero(), GRAY, shader);
// Create lights
Light lights[MAX_LIGHTS] = { 0 };
lights[0] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, -20 }, Vector3Zero(), GRAY, shader);
lights[1] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, 20 }, Vector3Zero(), GRAY, shader);
lights[2] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, 20 }, Vector3Zero(), GRAY, shader);
lights[3] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, -20 }, Vector3Zero(), GRAY, shader);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))
{
const Vector2 mouseDelta = GetMouseDelta();
camerarot.x = mouseDelta.x*0.05f;
camerarot.y = mouseDelta.y*0.05f;
}
else
{
camerarot.x = 0;
camerarot.y = 0;
}
//--------------------------------------------------------------------------------------
Vector3 modelpos = { 0 };
Vector3 camerarot = { 0 };
UpdateCameraPro(&camera,
(Vector3){ (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, // Move forward-backward
(IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f, // Move right-left
0.0f }, // Move up-down
camerarot, // Camera rotation
GetMouseWheelMove()*-2.0f); // Move to target (zoom)
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))
{
const Vector2 mouseDelta = GetMouseDelta();
camerarot.x = mouseDelta.x*0.05f;
camerarot.y = mouseDelta.y*0.05f;
}
else
{
camerarot.x = 0;
camerarot.y = 0;
}
// Cycle between models on mouse click
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1)%MAX_VOX_FILES;
UpdateCameraPro(&camera,
(Vector3) {
(IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - // Move forward-backward
(IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f,
(IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - // Move right-left
(IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f,
0.0f // Move up-down
},
camerarot,
GetMouseWheelMove()*-2.0f); // Move to target (zoom)
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
// Cycle between models on mouse click
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1) % MAX_VOX_FILES;
// Update light values (actually, only enable/disable them)
for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]);
//----------------------------------------------------------------------------------
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
// Update light values (actually, only enable/disable them)
for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]);
ClearBackground(RAYWHITE);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
// Draw 3D model
BeginMode3D(camera);
DrawModel(models[currentModel], modelpos, 1.0f, WHITE);
DrawGrid(10, 1.0);
ClearBackground(RAYWHITE);
// Draw spheres to show where the lights are
for (int i = 0; i < MAX_LIGHTS; i++)
{
if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
}
EndMode3D();
// Draw 3D model
BeginMode3D(camera);
// Display info
DrawRectangle(10, 40, 340, 70, Fade(SKYBLUE, 0.5f));
DrawRectangleLines(10, 40, 340, 70, Fade(DARKBLUE, 0.5f));
DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, BLUE);
DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, BLUE);
DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, BLUE);
DrawText(TextFormat("Model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
DrawModel(models[currentModel], modelpos, 1.0f, WHITE);
DrawGrid(10, 1.0);
EndDrawing();
//----------------------------------------------------------------------------------
}
// Draw spheres to show where the lights are
for (int i = 0; i < MAX_LIGHTS; i++)
{
if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
}
// De-Initialization
//--------------------------------------------------------------------------------------
// Unload models data (GPU VRAM)
for (int i = 0; i < MAX_VOX_FILES; i++) UnloadModel(models[i]);
EndMode3D();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
// Display info
DrawRectangle(10, 400, 340, 60, Fade(SKYBLUE, 0.5f));
DrawRectangleLines(10, 400, 340, 60, Fade(DARKBLUE, 0.5f));
DrawText("MOUSE LEFT BUTTON to CYCLE VOX MODELS", 40, 410, 10, BLUE);
DrawText("MOUSE MIDDLE BUTTON to ZOOM OR ROTATE CAMERA", 40, 420, 10, BLUE);
DrawText("UP-DOWN-LEFT-RIGHT KEYS to MOVE CAMERA", 40, 430, 10, BLUE);
DrawText(TextFormat("File: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
// Unload models data (GPU VRAM)
for (int i = 0; i < MAX_VOX_FILES; i++) UnloadModel(models[i]);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
return 0;
}

View file

@ -32,7 +32,7 @@ static Mesh GenMeshPoints(int numPoints);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main()
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
@ -57,7 +57,7 @@ int main()
Mesh mesh = GenMeshPoints(numPoints);
Model model = LoadModelFromMesh(mesh);
//SetTargetFPS(60);
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
@ -92,15 +92,12 @@ int main()
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(BLACK);
BeginMode3D(camera);
// The new method only uploads the points once to the GPU
if (useDrawModelPoints)
{
DrawModelPoints(model, position, 1.0f, WHITE);
}
if (useDrawModelPoints) DrawModelPoints(model, position, 1.0f, WHITE);
else
{
// The old method must continually draw the "points" (lines)
@ -124,17 +121,16 @@ int main()
// Draw a unit sphere for reference
DrawSphereWires(position, 1.0f, 10, 10, YELLOW);
EndMode3D();
// Draw UI text
DrawText(TextFormat("Point Count: %d", numPoints), 20, screenHeight - 50, 40, WHITE);
DrawText("Up - increase points", 20, 70, 20, WHITE);
DrawText("Down - decrease points", 20, 100, 20, WHITE);
DrawText("Space - drawing function", 20, 130, 20, WHITE);
DrawText(TextFormat("Point Count: %d", numPoints), 10, screenHeight - 50, 40, WHITE);
DrawText("UP - Increase points", 10, 40, 20, WHITE);
DrawText("DOWN - Decrease points", 10, 70, 20, WHITE);
DrawText("SPACE - Drawing function", 10, 100, 20, WHITE);
if (useDrawModelPoints) DrawText("Using: DrawModelPoints()", 20, 160, 20, GREEN);
else DrawText("Using: DrawPoint3D()", 20, 160, 20, RED);
if (useDrawModelPoints) DrawText("Using: DrawModelPoints()", 10, 130, 20, GREEN);
else DrawText("Using: DrawPoint3D()", 10, 130, 20, RED);
DrawFPS(10, 10);

View file

@ -148,25 +148,25 @@ void DrawSphereBasic(Color color)
{
for (int j = 0; j < slices; j++)
{
rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180.0f/(rings + 1))*i))*sinf(DEG2RAD*(j*360.0f/slices)),
sinf(DEG2RAD*(270+(180.0f/(rings + 1))*i)),
cosf(DEG2RAD*(270+(180.0f/(rings + 1))*i))*cosf(DEG2RAD*(j*360.0f/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360.0f/slices)),
sinf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1))),
cosf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360.0f/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360.0f/slices)),
sinf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1))),
cosf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360.0f/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*sinf(DEG2RAD*((j+1)*360/slices)),
sinf(DEG2RAD*(270+(180/(rings + 1))*(i))),
cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*cosf(DEG2RAD*((j+1)*360/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180.0f/(rings + 1))*i))*sinf(DEG2RAD*(j*360.0f/slices)),
sinf(DEG2RAD*(270+(180.0f/(rings + 1))*i)),
cosf(DEG2RAD*(270+(180.0f/(rings + 1))*i))*cosf(DEG2RAD*(j*360.0f/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180.0f/(rings + 1))*(i)))*sinf(DEG2RAD*((j+1)*360.0f/slices)),
sinf(DEG2RAD*(270+(180.0f/(rings + 1))*(i))),
cosf(DEG2RAD*(270+(180.0f/(rings + 1))*(i)))*cosf(DEG2RAD*((j+1)*360.0f/slices)));
rlVertex3f(cosf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360.0f/slices)),
sinf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1))),
cosf(DEG2RAD*(270+(180.0f/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360.0f/slices)));
}
}
rlEnd();

View file

@ -5,7 +5,7 @@
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
*
*
* Example contributed by Jopestpe (@jopestpe)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
@ -48,7 +48,7 @@ int main(void)
model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
float rotation = 0.0f;
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@ -59,7 +59,7 @@ int main(void)
//----------------------------------------------------------------------------------
rotation += 1.0f;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
@ -67,13 +67,13 @@ int main(void)
ClearBackground(RAYWHITE);
BeginMode3D(camera);
// Draw model defining: position, size, rotation-axis, rotation (degrees), size, and tint-color
DrawModelEx(model, (Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.5f, 1.0f, 0.0f },
// Draw model defining: position, size, rotation-axis, rotation (degrees), size, and tint-color
DrawModelEx(model, (Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.5f, 1.0f, 0.0f },
rotation, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE);
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);

View file

@ -54,7 +54,8 @@ int main(void)
Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
Model skybox = LoadModelFromMesh(cube);
// Set this to true to use an HDR Texture, Note that raylib must be built with HDR Support for this to work SUPPORT_FILEFORMAT_HDR
// Set this to true to use an HDR Texture
// NOTE: raylib must be built with HDR Support for this to work: SUPPORT_FILEFORMAT_HDR
bool useHDR = false;
// Load skybox shader and set required locations
@ -63,8 +64,8 @@ int main(void)
TextFormat("resources/shaders/glsl%i/skybox.fs", GLSL_VERSION));
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "environmentMap"), (int[1]){ MATERIAL_MAP_CUBEMAP }, SHADER_UNIFORM_INT);
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "doGamma"), (int[1]) { useHDR ? 1 : 0 }, SHADER_UNIFORM_INT);
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "vflipped"), (int[1]){ useHDR ? 1 : 0 }, SHADER_UNIFORM_INT);
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "doGamma"), (int[1]){ useHDR? 1 : 0 }, SHADER_UNIFORM_INT);
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "vflipped"), (int[1]){ useHDR? 1 : 0 }, SHADER_UNIFORM_INT);
// Load cubemap shader and setup required shader locations
Shader shdrCubemap = LoadShader(TextFormat("resources/shaders/glsl%i/cubemap.vs", GLSL_VERSION),
@ -91,9 +92,11 @@ int main(void)
}
else
{
Image img = LoadImage("resources/skybox.png");
skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT); // CUBEMAP_LAYOUT_PANORAMA
UnloadImage(img);
// TODO: WARNING: On PLATFORM_WEB it requires a big amount of memory to process input image
// and generate the required cubemap image to be passed to rlLoadTextureCubemap()
Image image = LoadImage("resources/skybox.png");
skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(image, CUBEMAP_LAYOUT_AUTO_DETECT);
UnloadImage(image);
}
DisableCursor(); // Limit cursor to relative movement inside the window
@ -132,9 +135,9 @@ int main(void)
}
else
{
Image img = LoadImage(droppedFiles.paths[0]);
skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT);
UnloadImage(img);
Image image = LoadImage(droppedFiles.paths[0]);
skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(image, CUBEMAP_LAYOUT_AUTO_DETECT);
UnloadImage(image);
}
TextCopy(skyboxFileName, droppedFiles.paths[0]);

View file

@ -22,7 +22,7 @@
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main()
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
@ -85,9 +85,9 @@ int main()
// Calculate the cube position
Vector3 cubePos = {
(float)(x - numBlocks/2)*(scale*3.0f) + scatter,
(float)(y - numBlocks/2)*(scale*2.0f) + scatter,
(float)(z - numBlocks/2)*(scale*3.0f) + scatter
(float)(x - (float)numBlocks/2)*(scale*3.0f) + scatter,
(float)(y - (float)numBlocks/2)*(scale*2.0f) + scatter,
(float)(z - (float)numBlocks/2)*(scale*3.0f) + scatter
};
// Pick a color with a hue depending on cube position for the rainbow color effect

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB