Watch
2
0
Fork
You've already forked raylib-cs
0

WASM examples (+ backports of new official examples) (#344)

* chg: New build system that uses the officially distributed binaries, bumped version to 8.0.0, simplified git workflow, removed deprecated OpenGL 1.1 functionality.

* chg: Modernize CI workflow, enable SourceLink

- Bump workflow actions to latest majors (Node 24); drop deprecated softprops/action-gh-release@v1
- Trigger push builds on main instead of master
- Create local nuget feed dir before pack (fixes NU1301)
- Enable Microsoft.SourceLink.GitHub for debugging symbols (ref PR #340)

* fix: centralized version data in Directory.build.props, and fixed various interop details that had incorrect function signatures

* chore: updated readme

* fix: version the native extract marker and chain download via DependsOnTargets

The .extracted marker now includes the raylib package name, so bumping
TargetRaylibTag re-extracts the new archive instead of silently keeping
(and packing/copying) the previous version's files.

_PrepareNativeLibrary and _StageWasmNative now depend directly on
_DownloadAndExtractInternal instead of CallTarget-ing it; dependency
targets run in the same project instance, so the resolved properties
(RaylibPackageName etc.) propagate naturally.

* fix: let the binding build for browser-wasm on both net8.0 and net10.0

The net8-era wasm workload (Microsoft.NET.Runtime.WebAssembly.Sdk 8.0.x,
auto-imported for RID browser-wasm) treats every browser-wasm project as
a wasm app: it forces OutputType=Exe after project evaluation (CS5001
for a classlib) and hooks its app-bundle build after Build, which errors
because a library has no assemblies to bundle. Opt Raylib-cs out via
DisableAutoWasmBuildApp (props time, before the workload defaults its
trigger) and pin OutputType back to Library in Directory.Build.targets
(evaluated after the workload props, so the assignment wins). net10's
wasm SDK needs neither workaround.

* chore: readme updated

* chg: simplifying build logic - a simple line in the documentation should save us the code here

* fix: Wrong signature of FrameBufferComplete

* chore: readme update

* feat: samples default to local project reference, and can optionally use the nuget package

* feat: backporting existing raylib-cs examples and new official raylib examples to WASM, adopting raylib's original code style

* chore: readme, gitignore, and targets backport.

* fix: Examples.csproj runs the download task when building locally

* feat: backporting existing raylib-cs examples and new official raylib examples to WASM, adopting raylib's original code style

* chore: readme, gitignore, and targets backport.

* chore: clean up linter warnings

* feat: html harness focuses the example and allows quick navigation with J/K instead.

* chore: readme mentions the property to use nuget vs. the local project reference

* feat: replaced the J/K navigation with good old HTML buttons

* chore: run dotnet format scoped default (was previously scoped to just 'style')
This commit is contained in:
tiger tiger tiger 2026-07-30 19:34:34 +02:00 committed by GitHub
commit 8c22e68c2a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
236 changed files with 40405 additions and 10896 deletions

View file

@ -0,0 +1,387 @@
/*******************************************************************************************
*
* raylib [models] example - animation blend custom
*
* Example complexity rating: [] 4/4
*
* Example originally created with raylib 5.5, last time updated with raylib 6.0
*
* Example contributed by dmitrii-brand (@dmitrii-brand) and reviewed by Ramon Santamaria (@raysan5)
*
* DETAILS: Example demonstrates per-bone animation blending, allowing smooth transitions
* between two animations by interpolating bone transforms. This is useful for:
* - Blending movement animations (walk/run) with action animations (jump/attack)
* - Creating smooth animation transitions
* - Layering animations (e.g., upper body attack while lower body walks)
*
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
* if not enabled, CPU skinning will be used instead
*
* 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) 2026 dmitrii-brand (@dmitrii-brand)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class AnimationBlendCustom : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
private const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Models / Animation Blend Custom";
public string Title => "raylib [models] example - animation blend custom";
private Camera3D camera;
private Model model;
private Vector3 position;
private Shader skinningShader;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex0;
private int animIndex1;
private int animCurrentFrame0;
private int animCurrentFrame1;
private bool upperBodyBlend;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(4.0f, 4.0f, 4.0f); // Camera position
camera.Target = new Vector3(0.0f, 1.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load gltf model
model = LoadModel("resources/models/gltf/greenman.glb");
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model position
// Load skinning shader
// WARNING: GPU skinning must be enabled in raylib with a compilation flag,
// if not enabled, CPU skinning will be used instead
skinningShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/skinning.vs",
$"resources/shaders/glsl{GlslVersion}/skinning.fs"
);
model.Materials[1].Shader = skinningShader;
// Load gltf model animations
animCount = 0;
anims = LoadModelAnimations("resources/models/gltf/greenman.glb", ref animCount);
// Use specific animation indices: 2-walk/move, 3-attack
animIndex0 = 2; // Walk/Move animation (index 2)
animIndex1 = 3; // Attack animation (index 3)
animCurrentFrame0 = 0;
animCurrentFrame1 = 0;
// Validate indices
if (animIndex0 >= animCount)
{
animIndex0 = 0;
}
if (animIndex1 >= animCount)
{
animIndex1 = (animCount > 1) ? 1 : 0;
}
upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50)
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Toggle upper/lower body blending mode (SPACE key)
if (IsKeyPressed(KeyboardKey.Space))
{
upperBodyBlend = !upperBodyBlend;
}
// Update animation frames
var anim0 = anims[animIndex0];
var anim1 = anims[animIndex1];
animCurrentFrame0 = (animCurrentFrame0 + 1) % anim0.KeyFrameCount;
animCurrentFrame1 = (animCurrentFrame1 + 1) % anim1.KeyFrameCount;
// Blend the two animations
// When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0)
// When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack)
var blendFactor = upperBodyBlend ? 1.0f : 0.5f;
UpdateModelAnimationBones(anim0, animCurrentFrame0, anim1, animCurrentFrame1, blendFactor, upperBodyBlend);
// raylib provided animation blending function
//UpdateModelAnimationEx(model, anim0, (float)animCurrentFrame0,
// anim1, (float)animCurrentFrame1, blendFactor);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
// Draw UI
DrawText($"ANIM 0: {anim0.NameToString()}", 10, 10, 20, Color.Gray);
DrawText($"ANIM 1: {anim1.NameToString()}", 10, 40, 20, Color.Gray);
DrawText($"[SPACE] Toggle blending mode: {(upperBodyBlend ? "Upper/Lower Body Blending" : "Uniform Blending")}",
10, GetScreenHeight() - 30, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
}
// Check if a bone is part of upper body (for selective blending)
private static bool IsUpperBodyBone(string boneName)
{
// Common upper body bone names (adjust based on your model)
if (boneName is "spine" or "spine1" or "spine2" or
"chest" or "upperChest" or
"neck" or "head" or
"shoulder" or "shoulder_L" or "shoulder_R" or
"upperArm" or "upperArm_L" or "upperArm_R" or
"lowerArm" or "lowerArm_L" or "lowerArm_R" or
"hand" or "hand_L" or "hand_R" or
"clavicle" or "clavicle_L" or "clavicle_R")
{
return true;
}
// Check if bone name contains upper body keywords
if (boneName.Contains("spine") || boneName.Contains("chest") ||
boneName.Contains("neck") || boneName.Contains("head") ||
boneName.Contains("shoulder") || boneName.Contains("arm") ||
boneName.Contains("hand") || boneName.Contains("clavicle"))
{
return true;
}
return false;
}
// Blend two animations per-bone with selective upper/lower body blending
private unsafe void UpdateModelAnimationBones(ModelAnimation anim0, int frame0,
ModelAnimation anim1, int frame1, float blend, bool upperBodyBlend)
{
// Validate inputs
if ((anim0.BoneCount != 0) && (anim0.KeyframePoses != null) &&
(anim1.BoneCount != 0) && (anim1.KeyframePoses != null) &&
(model.Skeleton.BoneCount != 0) && (model.Skeleton.BindPose != null))
{
// Clamp blend factor to [0, 1]
blend = MathF.Min(1.0f, MathF.Max(0.0f, blend));
// Ensure frame indices are valid
if (frame0 >= anim0.KeyFrameCount)
{
frame0 = anim0.KeyFrameCount - 1;
}
if (frame1 >= anim1.KeyFrameCount)
{
frame1 = anim1.KeyFrameCount - 1;
}
if (frame0 < 0)
{
frame0 = 0;
}
if (frame1 < 0)
{
frame1 = 0;
}
// Get bone count (use minimum of all to be safe)
var boneCount = model.Skeleton.BoneCount;
if (anim0.BoneCount < boneCount)
{
boneCount = anim0.BoneCount;
}
if (anim1.BoneCount < boneCount)
{
boneCount = anim1.BoneCount;
}
// Blend each bone
for (var boneIndex = 0; boneIndex < boneCount; boneIndex++)
{
// Determine blend factor for this bone
var boneBlendFactor = blend;
// If upper body blending is enabled, use different blend factors for upper vs lower body
if (upperBodyBlend)
{
var boneName = model.Skeleton.Bones[boneIndex].NameToString();
var isUpperBody = IsUpperBodyBone(boneName);
// Upper body: use anim1 (attack), Lower body: use anim0 (walk)
// blend = 0.0 means full anim0 (walk), 1.0 means full anim1 (attack)
if (isUpperBody)
{
boneBlendFactor = blend; // Upper body: blend towards anim1 (attack)
}
else
{
boneBlendFactor = 1.0f - blend; // Lower body: blend towards anim0 (walk) - invert the blend
}
}
// Get transforms from both animations
var bindTransform = model.Skeleton.BindPose[boneIndex];
var animTransform0 = anim0.KeyframePoses[frame0][boneIndex];
var animTransform1 = anim1.KeyframePoses[frame1][boneIndex];
// Blend the transforms
Transform blended = new();
blended.Translation = Vector3Lerp(animTransform0.Translation, animTransform1.Translation, boneBlendFactor);
blended.Rotation = QuaternionSlerp(animTransform0.Rotation, animTransform1.Rotation, boneBlendFactor);
blended.Scale = Vector3Lerp(animTransform0.Scale, animTransform1.Scale, boneBlendFactor);
// Convert bind pose to matrix
var bindMatrix = MatrixMultiply(MatrixMultiply(
MatrixScale(bindTransform.Scale.X, bindTransform.Scale.Y, bindTransform.Scale.Z),
QuaternionToMatrix(bindTransform.Rotation)),
MatrixTranslate(bindTransform.Translation.X, bindTransform.Translation.Y, bindTransform.Translation.Z));
// Convert blended transform to matrix
var blendedMatrix = MatrixMultiply(MatrixMultiply(
MatrixScale(blended.Scale.X, blended.Scale.Y, blended.Scale.Z),
QuaternionToMatrix(blended.Rotation)),
MatrixTranslate(blended.Translation.X, blended.Translation.Y, blended.Translation.Z));
// Calculate final bone matrix (similar to UpdateModelAnimationBones)
model.BoneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
}
// CPU skinning, updates CPU buffers and uploads them to GPU (if available)
// NOTE: Fallback in case GPU skinning is not supported or enabled
for (var m = 0; m < model.MeshCount; m++)
{
var mesh = model.Meshes[m];
Vector3 animVertex;
Vector3 animNormal;
var vertexValuesCount = mesh.VertexCount * 3;
var boneCounter = 0;
var bufferUpdateRequired = false; // Flag to check when anim vertex information is updated
// Skip if missing bone data or missing anim buffers initialization
if ((mesh.BoneWeights == null) || (mesh.BoneIndices == null) ||
(mesh.AnimVertices == null) || (mesh.AnimNormals == null))
{
continue;
}
for (var vCounter = 0; vCounter < vertexValuesCount; vCounter += 3)
{
mesh.AnimVertices[vCounter] = 0;
mesh.AnimVertices[vCounter + 1] = 0;
mesh.AnimVertices[vCounter + 2] = 0;
if (mesh.AnimNormals != null)
{
mesh.AnimNormals[vCounter] = 0;
mesh.AnimNormals[vCounter + 1] = 0;
mesh.AnimNormals[vCounter + 2] = 0;
}
// Iterates over 4 bones per vertex
for (var j = 0; j < 4; j++, boneCounter++)
{
var boneWeight = mesh.BoneWeights[boneCounter];
var boneIndex = mesh.BoneIndices[boneCounter];
// Early stop when no transformation will be applied
if (boneWeight == 0.0f)
{
continue;
}
animVertex = new Vector3(mesh.Vertices[vCounter], mesh.Vertices[vCounter + 1], mesh.Vertices[vCounter + 2]);
animVertex = Vector3Transform(animVertex, model.BoneMatrices[boneIndex]);
mesh.AnimVertices[vCounter] += animVertex.X * boneWeight;
mesh.AnimVertices[vCounter + 1] += animVertex.Y * boneWeight;
mesh.AnimVertices[vCounter + 2] += animVertex.Z * boneWeight;
bufferUpdateRequired = true;
// Normals processing
// NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
if ((mesh.Normals != null) && (mesh.AnimNormals != null))
{
animNormal = new Vector3(mesh.Normals[vCounter], mesh.Normals[vCounter + 1], mesh.Normals[vCounter + 2]);
animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.BoneMatrices[boneIndex])));
mesh.AnimNormals[vCounter] += animNormal.X * boneWeight;
mesh.AnimNormals[vCounter + 1] += animNormal.Y * boneWeight;
mesh.AnimNormals[vCounter + 2] += animNormal.Z * boneWeight;
}
}
}
if (bufferUpdateRequired)
{
// Update GPU vertex buffers with updated data (position + normals)
Rlgl.UpdateVertexBuffer(mesh.VboId[(int)ShaderLocationIndex.VertexPosition], mesh.AnimVertices, mesh.VertexCount * 3 * sizeof(float), 0);
if (mesh.Normals != null)
{
Rlgl.UpdateVertexBuffer(mesh.VboId[(int)ShaderLocationIndex.VertexNormal], mesh.AnimNormals, mesh.VertexCount * 3 * sizeof(float), 0);
}
}
}
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation blend custom");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AnimationBlendCustom();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,471 @@
/*******************************************************************************************
*
* raylib [models] example - animation blending
*
* Example complexity rating: [] 4/4
*
* Example originally created with raylib 5.5, last time updated with raylib 6.0
*
* Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) and reviewed by Ramon Santamaria (@raysan5)
*
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
* if not enabled, CPU skinning will be used instead
*
* 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) 2024-2026 Kirandeep (@Kirandeep-Singh-Khehra) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
// NOTE: The upstream example uses raygui for its UI controls (dropdowns, sliders, progress bars).
// raygui is not part of raylib-cs, so the required controls are reimplemented here with
// basic raylib drawing primitives, preserving the original behaviour.
namespace Examples.Models;
public partial class AnimationBlending : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
private const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Models / Animation Blending";
public string Title => "raylib [models] example - animation blending";
private Camera3D camera;
private Model model;
private Vector3 position;
private Shader skinningShader;
private unsafe ModelAnimation* anims;
private int animCount;
private int currentAnimPlaying;
private int nextAnimToPlay;
private bool animTransition;
private int animIndex0;
private float animCurrentFrame0;
private float animFrameSpeed0;
private int animIndex1;
private float animCurrentFrame1;
private float animFrameSpeed1;
private float animBlendFactor;
private float animBlendTime;
private float animBlendTimeCounter;
private bool animPause;
private string[] animNames;
private bool dropdownEditMode0;
private bool dropdownEditMode1;
private float animFrameProgress0;
private float animFrameProgress1;
private float animBlendProgress;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(6.0f, 6.0f, 6.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load model
model = LoadModel("resources/models/gltf/robot.glb"); // Load character model
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model world position
// Load skinning shader
// NOTE: It must be a valid shader, following raylib attribs/uniform conventions for GPU skinning
skinningShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/skinning.vs",
$"resources/shaders/glsl{GlslVersion}/skinning.fs"
);
// Skinning shader could be required to be assigned to all materials shaders, just to make
// sure required uniforms are being updated for the mesh using that material (and shader)
for (var i = 0; i < model.MaterialCount; i++)
{
model.Materials[i].Shader = skinningShader;
}
// Load model animations
animCount = 0;
anims = LoadModelAnimations("resources/models/gltf/robot.glb", ref animCount);
// Animation playing variables
// NOTE: Two animations are played with a smooth transition between them
currentAnimPlaying = 0; // Current animation playing (0 o 1)
nextAnimToPlay = 1; // Next animation to play (to transition)
animTransition = false; // Flag to register anim transition state
animIndex0 = 10; // Current animation playing (walking)
animCurrentFrame0 = 0.0f; // Current animation frame (supporting interpolated frames)
animFrameSpeed0 = 0.5f; // Current animation play speed
animIndex1 = 6; // Next animation to play (running)
animCurrentFrame1 = 0.0f; // Next animation frame (supporting interpolated frames)
animFrameSpeed1 = 0.5f; // Next animation play speed
animBlendFactor = 0.0f; // Blend factor from anim0[frame0] --> anim1[frame1], [0.0f..1.0f]
animBlendTime = 2.0f; // Time to blend from one playing animation to another (in seconds)
animBlendTimeCounter = 0.0f; // Time counter (delta time)
animPause = false; // Pause animation
// UI required variables
animNames = new string[animCount];
for (var i = 0; i < animCount; i++)
{
animNames[i] = anims[i].NameToString();
}
dropdownEditMode0 = false;
dropdownEditMode1 = false;
animFrameProgress0 = 0.0f;
animFrameProgress1 = 0.0f;
animBlendProgress = 0.0f;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.P))
{
animPause = !animPause;
}
if (!animPause)
{
// Start transition from anim0[] to anim1[]
if (IsKeyPressed(KeyboardKey.Space) && !animTransition)
{
if (currentAnimPlaying == 0)
{
// Transition anim0 --> anim1
nextAnimToPlay = 1;
animCurrentFrame1 = 0.0f;
}
else
{
// Transition anim1 --> anim0
nextAnimToPlay = 0;
animCurrentFrame0 = 0.0f;
}
// Set animation transition
animTransition = true;
animBlendTimeCounter = 0.0f;
animBlendFactor = 0.0f;
}
if (animTransition)
{
// Playing anim0 and anim1 at the same time
animCurrentFrame0 += animFrameSpeed0;
if (animCurrentFrame0 >= anims[animIndex0].KeyFrameCount)
{
animCurrentFrame0 = 0.0f;
}
animCurrentFrame1 += animFrameSpeed1;
if (animCurrentFrame1 >= anims[animIndex1].KeyFrameCount)
{
animCurrentFrame1 = 0.0f;
}
// Increment blend factor over time to transition from anim0 --> anim1 over time
// NOTE: Time blending could be other than linear, using some easing
animBlendFactor = animBlendTimeCounter / animBlendTime;
animBlendTimeCounter += GetFrameTime();
animBlendProgress = animBlendFactor;
// Update model with animations blending
if (nextAnimToPlay == 1)
{
// Blend anim0 --> anim1
UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
anims[animIndex1], animCurrentFrame1, animBlendFactor);
}
else
{
// Blend anim1 --> anim0
UpdateModelAnimationEx(model, anims[animIndex1], animCurrentFrame1,
anims[animIndex0], animCurrentFrame0, animBlendFactor);
}
// Check if transition completed
if (animBlendFactor > 1.0f)
{
// Reset frame states
if (currentAnimPlaying == 0)
{
animCurrentFrame0 = 0.0f;
}
else if (currentAnimPlaying == 1)
{
animCurrentFrame1 = 0.0f;
}
currentAnimPlaying = nextAnimToPlay; // Update current animation playing
animBlendFactor = 0.0f; // Reset blend factor
animTransition = false; // Exit transition mode
animBlendTimeCounter = 0.0f;
}
}
else
{
// Play only one anim, the current one
if (currentAnimPlaying == 0)
{
// Playing anim0 at defined speed
animCurrentFrame0 += animFrameSpeed0;
if (animCurrentFrame0 >= anims[animIndex0].KeyFrameCount)
{
animCurrentFrame0 = 0.0f;
}
UpdateModelAnimation(model, anims[animIndex0], animCurrentFrame0);
}
else if (currentAnimPlaying == 1)
{
// Playing anim1 at defined speed
animCurrentFrame1 += animFrameSpeed1;
if (animCurrentFrame1 >= anims[animIndex1].KeyFrameCount)
{
animCurrentFrame1 = 0.0f;
}
UpdateModelAnimation(model, anims[animIndex1], animCurrentFrame1);
}
}
}
// Update progress bars values with current frame for each animation
animFrameProgress0 = animCurrentFrame0;
animFrameProgress1 = animCurrentFrame1;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White); // Draw animated model
DrawGrid(10, 1.0f);
EndMode3D();
if (animTransition)
{
DrawText("ANIM TRANSITION BLENDING!", 170, 50, 30, Color.Blue);
}
// Draw UI elements
//---------------------------------------------------------------------------------------------
GuiSlider(new Rectangle(10, 38, 160, 12), null, $"x{animFrameSpeed0:0.0}", ref animFrameSpeed0, 0.1f, 2.0f);
GuiSlider(new Rectangle(GetScreenWidth() - 170.0f, 38, 160, 12), $"{animFrameSpeed1:0.0}x", null, ref animFrameSpeed1, 0.1f, 2.0f);
// Blending process progress bar
GuiProgressBar(new Rectangle(180, 14, 440, 16), null, null, animBlendProgress, 0.0f, 1.0f);
// Centered "PRESS SPACE" label
const string spaceLabel = "PRESS SPACE to START BLENDING";
var spaceLabelWidth = MeasureText(spaceLabel, 20);
DrawText(spaceLabel, (GetScreenWidth() - spaceLabelWidth) / 2, (int)(GetScreenHeight() - 100.0f + 10), 20, Color.DarkGray);
// Draw playing timeline with keyframes for anim0[]
GuiProgressBar(new Rectangle(60, GetScreenHeight() - 60.0f, GetScreenWidth() - 180.0f, 20), "ANIM 0",
$"FRAME: {animFrameProgress0:0.00} / {anims[animIndex0].KeyFrameCount}",
animFrameProgress0, 0.0f, anims[animIndex0].KeyFrameCount);
for (var i = 0; i < anims[animIndex0].KeyFrameCount; i++)
{
DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180) / anims[animIndex0].KeyFrameCount) * i),
GetScreenHeight() - 60, 1, 20, Color.Blue);
}
// Draw playing timeline with keyframes for anim1[]
GuiProgressBar(new Rectangle(60, GetScreenHeight() - 30.0f, GetScreenWidth() - 180.0f, 20), "ANIM 1",
$"FRAME: {animFrameProgress1:0.00} / {anims[animIndex1].KeyFrameCount}",
animFrameProgress1, 0.0f, anims[animIndex1].KeyFrameCount);
for (var i = 0; i < anims[animIndex1].KeyFrameCount; i++)
{
DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180) / anims[animIndex1].KeyFrameCount) * i),
GetScreenHeight() - 30, 1, 20, Color.Blue);
}
// Draw animation selectors for blending transition (drawn last so open lists render on top)
// NOTE: Transition does not start until requested
if (GuiDropdownBox(new Rectangle(10, 10, 160, 24), animNames, ref animIndex0, dropdownEditMode0))
{
dropdownEditMode0 = !dropdownEditMode0;
}
if (GuiDropdownBox(new Rectangle(GetScreenWidth() - 170.0f, 10, 160, 24), animNames, ref animIndex1, dropdownEditMode1))
{
dropdownEditMode1 = !dropdownEditMode1;
}
//---------------------------------------------------------------------------------------------
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
}
// Minimal immediate-mode slider (raygui replacement)
private static bool GuiSlider(Rectangle bounds, string textLeft, string textRight, ref float value, float minValue, float maxValue)
{
var mouse = GetMousePosition();
var dragging = false;
if (CheckCollisionPointRec(mouse, bounds) && IsMouseButtonDown(MouseButton.Left))
{
value = minValue + ((mouse.X - bounds.X) / bounds.Width) * (maxValue - minValue);
if (value < minValue)
{
value = minValue;
}
if (value > maxValue)
{
value = maxValue;
}
dragging = true;
}
DrawRectangleRec(bounds, Color.LightGray);
var pct = (value - minValue) / (maxValue - minValue);
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (!string.IsNullOrEmpty(textLeft))
{
DrawText(textLeft, (int)(bounds.X - MeasureText(textLeft, 10) - 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
if (!string.IsNullOrEmpty(textRight))
{
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
return dragging;
}
// Minimal immediate-mode progress bar (raygui replacement)
private static void GuiProgressBar(Rectangle bounds, string textLeft, string textRight, float value, float minValue, float maxValue)
{
DrawRectangleRec(bounds, Color.LightGray);
var pct = maxValue > minValue ? (value - minValue) / (maxValue - minValue) : 0.0f;
if (pct < 0)
{
pct = 0;
}
if (pct > 1)
{
pct = 1;
}
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (!string.IsNullOrEmpty(textLeft))
{
DrawText(textLeft, (int)(bounds.X - MeasureText(textLeft, 10) - 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
if (!string.IsNullOrEmpty(textRight))
{
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
}
// Minimal immediate-mode dropdown box (raygui replacement)
private static bool GuiDropdownBox(Rectangle bounds, string[] items, ref int active, bool editMode)
{
var result = false;
var mouse = GetMousePosition();
// Draw main box
DrawRectangleRec(bounds, Color.LightGray);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (active >= 0 && active < items.Length)
{
DrawText(items[active], (int)bounds.X + 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
DrawText(editMode ? "^" : "v", (int)(bounds.X + bounds.Width - 12), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
// Draw items when open
if (editMode)
{
for (var i = 0; i < items.Length; i++)
{
Rectangle item = new(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
var hover = CheckCollisionPointRec(mouse, item);
DrawRectangleRec(item, hover ? Color.SkyBlue : Color.LightGray);
DrawRectangleLinesEx(item, 1, Color.Gray);
DrawText(items[i], (int)item.X + 5, (int)(item.Y + item.Height / 2 - 5), 10, Color.DarkGray);
}
}
if (IsMouseButtonPressed(MouseButton.Left))
{
if (editMode)
{
for (var i = 0; i < items.Length; i++)
{
Rectangle item = new(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
if (CheckCollisionPointRec(mouse, item))
{
active = i;
break;
}
}
result = true; // any click closes the dropdown
}
else if (CheckCollisionPointRec(mouse, bounds))
{
result = true; // open the dropdown
}
}
return result;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation blending");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AnimationBlending();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,157 @@
/*******************************************************************************************
*
* raylib [models] example - animation gpu skinning
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5)
*
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
* if not enabled, CPU skinning will be used instead
*
* 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) 2024-2025 Daniel Holden (@orangeduck)
*
********************************************************************************************/
namespace Examples.Models;
public partial class AnimationGpuSkinning : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
private const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Models / Animation GPU Skinning";
public string Title => "raylib [models] example - animation gpu skinning";
private Camera3D camera;
private Model model;
private Vector3 position;
private Shader skinningShader;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private int animCurrentFrame;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(5.0f, 5.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 1.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load gltf model
model = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model position
// Load skinning shader
// NOTE: It must be a valid shader, following raylib attribs/uniform conventions for GPU skinning
skinningShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/skinning.vs",
$"resources/shaders/glsl{GlslVersion}/skinning.fs"
);
// Skinning shader could be required to be assigned to all materials shaders, just to make
// sure required uniforms are being updated for the mesh using that material (and shader)
model.Materials[1].Shader = skinningShader; // Just assigning to materials[1] for this model
// Load gltf model animations
animCount = 0;
anims = LoadModelAnimations("resources/models/gltf/greenman.glb", ref animCount);
// Animation playing variables
animIndex = 0; // Current animation playing
animCurrentFrame = 0; // Current animation frame
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Select current animation
if (IsKeyPressed(KeyboardKey.Right))
{
animIndex = (animIndex + 1) % animCount;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
animIndex = (animIndex + animCount - 1) % animCount;
}
// Update model animation
animCurrentFrame = (animCurrentFrame + 1) % anims[animIndex].KeyFrameCount;
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 40, 20, Color.Maroon);
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation gpu skinning");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AnimationGpuSkinning();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,289 @@
/*******************************************************************************************
*
* raylib [models] example - animation timing
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* 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) 2026 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
// NOTE: The upstream example uses raygui for its UI controls (dropdown, slider, progress bar).
// raygui is not part of raylib-cs, so the required controls are reimplemented here with
// basic raylib drawing primitives, preserving the original behaviour.
namespace Examples.Models;
public partial class AnimationTiming : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Animation Timing";
public string Title => "raylib [models] example - animation timing";
private Camera3D camera;
private Model model;
private Vector3 position;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private float animCurrentFrame;
private float animFrameSpeed;
private bool animPause;
private string[] animNames;
private bool dropdownEditMode;
private float animFrameProgress;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(6.0f, 6.0f, 6.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load model
model = LoadModel("resources/models/gltf/robot.glb");
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model world position
// Load model animations
animCount = 0;
anims = LoadModelAnimations("resources/models/gltf/robot.glb", ref animCount);
// Animation playing variables
animIndex = 10; // Current animation playing
animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
animFrameSpeed = 0.5f; // Animation play speed
animPause = false; // Pause animation
// UI required variables
animNames = new string[animCount];
for (var i = 0; i < animCount; i++)
{
animNames[i] = anims[i].NameToString();
}
dropdownEditMode = false;
animFrameProgress = 0.0f;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.P))
{
animPause = !animPause;
}
if (!animPause && (animIndex < animCount))
{
// Update model animation
animCurrentFrame += animFrameSpeed;
if (animCurrentFrame >= anims[animIndex].KeyFrameCount)
{
animCurrentFrame = 0.0f;
}
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
}
// NOTE: Animation and playing speed selected through UI
// Update progressbar value with current frame
animFrameProgress = animCurrentFrame;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
// Draw UI, select anim and playing speed
GuiSlider(new Rectangle(260, 10, 500, 24), "FRAME SPEED: ", $"x{animFrameSpeed:0.0}", ref animFrameSpeed, 0.1f, 2.0f);
// Draw playing timeline with keyframes
DrawText($"CURRENT FRAME: {animFrameProgress:0.00} / {anims[animIndex].KeyFrameCount}",
10, (int)(GetScreenHeight() - 64.0f), 10, Color.DarkGray);
GuiProgressBar(new Rectangle(10, GetScreenHeight() - 40.0f, GetScreenWidth() - 20.0f, 24), null, null,
animFrameProgress, 0.0f, anims[animIndex].KeyFrameCount);
for (var i = 0; i < anims[animIndex].KeyFrameCount; i++)
{
DrawRectangle(10 + (int)(((float)(GetScreenWidth() - 20) / anims[animIndex].KeyFrameCount) * i),
GetScreenHeight() - 40, 1, 24, Color.Blue);
}
// NOTE: Dropdown drawn last so its open item list renders on top
if (GuiDropdownBox(new Rectangle(10, 10, 140, 24), animNames, ref animIndex, dropdownEditMode))
{
dropdownEditMode = !dropdownEditMode;
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
}
// Minimal immediate-mode slider (raygui replacement)
private static bool GuiSlider(Rectangle bounds, string textLeft, string textRight, ref float value, float minValue, float maxValue)
{
var mouse = GetMousePosition();
var dragging = false;
if (CheckCollisionPointRec(mouse, bounds) && IsMouseButtonDown(MouseButton.Left))
{
value = minValue + ((mouse.X - bounds.X) / bounds.Width) * (maxValue - minValue);
if (value < minValue)
{
value = minValue;
}
if (value > maxValue)
{
value = maxValue;
}
dragging = true;
}
DrawRectangleRec(bounds, Color.LightGray);
var pct = (value - minValue) / (maxValue - minValue);
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (!string.IsNullOrEmpty(textLeft))
{
DrawText(textLeft, (int)(bounds.X - MeasureText(textLeft, 10) - 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
if (!string.IsNullOrEmpty(textRight))
{
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
return dragging;
}
// Minimal immediate-mode progress bar (raygui replacement)
private static void GuiProgressBar(Rectangle bounds, string textLeft, string textRight, float value, float minValue, float maxValue)
{
DrawRectangleRec(bounds, Color.LightGray);
var pct = maxValue > minValue ? (value - minValue) / (maxValue - minValue) : 0.0f;
if (pct < 0)
{
pct = 0;
}
if (pct > 1)
{
pct = 1;
}
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (!string.IsNullOrEmpty(textLeft))
{
DrawText(textLeft, (int)(bounds.X - MeasureText(textLeft, 10) - 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
if (!string.IsNullOrEmpty(textRight))
{
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
}
// Minimal immediate-mode dropdown box (raygui replacement)
private static bool GuiDropdownBox(Rectangle bounds, string[] items, ref int active, bool editMode)
{
var result = false;
var mouse = GetMousePosition();
// Draw main box
DrawRectangleRec(bounds, Color.LightGray);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (active >= 0 && active < items.Length)
{
DrawText(items[active], (int)bounds.X + 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
DrawText(editMode ? "^" : "v", (int)(bounds.X + bounds.Width - 12), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
// Draw items when open
if (editMode)
{
for (var i = 0; i < items.Length; i++)
{
Rectangle item = new(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
var hover = CheckCollisionPointRec(mouse, item);
DrawRectangleRec(item, hover ? Color.SkyBlue : Color.LightGray);
DrawRectangleLinesEx(item, 1, Color.Gray);
DrawText(items[i], (int)item.X + 5, (int)(item.Y + item.Height / 2 - 5), 10, Color.DarkGray);
}
}
if (IsMouseButtonPressed(MouseButton.Left))
{
if (editMode)
{
for (var i = 0; i < items.Length; i++)
{
Rectangle item = new(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
if (CheckCollisionPointRec(mouse, item))
{
active = i;
break;
}
}
result = true; // any click closes the dropdown
}
else if (CheckCollisionPointRec(mouse, bounds))
{
result = true; // open the dropdown
}
}
return result;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timing");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AnimationTiming();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,199 @@
/*******************************************************************************************
*
* raylib [models] example - basic voxel
*
* Example complexity rating: [] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Tim Little (@timlittle) 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 Tim Little (@timlittle)
*
********************************************************************************************/
namespace Examples.Models;
public partial class BasicVoxel : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int WorldSize = 8; // Size of our voxel world (8x8x8 cubes)
public string Name => "Models / Basic Voxel";
public string Title => "raylib [models] example - basic voxel";
public bool CursorDisabled => true;
private Camera3D camera;
private Model cubeModel;
private bool[,,] voxels;
public unsafe void Init()
{
// Define the camera to look into our 3d world (first person)
camera = new();
camera.Position = new Vector3(-2.0f, 0.0f, -2.0f); // Camera position at ground level
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Create a cube model
var cubeMesh = GenMeshCube(1.0f, 1.0f, 1.0f); // Create a unit cube mesh
cubeModel = LoadModelFromMesh(cubeMesh); // Convert mesh to a model
cubeModel.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Color = Color.Beige;
// Initialize voxel world - fill with voxels
voxels = new bool[WorldSize, WorldSize, WorldSize];
for (var x = 0; x < WorldSize; x++)
{
for (var y = 0; y < WorldSize; y++)
{
for (var z = 0; z < WorldSize; z++)
{
voxels[x, y, z] = true;
}
}
}
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.FirstPerson);
// Handle voxel removal with mouse click
// This method is quite inefficient. Ray marching through the voxel grid using DDA would be faster, but more complex.
if (IsMouseButtonPressed(MouseButton.Left))
{
// Cast a ray from the screen center (where crosshair would be)
Vector2 screenCenter = new(GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f);
var ray = GetScreenToWorldRay(screenCenter, camera);
// Check ray collision with all voxels
var closestDistance = 99999.0f;
Vector3 closestVoxelPosition = new(-1, -1, -1);
var voxelFound = false;
for (var x = 0; x < WorldSize; x++)
{
for (var y = 0; y < WorldSize; y++)
{
for (var z = 0; z < WorldSize; z++)
{
if (!voxels[x, y, z])
{
continue; // Skip empty voxels
}
// Build a bounding box for this voxel
Vector3 position = new(x, y, z);
BoundingBox box = new(
new Vector3(position.X - 0.5f, position.Y - 0.5f, position.Z - 0.5f),
new Vector3(position.X + 0.5f, position.Y + 0.5f, position.Z + 0.5f)
);
// Check ray-box collision
var collision = GetRayCollisionBox(ray, box);
if (collision.Hit && (collision.Distance < closestDistance))
{
closestDistance = collision.Distance;
closestVoxelPosition = new Vector3(x, y, z);
voxelFound = true;
}
}
}
}
// Remove the closest voxel if one was hit
if (voxelFound)
{
voxels[(int)closestVoxelPosition.X,
(int)closestVoxelPosition.Y,
(int)closestVoxelPosition.Z] = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 1.0f);
// Draw all voxels
for (var x = 0; x < WorldSize; x++)
{
for (var y = 0; y < WorldSize; y++)
{
for (var z = 0; z < WorldSize; z++)
{
if (!voxels[x, y, z])
{
continue;
}
Vector3 position = new(x, y, z);
DrawModel(cubeModel, position, 1.0f, Color.Beige);
DrawCubeWires(position, 1.0f, 1.0f, 1.0f, Color.Black);
}
}
}
EndMode3D();
// Draw reference point for raycasting to delete blocks
DrawCircle(GetScreenWidth() / 2, GetScreenHeight() / 2, 4, Color.Red);
DrawText("Left-click a voxel to remove it!", 10, 10, 20, Color.DarkGray);
DrawText("WASD to move, mouse to look around", 10, 35, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(cubeModel);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - basic voxel");
DisableCursor(); // Lock mouse to window center
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new BasicVoxel();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,134 +1,145 @@
/*******************************************************************************************
*
* raylib [models] example - Drawing billboards
* raylib [models] example - billboard rendering
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 3/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.3, last time updated with raylib 3.5
*
* 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class BillboardDemo
public partial class BillboardDemo : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Billboard Demo";
public string Title => "raylib [models] example - billboard rendering";
private Camera3D camera;
private Texture2D bill;
private Vector3 billPositionStatic;
private Vector3 billPositionRotating;
private Rectangle source;
private Vector3 billUp;
private Vector2 size;
private Vector2 origin;
private float distanceStatic;
private float distanceRotating;
private float rotation;
public void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(5.0f, 4.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
bill = LoadTexture("resources/billboard.png"); // Our billboard texture
billPositionStatic = new(0.0f, 2.0f, 0.0f); // Position of static billboard
billPositionRotating = new(1.0f, 2.0f, 1.0f); // Position of rotating billboard
// Entire billboard texture, source is used to take a segment from a larger texture
source = new(0.0f, 0.0f, (float)bill.Width, (float)bill.Height);
// NOTE: Billboard locked on axis-Y
billUp = new(0.0f, 1.0f, 0.0f);
// Set the height of the rotating billboard to 1.0 with the aspect ratio fixed
size = new(source.Width / source.Height, 1.0f);
// Rotate around origin
// Here we choose to rotate around the image center
origin = size * 0.5f;
// Distance is needed for the correct billboard draw order
// Larger distance (further away from the camera) should be drawn prior to smaller distance
distanceStatic = 0.0f;
distanceRotating = 0.0f;
rotation = 0.0f;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
rotation += 0.4f;
distanceStatic = Vector3.Distance(camera.Position, billPositionStatic);
distanceRotating = Vector3.Distance(camera.Position, billPositionRotating);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 1.0f); // Draw a grid
// Draw order matters!
if (distanceStatic > distanceRotating)
{
DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White);
DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, Color.White);
}
else
{
DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, Color.White);
DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White);
}
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(bill); // Unload texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - billboard rendering");
InitWindow(screenWidth, screenHeight, "raylib [models] example - drawing billboards");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(5.0f, 4.0f, 5.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Our texture billboard
Texture2D bill = LoadTexture("resources/billboard.png");
// Position of billboard billboard
Vector3 billPositionStatic = new(0.0f, 2.0f, 0.0f);
Vector3 billPositionRotating = new(1.0f, 2.0f, 1.0f);
// Entire billboard texture, source is used to take a segment from a larger texture.
Rectangle source = new(0.0f, 0.0f, (float)bill.Width, (float)bill.Height);
// NOTE: Billboard locked on axis-Y
Vector3 billUp = new(0.0f, 1.0f, 0.0f);
// Rotate around origin
// Here we choose to rotate around the image center
// NOTE: (-1, 1) is the range where origin.X, origin.Y is inside the texture
Vector2 rotateOrigin = Vector2.Zero;
// Distance is needed for the correct billboard draw order
// Larger distance (further away from the camera) should be drawn prior to smaller distance.
float distanceStatic = 0.0f;
float distanceRotating = 0.0f;
float rotation = 0.0f;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new BillboardDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
rotation += 0.4f;
distanceStatic = Vector3.Distance(camera.Position, billPositionStatic);
distanceRotating = Vector3.Distance(camera.Position, billPositionRotating);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 1.0f);
// Draw order matters!
if (distanceStatic > distanceRotating)
{
DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White);
DrawBillboardPro(
camera,
bill,
source,
billPositionRotating,
billUp,
new Vector2(1.0f, 1.0f),
rotateOrigin,
rotation,
Color.White
);
}
else
{
DrawBillboardPro(
camera,
bill,
source,
billPositionRotating,
billUp,
new Vector2(1.0f, 1.0f),
rotateOrigin,
rotation,
Color.White
);
DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White);
}
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(bill);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,244 @@
/*******************************************************************************************
*
* raylib [models] example - bone socket
*
* Example complexity rating: [] 4/4
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by iP (@ipzaur) 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) 2024-2025 iP (@ipzaur)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class BoneSocket : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int BoneSockets = 3;
private const int BoneSocketHat = 0;
private const int BoneSocketHandR = 1;
private const int BoneSocketHandL = 2;
public string Name => "Models / Bone Socket";
public string Title => "raylib [models] example - bone socket";
public bool CursorDisabled => true;
private Camera3D camera;
private Model characterModel;
private Model[] equipModel;
private bool[] showEquip;
private int animsCount;
private int animIndex;
private int animCurrentFrame;
private unsafe ModelAnimation* modelAnimations;
private int[] boneSocketIndex;
private Vector3 position;
private int angle;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(5.0f, 5.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load gltf model
characterModel = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
equipModel = new Model[BoneSockets]
{
LoadModel("resources/models/gltf/greenman_hat.glb"), // Index for the hat model is the same as BONE_SOCKET_HAT
LoadModel("resources/models/gltf/greenman_sword.glb"), // Index for the sword model is the same as BONE_SOCKET_HAND_R
LoadModel("resources/models/gltf/greenman_shield.glb") // Index for the shield model is the same as BONE_SOCKET_HAND_L
};
showEquip = new bool[3] { true, true, true }; // Toggle on/off equip
// Load gltf model animations
animsCount = 0;
animIndex = 0;
animCurrentFrame = 0;
modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", ref animsCount);
// Indices of bones for sockets
boneSocketIndex = new int[BoneSockets] { -1, -1, -1 };
// Search bones for sockets
for (var i = 0; i < characterModel.Skeleton.BoneCount; i++)
{
var boneName = characterModel.Skeleton.Bones[i].NameToString();
if (boneName == "socket_hat")
{
boneSocketIndex[BoneSocketHat] = i;
continue;
}
if (boneName == "socket_hand_R")
{
boneSocketIndex[BoneSocketHandR] = i;
continue;
}
if (boneName == "socket_hand_L")
{
boneSocketIndex[BoneSocketHandL] = i;
continue;
}
}
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model position
angle = 0; // Set angle for rotate character
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.ThirdPerson);
// Rotate character
if (IsKeyDown(KeyboardKey.F))
{
angle = (angle + 1) % 360;
}
else if (IsKeyDown(KeyboardKey.H))
{
angle = (360 + angle - 1) % 360;
}
// Select current animation
if (IsKeyPressed(KeyboardKey.T))
{
animIndex = (animIndex + 1) % animsCount;
}
else if (IsKeyPressed(KeyboardKey.G))
{
animIndex = (animIndex + animsCount - 1) % animsCount;
}
// Toggle shown of equip
if (IsKeyPressed(KeyboardKey.One))
{
showEquip[BoneSocketHat] = !showEquip[BoneSocketHat];
}
if (IsKeyPressed(KeyboardKey.Two))
{
showEquip[BoneSocketHandR] = !showEquip[BoneSocketHandR];
}
if (IsKeyPressed(KeyboardKey.Three))
{
showEquip[BoneSocketHandL] = !showEquip[BoneSocketHandL];
}
// Update model animation
var anim = modelAnimations[animIndex];
animCurrentFrame = (animCurrentFrame + 1) % anim.KeyFrameCount;
UpdateModelAnimation(characterModel, anim, animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw character
var characterRotate = QuaternionFromAxisAngle(new Vector3(0.0f, 1.0f, 0.0f), angle * DEG2RAD);
characterModel.Transform = MatrixMultiply(QuaternionToMatrix(characterRotate), MatrixTranslate(position.X, position.Y, position.Z));
UpdateModelAnimation(characterModel, anim, animCurrentFrame);
DrawMesh(characterModel.Meshes[0], characterModel.Materials[1], characterModel.Transform);
// Draw equipments (hat, sword, shield)
for (var i = 0; i < BoneSockets; i++)
{
if (!showEquip[i])
{
continue;
}
var transform = &anim.KeyframePoses[animCurrentFrame][boneSocketIndex[i]];
var inRotation = characterModel.Skeleton.BindPose[boneSocketIndex[i]].Rotation;
var outRotation = transform->Rotation;
// Calculate socket rotation (angle between bone in initial pose and same bone in current animation frame)
var rotate = QuaternionMultiply(outRotation, QuaternionInvert(inRotation));
var matrixTransform = QuaternionToMatrix(rotate);
// Translate socket to its position in the current animation
matrixTransform = MatrixMultiply(matrixTransform, MatrixTranslate(transform->Translation.X, transform->Translation.Y, transform->Translation.Z));
// Transform the socket using the transform of the character (angle and translate)
matrixTransform = MatrixMultiply(matrixTransform, characterModel.Transform);
// Draw mesh at socket position with socket angle rotation
DrawMesh(equipModel[i].Meshes[0], equipModel[i].Materials[1], matrixTransform);
}
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Use the T/G to switch animation", 10, 10, 20, Color.Gray);
DrawText("Use the F/H to rotate character left/right", 10, 35, 20, Color.Gray);
DrawText("Use the 1,2,3 to toggle shown of hat, sword and shield", 10, 60, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(modelAnimations, animsCount);
UnloadModel(characterModel); // Unload character model and meshes/material
// Unload equipment model and meshes/material
for (var i = 0; i < BoneSockets; i++)
{
UnloadModel(equipModel[i]);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - bone socket");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new BoneSocket();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,142 +1,179 @@
/*******************************************************************************************
*
* raylib [models] example - Detect basic 3d collisions (box vs sphere vs box)
* raylib [models] example - box collisions
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 1/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.3, last time updated with raylib 3.5
*
* 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class BoxCollisions
public partial class BoxCollisions : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Box Collisions";
public string Title => "raylib [models] example - box collisions";
private Camera3D camera;
private Vector3 playerPosition;
private Vector3 playerSize;
private Color playerColor;
private Vector3 enemyBoxPos;
private Vector3 enemyBoxSize;
private Vector3 enemySpherePos;
private float enemySphereSize;
private bool collision;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Vector3 playerPosition = new(0.0f, 1.0f, 2.0f);
Vector3 playerSize = new(1.0f, 2.0f, 1.0f);
Color playerColor = Color.Green;
playerPosition = new(0.0f, 1.0f, 2.0f);
playerSize = new(1.0f, 2.0f, 1.0f);
playerColor = Color.Green;
Vector3 enemyBoxPos = new(-4.0f, 1.0f, 0.0f);
Vector3 enemyBoxSize = new(2.0f, 2.0f, 2.0f);
enemyBoxPos = new(-4.0f, 1.0f, 0.0f);
enemyBoxSize = new(2.0f, 2.0f, 2.0f);
Vector3 enemySpherePos = new(4.0f, 0.0f, 0.0f);
float enemySphereSize = 1.5f;
enemySpherePos = new(4.0f, 0.0f, 0.0f);
enemySphereSize = 1.5f;
bool collision = false;
collision = false;
}
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Move player
if (IsKeyDown(KeyboardKey.Right))
{
playerPosition.X += 0.2f;
}
else if (IsKeyDown(KeyboardKey.Left))
{
playerPosition.X -= 0.2f;
}
else if (IsKeyDown(KeyboardKey.Down))
{
playerPosition.Z += 0.2f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
playerPosition.Z -= 0.2f;
}
collision = false;
// Check collisions player vs enemy-box
BoundingBox box1 = new(
playerPosition - (playerSize / 2),
playerPosition + (playerSize / 2)
);
BoundingBox box2 = new(
enemyBoxPos - (enemyBoxSize / 2),
enemyBoxPos + (enemyBoxSize / 2)
);
if (CheckCollisionBoxes(box1, box2))
{
collision = true;
}
// Check collisions player vs enemy-sphere
if (CheckCollisionBoxSphere(box1, enemySpherePos, enemySphereSize))
{
collision = true;
}
if (collision)
{
playerColor = Color.Red;
}
else
{
playerColor = Color.Green;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw enemy-box
DrawCube(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.Gray);
DrawCubeWires(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.DarkGray);
// Draw enemy-sphere
DrawSphere(enemySpherePos, enemySphereSize, Color.Gray);
DrawSphereWires(enemySpherePos, enemySphereSize, 16, 16, Color.DarkGray);
// Draw player
DrawCubeV(playerPosition, playerSize, playerColor);
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D();
DrawText("Move player with arrow keys to collide", 220, 40, 20, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new BoxCollisions();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Move player
if (IsKeyDown(KeyboardKey.Right))
{
playerPosition.X += 0.2f;
}
else if (IsKeyDown(KeyboardKey.Left))
{
playerPosition.X -= 0.2f;
}
else if (IsKeyDown(KeyboardKey.Down))
{
playerPosition.Z += 0.2f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
playerPosition.Z -= 0.2f;
}
collision = false;
// Check collisions player vs enemy-box
BoundingBox box1 = new(
playerPosition - (playerSize / 2),
playerPosition + (playerSize / 2)
);
BoundingBox box2 = new(
enemyBoxPos - (enemyBoxSize / 2),
enemyBoxPos + (enemyBoxSize / 2)
);
if (CheckCollisionBoxes(box1, box2))
{
collision = true;
}
// Check collisions player vs enemy-sphere
if (CheckCollisionBoxSphere(box1, enemySpherePos, enemySphereSize))
{
collision = true;
}
if (collision)
{
playerColor = Color.Red;
}
else
{
playerColor = Color.Green;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw enemy-box
DrawCube(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.Gray);
DrawCubeWires(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.DarkGray);
// Draw enemy-sphere
DrawSphere(enemySpherePos, enemySphereSize, Color.Gray);
DrawSphereWires(enemySpherePos, enemySphereSize, 16, 16, Color.DarkGray);
// Draw player
DrawCubeV(playerPosition, playerSize, playerColor);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Move player with cursors to collide", 220, 40, 20, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,104 +1,142 @@
/*******************************************************************************************
*
* raylib [models] example - Cubicmap loading and drawing
* raylib [models] example - cubicmap rendering
*
* This example has been created using raylib 1.8 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 2/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.8, last time updated with raylib 3.5
*
* 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class CubicmapDemo
public partial class CubicmapDemo : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Cubicmap Demo";
public string Title => "raylib [models] example - cubicmap rendering";
private Camera3D camera;
private Texture2D cubicmap;
private Texture2D texture;
private Model model;
private Vector3 mapPosition;
private bool pause;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - cubesmap loading and drawing");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(16.0f, 14.0f, 16.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(16.0f, 14.0f, 16.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
Image image = LoadImage("resources/cubicmap.png");
Texture2D cubicmap = LoadTextureFromImage(image);
var image = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM)
cubicmap = LoadTextureFromImage(image); // Convert image to texture to display (VRAM)
Mesh mesh = GenMeshCubicmap(image, new Vector3(1.0f, 1.0f, 1.0f));
Model model = LoadModelFromMesh(mesh);
var mesh = GenMeshCubicmap(image, new Vector3(1.0f, 1.0f, 1.0f));
model = LoadModelFromMesh(mesh);
// NOTE: By default each cube is mapped to one part of texture atlas
Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture
// Set map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 mapPosition = new(-16.0f, 0.0f, -8.0f);
UnloadImage(image);
mapPosition = new(-16.0f, 0.0f, -8.0f); // Set model position
SetTargetFPS(60);
UnloadImage(image); // Unload cubesmap image from RAM, already uploaded to VRAM
pause = false; // Pause camera orbital rotation (and zoom)
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
}
if (!pause)
{
UpdateCamera(ref camera, CameraMode.Orbital);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.White);
EndMode3D();
Vector2 position = new(screenWidth - cubicmap.Width * 4 - 20, 20);
DrawTextureEx(cubicmap, position, 0.0f, 4.0f, Color.White);
DrawRectangleLines(
screenWidth - cubicmap.Width * 4 - 20,
20,
cubicmap.Width * 4,
cubicmap.Height * 4,
Color.Green
);
DrawText("cubicmap image used to", 658, 90, 10, Color.Gray);
DrawText("generate map 3d model", 658, 104, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(cubicmap); // Unload cubicmap texture
UnloadTexture(texture); // Unload map texture
UnloadModel(model); // Unload map model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - cubicmap rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new CubicmapDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.White);
EndMode3D();
Vector2 position = new(screenWidth - cubicmap.Width * 4 - 20, 20);
DrawTextureEx(cubicmap, position, 0.0f, 4.0f, Color.White);
DrawRectangleLines(
screenWidth - cubicmap.Width * 4 - 20,
20,
cubicmap.Width * 4,
cubicmap.Height * 4,
Color.Green
);
DrawText("cubicmap image used to", 658, 90, 10, Color.Gray);
DrawText("generate map 3d model", 658, 104, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(cubicmap);
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

602
Examples/Models/Decals.cs Normal file
View file

@ -0,0 +1,602 @@
/*******************************************************************************************
*
* raylib [models] example - decals
*
* Example complexity rating: [] 4/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5)
* Based on previous work by @mrdoob
*
* 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 JP Mortiboys (@themushroompirates) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class Decals : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxDecals = 256;
public string Name => "Models / Decals";
public string Title => "raylib [models] example - decals";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Model model;
private Texture2D modelTexture;
private BoundingBox modelBBox;
private float decalSize;
private float decalOffset;
private Model placementCube;
private Material decalMaterial;
private Texture2D decalTexture;
private bool showModel;
private readonly List<Model> decalModels = new();
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(5.0f, 5.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 1.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.6f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load character model
model = LoadModel("resources/models/obj/character.obj");
// Apply character skin
modelTexture = LoadTexture("resources/models/obj/character_diffuse.png");
SetTextureFilter(modelTexture, TextureFilter.Bilinear);
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = modelTexture;
modelBBox = GetMeshBoundingBox(model.Meshes[0]); // Get mesh bounding box
camera.Target = Vector3Lerp(modelBBox.Min, modelBBox.Max, 0.5f);
camera.Position = modelBBox.Max * 1.0f;
camera.Position.X *= 0.1f;
var modelSize = MathF.Min(
MathF.Min(MathF.Abs(modelBBox.Max.X - modelBBox.Min.X), MathF.Abs(modelBBox.Max.Y - modelBBox.Min.Y)),
MathF.Abs(modelBBox.Max.Z - modelBBox.Min.Z));
camera.Position = new Vector3(0.0f, modelBBox.Max.Y * 1.2f, modelSize * 3.0f);
decalSize = modelSize * 0.25f;
decalOffset = 0.01f;
placementCube = LoadModelFromMesh(GenMeshCube(decalSize, decalSize, decalSize));
placementCube.Materials[0].Maps[0].Color = Color.Lime;
decalMaterial = LoadMaterialDefault();
decalMaterial.Maps[0].Color = Color.Yellow;
var decalImage = LoadImage("resources/raylib_logo.png");
ImageResizeNN(ref decalImage, decalImage.Width / 4, decalImage.Height / 4);
decalTexture = LoadTextureFromImage(decalImage);
UnloadImage(decalImage);
SetTextureFilter(decalTexture, TextureFilter.Bilinear);
decalMaterial.Maps[(int)MaterialMapIndex.Diffuse].Texture = decalTexture;
decalMaterial.Maps[(int)MaterialMapIndex.Diffuse].Color = Color.RayWhite;
showModel = true;
decalModels.Clear();
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsMouseButtonDown(MouseButton.Right))
{
UpdateCamera(ref camera, CameraMode.ThirdPerson);
}
// Display information about closest hit
RayCollision collision = new();
collision.Distance = float.MaxValue;
collision.Hit = false;
// Get mouse ray
var ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check ray collision against bounding box first, before trying the full ray-mesh test
var boxHitInfo = GetRayCollisionBox(ray, modelBBox);
RayCollision meshHitInfo = new();
if (boxHitInfo.Hit && (decalModels.Count < MaxDecals))
{
// Check ray collision against model meshes
for (var m = 0; m < model.MeshCount; m++)
{
// NOTE: We consider the model.transform for the collision check but
// it can be checked against any transform Matrix, used when checking against same
// model drawn multiple times with multiple transforms
meshHitInfo = GetRayCollisionMesh(ray, model.Meshes[m], model.Transform);
if (meshHitInfo.Hit)
{
// Save the closest hit mesh
if (!collision.Hit || (collision.Distance > meshHitInfo.Distance))
{
collision = meshHitInfo;
}
}
}
if (meshHitInfo.Hit)
{
collision = meshHitInfo;
}
}
// Add decal to mesh on hit point
if (collision.Hit && IsMouseButtonPressed(MouseButton.Left) && (decalModels.Count < MaxDecals))
{
// Create the transformation to project the decal
var origin = collision.Point + (collision.Normal * 1.0f);
var splat = MatrixLookAt(collision.Point, origin, new Vector3(0.0f, 1.0f, 0.0f));
// Spin the placement around a bit
splat = MatrixMultiply(splat, MatrixRotateZ(DEG2RAD * GetRandomValue(-180, 180)));
var decalMesh = GenMeshDecal(model, splat, decalSize, decalOffset);
if (decalMesh.VertexCount > 0)
{
var decalModel = LoadModelFromMesh(decalMesh);
decalModel.Materials[0].Maps[0] = decalMaterial.Maps[0];
decalModels.Add(decalModel);
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw the model at the origin and default scale
if (showModel)
{
DrawModel(model, new Vector3(0.0f, 0.0f, 0.0f), 1.0f, Color.White);
}
// Draw the decal models
for (var i = 0; i < decalModels.Count; i++)
{
DrawModel(decalModels[i], Vector3.Zero, 1.0f, Color.White);
}
// If we hit the mesh, draw the box for the decal
if (collision.Hit)
{
var origin = collision.Point + (collision.Normal * 1.0f);
var splat = MatrixLookAt(collision.Point, origin, new Vector3(0, 1, 0));
placementCube.Transform = MatrixInvert(splat);
DrawModel(placementCube, Vector3.Zero, 1.0f, Fade(Color.White, 0.5f));
}
DrawGrid(10, 10.0f);
EndMode3D();
float yPos = 10;
var x0 = GetScreenWidth() - 300.0f;
var x1 = x0 + 100;
var x2 = x1 + 100;
DrawText("Vertices", (int)x1, (int)yPos, 10, Color.Lime);
DrawText("Triangles", (int)x2, (int)yPos, 10, Color.Lime);
yPos += 15;
var vertexCount = 0;
var triangleCount = 0;
for (var i = 0; i < model.MeshCount; i++)
{
vertexCount += model.Meshes[i].VertexCount;
triangleCount += model.Meshes[i].TriangleCount;
}
DrawText("Main model", (int)x0, (int)yPos, 10, Color.Lime);
DrawText($"{vertexCount}", (int)x1, (int)yPos, 10, Color.Lime);
DrawText($"{triangleCount}", (int)x2, (int)yPos, 10, Color.Lime);
yPos += 15;
for (var i = 0; i < decalModels.Count; i++)
{
if (i == 20)
{
DrawText("...", (int)x0, (int)yPos, 10, Color.Lime);
yPos += 15;
}
if (i < 20)
{
DrawText($"Decal #{i + 1}", (int)x0, (int)yPos, 10, Color.Lime);
DrawText($"{decalModels[i].Meshes[0].VertexCount}", (int)x1, (int)yPos, 10, Color.Lime);
DrawText($"{decalModels[i].Meshes[0].TriangleCount}", (int)x2, (int)yPos, 10, Color.Lime);
yPos += 15;
}
vertexCount += decalModels[i].Meshes[0].VertexCount;
triangleCount += decalModels[i].Meshes[0].TriangleCount;
}
DrawText("TOTAL", (int)x0, (int)yPos, 10, Color.Lime);
DrawText($"{vertexCount}", (int)x1, (int)yPos, 10, Color.Lime);
DrawText($"{triangleCount}", (int)x2, (int)yPos, 10, Color.Lime);
yPos += 15;
DrawText("Hold RMB to move camera", 10, 430, 10, Color.Gray);
DrawText("(c) Character model and texture from kenney.nl", screenWidth - 260, screenHeight - 20, 10, Color.Gray);
// UI elements
if (GuiButton(new Rectangle(10, screenHeight - 1000.0f, 100, 60), showModel ? "Hide Model" : "Show Model"))
{
showModel = !showModel;
}
if (GuiButton(new Rectangle(10 + 110, screenHeight - 100.0f, 100, 60), "Clear Decals"))
{
// Clear decals, unload all decal models
for (var i = 0; i < decalModels.Count; i++)
{
UnloadModel(decalModels[i]);
}
decalModels.Clear();
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model);
UnloadTexture(modelTexture);
// Unload decal models
for (var i = 0; i < decalModels.Count; i++)
{
UnloadModel(decalModels[i]);
}
decalModels.Clear();
UnloadTexture(decalTexture);
UnloadModel(placementCube);
}
// Clip segment
private static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s)
{
var d0 = Vector3.Dot(v0, p) - s;
var d1 = Vector3.Dot(v1, p) - s;
var s0 = d0 / (d0 - d1);
return Vector3.Lerp(v0, v1, s0);
}
// Generate mesh decals for provided model
private static unsafe Mesh GenMeshDecal(Model target, Matrix4x4 projection, float decalSize, float decalOffset)
{
// We're going to use these to build up our decal meshes
var meshBuilders = new List<Vector3>[2] { new(), new() };
// We're going to need the inverse matrix
var invProj = MatrixInvert(projection);
// We'll be flip-flopping between the two mesh builders
// Reading from one and writing to the other, then swapping
var mbIndex = 0;
// First pass, just get any triangle inside the bounding box (for each mesh of the model)
for (var meshIndex = 0; meshIndex < target.MeshCount; meshIndex++)
{
var mesh = target.Meshes[meshIndex];
for (var tri = 0; tri < mesh.TriangleCount; tri++)
{
var vertices = new Vector3[3];
// The way we calculate the vertices of the mesh triangle
// depend on whether the mesh vertices are indexed or not
if (mesh.Indices == null)
{
for (var v = 0; v < 3; v++)
{
vertices[v] = new 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 (var v = 0; v < 3; v++)
{
vertices[v] = new 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
var insideCount = 0;
for (var i = 0; i < 3; i++)
{
// To projection space
var v = Vector3Transform(vertices[i], projection);
if ((MathF.Abs(v.X) < decalSize) || (MathF.Abs(v.Y) <= decalSize) || (MathF.Abs(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)
{
meshBuilders[mbIndex].Add(vertices[0]);
meshBuilders[mbIndex].Add(vertices[1]);
meshBuilders[mbIndex].Add(vertices[2]);
}
}
}
// Clipping time! We need to clip against all 6 directions
Vector3[] planes =
{
new(1, 0, 0),
new(-1, 0, 0),
new(0, 1, 0),
new(0, -1, 0),
new(0, 0, 1),
new(0, 0, -1)
};
for (var face = 0; face < 6; face++)
{
// Swap current model builder (so we read from the one we just wrote to)
mbIndex = 1 - mbIndex;
var inMesh = meshBuilders[1 - mbIndex];
var outMesh = meshBuilders[mbIndex];
// Reset write builder
outMesh.Clear();
var s = 0.5f * decalSize;
for (var i = 0; i < inMesh.Count; i += 3)
{
Vector3 nV1, nV2, nV3, nV4;
var d1 = Vector3.Dot(inMesh[i + 0], planes[face]) - s;
var d2 = Vector3.Dot(inMesh[i + 1], planes[face]) - s;
var d3 = Vector3.Dot(inMesh[i + 2], planes[face]) - s;
var v1Out = d1 > 0;
var v2Out = d2 > 0;
var v3Out = d3 > 0;
// Calculate, how many vertices of the face lie outside of the clipping plane
var total = (v1Out ? 1 : 0) + (v2Out ? 1 : 0) + (v3Out ? 1 : 0);
switch (total)
{
case 0:
// The entire face lies inside of the plane, no clipping needed
outMesh.Add(inMesh[i]);
outMesh.Add(inMesh[i + 1]);
outMesh.Add(inMesh[i + 2]);
break;
case 1:
// One vertex lies outside of the plane, perform clipping
if (v2Out)
{
nV1 = inMesh[i];
nV2 = inMesh[i + 2];
nV3 = ClipSegment(inMesh[i + 1], nV1, planes[face], s);
nV4 = ClipSegment(inMesh[i + 1], nV2, planes[face], s);
outMesh.Add(nV3);
outMesh.Add(nV2);
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
outMesh.Add(nV4);
}
else
{
if (v1Out)
{
nV1 = inMesh[i + 1];
nV2 = inMesh[i + 2];
nV3 = ClipSegment(inMesh[i], nV1, planes[face], s);
nV4 = ClipSegment(inMesh[i], nV2, planes[face], s);
}
else // v3Out
{
nV1 = inMesh[i];
nV2 = inMesh[i + 1];
nV3 = ClipSegment(inMesh[i + 2], nV1, planes[face], s);
nV4 = ClipSegment(inMesh[i + 2], nV2, planes[face], s);
}
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
outMesh.Add(nV4);
outMesh.Add(nV3);
outMesh.Add(nV2);
}
break;
case 2:
// Two vertices lies outside of the plane, perform clipping
if (!v1Out)
{
nV1 = inMesh[i];
nV2 = ClipSegment(nV1, inMesh[i + 1], planes[face], s);
nV3 = ClipSegment(nV1, inMesh[i + 2], planes[face], s);
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
}
if (!v2Out)
{
nV1 = inMesh[i + 1];
nV2 = ClipSegment(nV1, inMesh[i + 2], planes[face], s);
nV3 = ClipSegment(nV1, inMesh[i], planes[face], s);
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
}
if (!v3Out)
{
nV1 = inMesh[i + 2];
nV2 = ClipSegment(nV1, inMesh[i], planes[face], s);
nV3 = ClipSegment(nV1, inMesh[i + 1], planes[face], s);
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
}
break;
default: // The entire face lies outside of the plane, so let's discard the corresponding vertices
break;
}
}
}
// Now we just need to re-transform the vertices
var theMesh = meshBuilders[mbIndex];
// Allocate room for UVs
if (theMesh.Count > 0)
{
var uvs = new Vector2[theMesh.Count];
for (var i = 0; i < theMesh.Count; i++)
{
var vert = theMesh[i];
// Calculate the UVs based on the projected coords
// They are clipped to (-decalSize .. decalSize) and we want them (0..1)
uvs[i] = new Vector2(vert.X / decalSize + 0.5f, vert.Y / decalSize + 0.5f);
// Tiny nudge in the normal direction so it renders properly over the mesh
vert.Z -= decalOffset;
// From projection space to world space
theMesh[i] = Vector3Transform(vert, invProj);
}
// Decal model data ready, create the mesh and return it
return BuildMesh(theMesh, uvs);
}
// Return a blank mesh as there's nothing to add
return new Mesh();
}
// Build a Mesh from builder data
private static unsafe Mesh BuildMesh(List<Vector3> builderVertices, Vector2[] uvs)
{
Mesh outMesh = new(builderVertices.Count, builderVertices.Count / 3);
outMesh.AllocVertices();
outMesh.AllocTexCoords();
var vertices = outMesh.VerticesAs<Vector3>();
var texcoords = outMesh.TexCoordsAs<Vector2>();
for (var i = 0; i < builderVertices.Count; i++)
{
vertices[i] = builderVertices[i];
texcoords[i] = uvs[i];
}
UploadMesh(ref outMesh, false);
return outMesh;
}
// Button UI element
private static bool GuiButton(Rectangle rec, string label)
{
var bgColor = Color.Gray;
var pressed = false;
if (CheckCollisionPointRec(GetMousePosition(), rec))
{
bgColor = Color.LightGray;
if (IsMouseButtonPressed(MouseButton.Left))
{
pressed = true;
}
}
DrawRectangleRec(rec, bgColor);
DrawRectangleLinesEx(rec, 2.0f, Color.DarkGray);
var fontSize = 10;
var 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, Color.DarkGray);
return pressed;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [models] example - decals");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Decals();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,145 @@
/*******************************************************************************************
*
* raylib [models] example - directional billboard
*
* Example complexity rating: [] 2/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* 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
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class DirectionalBillboard : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Directional Billboard";
public string Title => "raylib [models] example - directional billboard";
private Camera3D camera;
private Texture2D skillbot;
private float animTimer;
private uint anim;
public void Init()
{
// Set up the camera
camera = new();
camera.Position = new Vector3(2.0f, 1.0f, 2.0f); // Starting position
camera.Target = new Vector3(0.0f, 0.5f, 0.0f); // Target position
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Up vector
camera.FovY = 45.0f; // FOV
camera.Projection = CameraProjection.Perspective; // Projection type (Standard 3D perspective)
// Load billboard texture
skillbot = LoadTexture("resources/skillbot.png");
// Timer to update animation
animTimer = 0.0f;
// Animation frame
anim = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Update timer with delta time
animTimer += GetFrameTime();
// Update frame index after a certain amount of time (half a second)
if (animTimer > 0.5f)
{
animTimer = 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
var dir = (float)Math.Floor(((Vector2Angle(new Vector2(2.0f, 0.0f), new Vector2(camera.Position.X, camera.Position.Z)) / MathF.PI) * 4.0f) + 0.25f);
// Correct frame index if angle is negative
if (dir < 0.0f)
{
dir = 8.0f - Math.Abs((int)dir);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.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, new Rectangle(0.0f + (anim * 24.0f), 0.0f + (dir * 24.0f), 24.0f, 24.0f),
Vector3.Zero, new Vector3(0.0f, 1.0f, 0.0f), Vector2.One, new Vector2(0.5f, 0.0f), 0, Color.White);
EndMode3D();
// Render various variables for reference
DrawText($"animation: {anim}", 10, 10, 20, Color.DarkGray);
DrawText($"direction frame: {dir:0}", 10, 40, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// Unload billboard texture
UnloadTexture(skillbot);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - directional billboard");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new DirectionalBillboard();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,21 +1,27 @@
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class DynamicMesh
public partial class DynamicMesh : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int triangleRows = 48;
private const int vertexRows = triangleRows + 1;
public string Name => "Models / Dynamic Mesh";
public string Title => "raylib [models] example - dynamic mesh";
private Camera3D camera;
private Mesh dynamicMesh;
private Texture2D texture;
private Color[] pixels;
private Material material;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - dynamic mesh");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = Vector3.One * 1.5f;
camera.Target = camera.Position + new Vector3(1f, -0.25f, 1f);
camera.Up = Vector3.UnitY;
@ -23,18 +29,14 @@ public class DynamicMesh
camera.Projection = CameraProjection.Perspective;
// Generate a dynamic mesh using utils to allocate/access mesh attribute data
const int triangleRows = 48;
const int vertexRows = triangleRows + 1;
Mesh dynamicMesh = new(vertexRows * vertexRows, triangleRows * triangleRows * 2);
dynamicMesh = new(vertexRows * vertexRows, triangleRows * triangleRows * 2);
dynamicMesh.AllocVertices();
dynamicMesh.AllocTexCoords();
dynamicMesh.AllocIndices();
Span<Vector3> vertices = dynamicMesh.VerticesAs<Vector3>();
Span<Vector2> texcoords = dynamicMesh.TexCoordsAs<Vector2>();
Span<ushort> indices = dynamicMesh.IndicesAs<ushort>();
var indices = dynamicMesh.IndicesAs<ushort>();
for (int z = 0, i = 0; z < triangleRows; z++)
{
for (int x = 0; x < triangleRows; x++, i += 6)
for (var x = 0; x < triangleRows; x++, i += 6)
{
indices[i + 0] = (ushort)(x + (z * vertexRows));
indices[i + 1] = (ushort)(indices[i] + vertexRows);
@ -47,73 +49,96 @@ public class DynamicMesh
UploadMesh(ref dynamicMesh, true);
// Allocate the texture
Image image = GenImageColor(triangleRows, triangleRows, Color.Blank);
Texture2D texture = LoadTextureFromImage(image);
Color[] pixels = new Color[texture.Width * texture.Height];
var image = GenImageColor(triangleRows, triangleRows, Color.Blank);
texture = LoadTextureFromImage(image);
pixels = new Color[texture.Width * texture.Height];
UnloadImage(image);
// Load the material
Material material = LoadMaterialDefault();
material = LoadMaterialDefault();
SetMaterialTexture(ref material, MaterialMapIndex.Diffuse, texture);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
var time = (float)GetTime();
Random random = new(42);
var vertices = dynamicMesh.VerticesAs<Vector3>();
var texcoords = dynamicMesh.TexCoordsAs<Vector2>();
for (int z = 0, i = 0; z < vertexRows; z++)
{
for (var x = 0; x < vertexRows; x++, i++)
{
var noiseX = SmoothNoise(time + random.Next(10000));
var noiseZ = SmoothNoise(time + random.Next(10000));
vertices[i].X = x + noiseX - .5f;
vertices[i].Y = (noiseX + noiseZ) / 2;
vertices[i].Z = z + noiseZ - .5f;
texcoords[i].X = (x - noiseZ) / triangleRows;
texcoords[i].Y = (z - noiseX) / triangleRows;
}
}
UpdateMeshBuffer<Vector3>(dynamicMesh, Mesh.VboIdIndexVertices, vertices, 0);
UpdateMeshBuffer<Vector2>(dynamicMesh, Mesh.VboIdIndexTexCoords, texcoords, 0);
for (int y = 0, i = 0; y < texture.Height; y++)
{
for (var x = 0; x < texture.Width; x++, i++)
{
pixels[i] = new(32, 178, 170, 255);
pixels[i] = ColorBrightness(pixels[i], (SmoothNoise(time + random.Next(10000)) / 8) - (1 / 16f));
pixels[i] = ColorAlpha(pixels[i], (triangleRows - new Vector2(x, y).Length()) / triangleRows);
}
}
UpdateTexture(texture, pixels);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawMesh(dynamicMesh, material, Matrix4x4.Identity);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMaterial(material);
// Raylib.UnloadTexture(texture); <- No need to unload the texture. UnloadMaterial(Material) already unloaded it for us
UnloadMesh(dynamicMesh);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - dynamic mesh");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new DynamicMesh();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
float time = (float)GetTime();
Random random = new(42);
for (int z = 0, i = 0; z < vertexRows; z++)
{
for (int x = 0; x < vertexRows; x++, i++)
{
float noiseX = SmoothNoise(time + random.Next(10000));
float noiseZ = SmoothNoise(time + random.Next(10000));
vertices[i].X = x + noiseX - .5f;
vertices[i].Y = (noiseX + noiseZ) / 2;
vertices[i].Z = z + noiseZ - .5f;
texcoords[i].X = (x - noiseZ) / triangleRows;
texcoords[i].Y = (z - noiseX) / triangleRows;
}
}
UpdateMeshBuffer<Vector3>(dynamicMesh, Mesh.VboIdIndexVertices, vertices, 0);
UpdateMeshBuffer<Vector2>(dynamicMesh, Mesh.VboIdIndexTexCoords, texcoords, 0);
for (int y = 0, i = 0; y < texture.Height; y++)
{
for (int x = 0; x < texture.Width; x++, i++)
{
pixels[i] = new(32, 178, 170, 255);
pixels[i] = ColorBrightness(pixels[i], (SmoothNoise(time + random.Next(10000)) / 8) - (1 / 16f));
pixels[i] = ColorAlpha(pixels[i], (triangleRows - new Vector2(x, y).Length()) / triangleRows);
}
}
UpdateTexture(texture, pixels);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawMesh(dynamicMesh, material, Matrix4x4.Identity);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadMaterial(material);
// Raylib.UnloadTexture(texture); <- No need to unload the texture. UnloadMaterial(Material) already unloaded it for us
UnloadMesh(dynamicMesh);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -2,152 +2,176 @@
*
* raylib [models] example - first person maze
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 2/4
*
* Copyright (c) 2019 Ramon Santamaria (@raysan5)
* Example originally created with raylib 2.5, last time updated with raylib 3.5
*
* 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) 2019-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class FirstPersonMaze
public unsafe partial class FirstPersonMaze : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / First Person Maze";
public string Title => "raylib [models] example - first person maze";
public bool CursorDisabled => true;
private Camera3D camera;
private Texture2D cubicmap;
private Texture2D texture;
private Model model;
private Color* mapPixels;
private Vector3 mapPosition;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(0.2f, 0.4f, 0.2f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(0.2f, 0.4f, 0.2f); // Camera position
camera.Target = new Vector3(0.185f, 0.4f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
Image imMap = LoadImage("resources/cubicmap.png");
Texture2D cubicmap = LoadTextureFromImage(imMap);
Mesh mesh = GenMeshCubicmap(imMap, new Vector3(1.0f, 1.0f, 1.0f));
Model model = LoadModelFromMesh(mesh);
var imMap = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM)
cubicmap = LoadTextureFromImage(imMap); // Convert image to texture to display (VRAM)
var mesh = GenMeshCubicmap(imMap, new Vector3(1.0f, 1.0f, 1.0f));
model = LoadModelFromMesh(mesh);
// NOTE: By default each cube is mapped to one part of texture atlas
Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture
// Set map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
// Get map image data to be used for collision detection
Color* mapPixels = LoadImageColors(imMap);
UnloadImage(imMap);
mapPixels = LoadImageColors(imMap);
UnloadImage(imMap); // Unload image from RAM
Vector3 mapPosition = new(-16.0f, 0.0f, -8.0f);
Vector3 playerPosition = camera.Position;
mapPosition = new(-16.0f, 0.0f, -8.0f); // Set model position
}
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
public void Update()
{
// Update
//----------------------------------------------------------------------------------
var oldCamPos = camera.Position; // Store old camera position
// Main game loop
while (!WindowShouldClose())
UpdateCamera(ref camera, CameraMode.FirstPerson);
// Check player collision (we simplify to 2D collision detection)
Vector2 playerPos = new(camera.Position.X, camera.Position.Z);
var playerRadius = 0.1f; // Collision radius (player is modelled as a cilinder for collision)
var playerCellX = (int)(playerPos.X - mapPosition.X + 0.5f);
var playerCellY = (int)(playerPos.Y - mapPosition.Z + 0.5f);
// Out-of-limits security check
if (playerCellX < 0)
{
// Update
//----------------------------------------------------------------------------------
Vector3 oldCamPos = camera.Position;
playerCellX = 0;
}
else if (playerCellX >= cubicmap.Width)
{
playerCellX = cubicmap.Width - 1;
}
UpdateCamera(ref camera, CameraMode.FirstPerson);
if (playerCellY < 0)
{
playerCellY = 0;
}
else if (playerCellY >= cubicmap.Height)
{
playerCellY = cubicmap.Height - 1;
}
// Check player collision (we simplify to 2D collision detection)
Vector2 playerPos = new(camera.Position.X, camera.Position.Z);
// Collision radius (player is modelled as a cilinder for collision)
float playerRadius = 0.1f;
int playerCellX = (int)(playerPos.X - mapPosition.X + 0.5f);
int playerCellY = (int)(playerPos.Y - mapPosition.Z + 0.5f);
// Out-of-limits security check
if (playerCellX < 0)
// Check map collisions using image data and player position against surrounding cells only
for (var y = playerCellY - 1; y <= playerCellY + 1; y++)
{
// Avoid map accessing out of bounds
if ((y >= 0) && (y < cubicmap.Height))
{
playerCellX = 0;
}
else if (playerCellX >= cubicmap.Width)
{
playerCellX = cubicmap.Width - 1;
}
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++)
{
for (int x = 0; x < cubicmap.Width; x++)
for (var x = playerCellX - 1; x <= playerCellX + 1; x++)
{
Color* mapPixelsData = mapPixels;
// Collision: Color.white pixel, only check R channel
Rectangle rec = new(
mapPosition.X - 0.5f + x * 1.0f,
mapPosition.Z - 0.5f + y * 1.0f,
1.0f,
1.0f
);
bool collision = CheckCollisionCircleRec(playerPos, playerRadius, rec);
if ((mapPixelsData[y * cubicmap.Width + x].R == 255) && collision)
// 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,
new 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;
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw maze map
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.White);
EndMode3D();
DrawTextureEx(cubicmap, new Vector2(GetScreenWidth() - cubicmap.Width * 4 - 20, 20), 0.0f, 4.0f, Color.White);
DrawRectangleLines(GetScreenWidth() - cubicmap.Width * 4 - 20, 20, cubicmap.Width * 4, cubicmap.Height * 4, Color.Green);
// Draw player position radar
DrawRectangle(GetScreenWidth() - cubicmap.Width * 4 - 20 + playerCellX * 4, 20 + playerCellY * 4, 4, 4, Color.Red);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.White); // Draw maze map
EndMode3D();
DrawTextureEx(cubicmap, new Vector2(GetScreenWidth() - cubicmap.Width * 4 - 20, 20), 0.0f, 4.0f, Color.White);
DrawRectangleLines(GetScreenWidth() - cubicmap.Width * 4 - 20, 20, cubicmap.Width * 4, cubicmap.Height * 4, Color.Green);
// Draw player position radar
DrawRectangle(GetScreenWidth() - cubicmap.Width * 4 - 20 + playerCellX * 4, 20 + playerCellY * 4, 4, 4, Color.Red);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadImageColors(mapPixels); // Unload color array
UnloadTexture(cubicmap); // Unload cubicmap texture
UnloadTexture(texture); // Unload map texture
UnloadModel(model); // Unload map model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new FirstPersonMaze();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadImageColors(mapPixels);
UnloadTexture(cubicmap);
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,83 +1,105 @@
/*******************************************************************************************
*
* raylib [models] example - Draw some basic geometric shapes (cube, sphere, cylinder...)
* raylib [models] example - geometric shapes
*
* This example has been created using raylib 1.0 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 1/4
*
* Copyright (c) 2014 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.0, last time updated with raylib 3.5
*
* 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) 2014-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class GeometricShapes
public partial class GeometricShapes : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Geometric Shapes";
public string Title => "raylib [models] example - geometric shapes";
private Camera3D camera;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
}
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red);
DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold);
DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon);
DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green);
DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime);
DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue);
DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue);
DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown);
DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold);
DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink);
DrawCapsule(new Vector3(-3.0f, 1.5f, -4.0f), new Vector3(-4.0f, -1.0f, -4.0f), 1.2f, 8, 8, Color.Violet);
DrawCapsuleWires(new Vector3(-3.0f, 1.5f, -4.0f), new Vector3(-4.0f, -1.0f, -4.0f), 1.2f, 8, 8, Color.Purple);
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new GeometricShapes();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red);
DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold);
DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon);
DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green);
DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime);
DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue);
DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue);
DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown);
DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold);
DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink);
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,90 +1,116 @@
/*******************************************************************************************
*
* raylib [models] example - Heightmap loading and drawing
* raylib [models] example - heightmap rendering
*
* This example has been created using raylib 1.8 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 1/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.8, last time updated with raylib 3.5
*
* 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class HeightmapDemo
public partial class HeightmapDemo : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Heightmap Demo";
public string Title => "raylib [models] example - heightmap rendering";
private Camera3D camera;
private Texture2D texture;
private Model model;
private Vector3 mapPosition;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing");
// Define our custom camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(18.0f, 16.0f, 18.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(18.0f, 21.0f, 18.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
Image image = LoadImage("resources/heightmap.png");
Texture2D texture = LoadTextureFromImage(image);
var image = LoadImage("resources/heightmap.png"); // Load heightmap image (RAM)
texture = LoadTextureFromImage(image); // Convert image to texture (VRAM)
Mesh mesh = GenMeshHeightmap(image, new Vector3(16, 8, 16));
Model model = LoadModelFromMesh(mesh);
var mesh = GenMeshHeightmap(image, new Vector3(16, 8, 16)); // Generate heightmap mesh (RAM and VRAM)
model = LoadModelFromMesh(mesh); // Load model from generated mesh
// Set map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 mapPosition = new(-8.0f, 0.0f, -8.0f);
mapPosition = new(-8.0f, 0.0f, -8.0f); // Define model position
UnloadImage(image);
UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.Red);
DrawGrid(20, 1.0f);
EndMode3D();
DrawTexture(texture, screenWidth - texture.Width - 20, 20, Color.White);
DrawRectangleLines(screenWidth - texture.Width - 20, 20, texture.Width, texture.Height, Color.Green);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture); // Unload texture
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new HeightmapDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.Red);
DrawGrid(20, 1.0f);
EndMode3D();
DrawTexture(texture, screenWidth - texture.Width - 20, 20, Color.White);
DrawRectangleLines(screenWidth - texture.Width - 20, 20, texture.Width, texture.Height, Color.Green);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -20,98 +20,124 @@
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class LoadingGltf
public partial class LoadingGltf : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Loading GLTF";
public string Title => "raylib [models] example - loading gltf";
private Camera3D camera;
private Model model;
private Vector3 position;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private float animCurrentFrame;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(6.0f, 6.0f, 6.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load model
model = LoadModel("resources/models/gltf/robot.glb");
position = new(0.0f, 0.0f, 0.0f); // Set model world position
// Load model animations
anims = LoadModelAnimations("resources/models/gltf/robot.glb", ref animCount);
// Animation playing variables
animIndex = 0; // Current animation playing
animCurrentFrame = 0.0f; // Current animation frame
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Select current animation
if (IsKeyPressed(KeyboardKey.Right))
{
animIndex = (animIndex + 1) % animCount;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
animIndex = (animIndex + animCount - 1) % animCount;
}
// Update model animation
animCurrentFrame = (animCurrentFrame + 1) % anims[animIndex].KeyFrameCount;
UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(
model,
position,
1f,
Color.White
);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 40, 20, Color.Maroon);
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animations data
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading gltf");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(6.0f, 6.0f, 6.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Model model = LoadModel("resources/models/gltf/robot.glb");
Vector3 position = new(0.0f, 0.0f, 0.0f);
// Load animation data
var anims = LoadModelAnimations("resources/models/gltf/robot.glb");
// Animation playing variables
int animIndex = 0;
float animCurrentFrame = 0.0f;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingGltf();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.Right))
{
animIndex = (animIndex + 1) % anims.Length;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
animIndex = (animIndex + anims.Length - 1) % anims.Length;
}
// Update model animation
animCurrentFrame = (animCurrentFrame + 1) % anims[animIndex].KeyFrameCount;
UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(
model,
position,
1f,
Color.White
);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 40, 20, Color.Maroon);
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModelAnimations(anims);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -19,99 +19,123 @@
*
********************************************************************************************/
using System.Numerics;
using System.Runtime.InteropServices;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class LoadingIqm
public partial class LoadingIqm : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Loading IQM";
public string Title => "raylib [models] example - loading iqm";
private Camera3D camera;
private Model model;
private Texture2D texture;
private Vector3 position;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private float animCurrentFrame;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(10.0f, 10.0f, 10.0f); // Camera position
camera.Target = new Vector3(0.0f, 4.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera mode type
model = LoadModel("resources/models/iqm/guy.iqm"); // Load the animated model mesh and basic data
texture = LoadTexture("resources/models/iqm/guytex.png"); // Load model texture and set material
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture); // Set model material map texture
position = new(0.0f, 0.0f, 0.0f); // Set model position
// Load animation data
anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", ref animCount);
// Animation playing variables
animIndex = 0; // Current animation playing
animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Play animation when spacebar is held down
animCurrentFrame += 1.0f;
UpdateModelAnimation(model, anims[0], animCurrentFrame);
if (animCurrentFrame >= anims[0].KeyFrameCount)
{
animCurrentFrame = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModelEx(
model,
position,
new Vector3(1.0f, 0.0f, 0.0f),
-90.0f,
new Vector3(1.0f, 1.0f, 1.0f),
Color.White
);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 10, 20, Color.Maroon);
DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadTexture(texture); // Unload texture
UnloadModelAnimations(anims, animCount); // Unload model animations data
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading iqm");
InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(10.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 4.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Model model = LoadModel("resources/models/iqm/guy.iqm");
Texture2D texture = LoadTexture("resources/models/iqm/guytex.png");
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture);
Vector3 position = new(0.0f, 0.0f, 0.0f);
// Load animation data
var anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm");
// Animation playing variables
int animIndex = 0;
float animCurrentFrame = 0.0f;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingIqm();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Play animation when spacebar is held down
animCurrentFrame += 1.0f;
UpdateModelAnimation(model, anims[0], animCurrentFrame);
if (animCurrentFrame >= anims[0].KeyFrameCount)
{
animCurrentFrame = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModelEx(
model,
position,
new Vector3(1.0f, 0.0f, 0.0f),
-90.0f,
new Vector3(1.0f, 1.0f, 1.0f),
Color.White
);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 10, 20, Color.Maroon);
DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture);
UnloadModelAnimations(anims);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,170 @@
/*******************************************************************************************
*
* raylib [models] example - loading m3d
*
* Example complexity rating: [] 2/4
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by bzt (@bztsrc) and reviewed by Ramon Santamaria (@raysan5)
*
* NOTES:
* - Model3D (M3D) fileformat specs: https://gitlab.com/bztsrc/model3d
* - Bender M3D exported: https://gitlab.com/bztsrc/model3d/-/tree/master/blender
*
* 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) 2022-2025 bzt (@bztsrc)
*
********************************************************************************************/
namespace Examples.Models;
public partial class LoadingM3d : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Loading M3D";
public string Title => "raylib [models] example - loading m3d";
private Camera3D camera;
private Model model;
private Vector3 position;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private float animCurrentFrame;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(1.5f, 1.5f, 1.5f); // Camera position
camera.Target = new Vector3(0.0f, 0.4f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load model
model = LoadModel("resources/models/m3d/cesium_man.m3d"); // Load the animated model mesh and basic data
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model position
// Load animation data
animCount = 0;
anims = LoadModelAnimations("resources/models/m3d/cesium_man.m3d", ref animCount);
// Animation playing variables
animIndex = 0; // Current animation playing
animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Select current animation
if (IsKeyPressed(KeyboardKey.Right))
{
animIndex = (animIndex + 1) % animCount;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
animIndex = (animIndex + animCount - 1) % animCount;
}
// Update model animation
animCurrentFrame += 1.0f;
if (animCurrentFrame >= anims[animIndex].KeyFrameCount)
{
animCurrentFrame = 0.0f;
}
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw 3d model with texture
if (!IsKeyDown(KeyboardKey.Space))
{
DrawModel(model, position, 1.0f, Color.White);
}
else
{
// Draw the animated skeleton
DrawModelSkeleton(model.Skeleton, anims[animIndex].KeyframePoses[(int)animCurrentFrame], 1.0f, Color.Red);
}
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 10, 20, Color.LightGray);
DrawText("Press SPACE to draw skeleton", 10, 40, 20, Color.Maroon);
DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animations data
UnloadModel(model); // Unload model
}
// Draw model skeleton
private static unsafe void DrawModelSkeleton(ModelSkeleton skeleton, Transform* pose, float scale, Color color)
{
// Loop to (boneCount - 1) because the last one is a special "no bone" bone,
// needed to workaround buggy models without a -1, a cube is always drawn at the origin
for (var i = 0; i < skeleton.BoneCount - 1; i++)
{
// Display the frame-pose skeleton
DrawCube(pose[i].Translation, scale * 0.05f, scale * 0.05f, scale * 0.05f, color);
if (skeleton.Bones[i].Parent >= 0)
{
DrawLine3D(pose[i].Translation, pose[skeleton.Bones[i].Parent].Translation, color);
}
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading m3d");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingM3d();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,244 @@
/*******************************************************************************************
*
* raylib [models] example - loading vox
*
* Example complexity rating: [] 1/4
*
* Example originally created with raylib 4.0, last time updated with raylib 4.0
*
* Example contributed by Johann Nadalutti (@procfxgen) 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) 2021-2025 Johann Nadalutti (@procfxgen) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
using Examples.Shared;
namespace Examples.Models;
public partial class LoadingVox : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxVoxFiles = 4;
private const int MaxLights = 4;
#if BROWSER
private const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Models / Loading VOX";
public string Title => "raylib [models] example - loading vox";
private static readonly string[] VoxFileNames =
{
"resources/models/vox/chr_knight.vox",
"resources/models/vox/chr_sword.vox",
"resources/models/vox/monu9.vox",
"resources/models/vox/fez.vox"
};
private Camera3D camera;
private Model[] models;
private int currentModel;
private Vector3 modelpos;
private Vector3 camerarot;
private Shader shader;
private Light[] lights;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(10.0f, 10.0f, 10.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load MagicaVoxel files
models = new Model[MaxVoxFiles];
for (var i = 0; i < MaxVoxFiles; i++)
{
// Load VOX file and measure time
var t0 = GetTime() * 1000.0;
models[i] = LoadModel(VoxFileNames[i]);
var t1 = GetTime() * 1000.0;
TraceLog(TraceLogLevel.Info, $"[{VoxFileNames[i]}] Model file loaded in {t1 - t0:0.000} ms");
// Compute model translation matrix to center model on draw position (0, 0 , 0)
var bb = GetModelBoundingBox(models[i]);
Vector3 center = new();
center.X = bb.Min.X + ((bb.Max.X - bb.Min.X) / 2);
center.Z = bb.Min.Z + ((bb.Max.Z - bb.Min.Z) / 2);
var matTranslate = MatrixTranslate(-center.X, 0, -center.Z);
models[i].Transform = matTranslate;
}
currentModel = 0;
modelpos = new Vector3(0, 0, 0);
camerarot = new Vector3(0, 0, 0);
// Load voxel shader
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/voxel_lighting.vs",
$"resources/shaders/glsl{GlslVersion}/voxel_lighting.fs"
);
// Get some required shader locations
shader.Locs[(int)ShaderLocationIndex.VectorView] = 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[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocation(shader, "matModel");
// Ambient light level (some basic lighting)
var ambientLoc = GetShaderLocation(shader, "ambient");
Raylib.SetShaderValue(shader, ambientLoc, new[] { 0.1f, 0.1f, 0.1f, 1.0f }, ShaderUniformDataType.Vec4);
// Assign out lighting shader to model
for (var i = 0; i < MaxVoxFiles; i++)
{
for (var j = 0; j < models[i].MaterialCount; j++)
{
models[i].Materials[j].Shader = shader;
}
}
// Create lights
lights = new Light[MaxLights];
lights[0] = Rlights.CreateLight(0, LightType.Point, new Vector3(-20, 20, -20), Vector3.Zero, Color.Gray, shader);
lights[1] = Rlights.CreateLight(1, LightType.Point, new Vector3(20, -20, 20), Vector3.Zero, Color.Gray, shader);
lights[2] = Rlights.CreateLight(2, LightType.Point, new Vector3(-20, 20, 20), Vector3.Zero, Color.Gray, shader);
lights[3] = Rlights.CreateLight(3, LightType.Point, new Vector3(20, -20, -20), Vector3.Zero, Color.Gray, shader);
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsMouseButtonDown(MouseButton.Middle))
{
var mouseDelta = GetMouseDelta();
camerarot.X = mouseDelta.X * 0.05f;
camerarot.Y = mouseDelta.Y * 0.05f;
}
else
{
camerarot.X = 0;
camerarot.Y = 0;
}
// Update camere movement, custom controls
UpdateCameraPro(ref camera,
new Vector3(
(IsKeyDown(KeyboardKey.W) || IsKeyDown(KeyboardKey.Up) ? 0.1f : 0.0f) - (IsKeyDown(KeyboardKey.S) || IsKeyDown(KeyboardKey.Down) ? 0.1f : 0.0f), // Move forward-backward
(IsKeyDown(KeyboardKey.D) || IsKeyDown(KeyboardKey.Right) ? 0.1f : 0.0f) - (IsKeyDown(KeyboardKey.A) || IsKeyDown(KeyboardKey.Left) ? 0.1f : 0.0f), // Move right-left
0.0f), // Move up-down
camerarot, // Camera rotation
GetMouseWheelMove() * -2.0f); // Move to target (zoom)
// Cycle between models on mouse click
if (IsMouseButtonPressed(MouseButton.Left))
{
currentModel = (currentModel + 1) % MaxVoxFiles;
}
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
Raylib.SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], camera.Position, ShaderUniformDataType.Vec3);
// Update light values (actually, only enable/disable them)
for (var i = 0; i < MaxLights; i++)
{
Rlights.UpdateLightValues(shader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw 3D model
BeginMode3D(camera);
DrawModel(models[currentModel], modelpos, 1.0f, Color.White);
DrawGrid(10, 1.0f);
// Draw spheres to show where the lights are
for (var i = 0; i < MaxLights; 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();
// Display info
DrawRectangle(10, 40, 340, 70, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 40, 340, 70, Fade(Color.DarkBlue, 0.5f));
DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, Color.Blue);
DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, Color.Blue);
DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, Color.Blue);
DrawText($"VOX model file: {GetFileName(VoxFileNames[currentModel])}", 10, 10, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// Unload models data (GPU VRAM)
for (var i = 0; i < MaxVoxFiles; i++)
{
UnloadModel(models[i]);
}
UnloadShader(shader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading vox");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingVox();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,21 +1,23 @@
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class MeshDemo
public partial class MeshDemo : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Mesh Demo";
public string Title => "raylib [models] example - mesh demo";
private Camera3D camera;
private Model model;
private Texture2D texture;
private float rotationAngle;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh demo");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = Vector3.One * 1.5f;
camera.Target = Vector3.Zero;
camera.Up = Vector3.UnitY;
@ -28,10 +30,10 @@ public class MeshDemo
tetrahedron.AllocTexCoords();
tetrahedron.AllocColors();
tetrahedron.AllocIndices();
Span<Vector3> vertices = tetrahedron.VerticesAs<Vector3>();
Span<Vector2> texcoords = tetrahedron.TexCoordsAs<Vector2>();
Span<Color> colors = tetrahedron.ColorsAs<Color>();
Span<ushort> indices = tetrahedron.IndicesAs<ushort>();
var vertices = tetrahedron.VerticesAs<Vector3>();
var texcoords = tetrahedron.TexCoordsAs<Vector2>();
var colors = tetrahedron.ColorsAs<Color>();
var indices = tetrahedron.IndicesAs<ushort>();
// Coordinates for a regular tetrahedron
vertices[0] = new(MathF.Sqrt(8f / 9f), 0f, -1f / 3f);
@ -65,49 +67,69 @@ public class MeshDemo
indices[10] = 3;
indices[11] = 2;
float rotationAngle = 0f;
rotationAngle = 0f;
Raylib.UploadMesh(ref tetrahedron, false);
Model model = Raylib.LoadModelFromMesh(tetrahedron);
model = Raylib.LoadModelFromMesh(tetrahedron);
Image image = Raylib.GenImagePerlinNoise(16, 16, 0, 0, 1000f);
var image = Raylib.GenImagePerlinNoise(16, 16, 0, 0, 1000f);
Raylib.ImageBlurGaussian(ref image, 2);
Raylib.ImageColorBrightness(ref image, 100);
Raylib.ImageDither(ref image, 4, 4, 4, 4);
Texture2D texture = Raylib.LoadTextureFromImage(image);
texture = Raylib.LoadTextureFromImage(image);
Raylib.UnloadImage(image);
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
rotationAngle = Raymath.Wrap(rotationAngle += 1f, 0f, 360f);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
Raylib.DrawModelEx(model, Vector3.Zero, Vector3.UnitX, rotationAngle, Vector3.One, Color.White);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture);
UnloadModel(model);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh demo");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new MeshDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
rotationAngle = Raymath.Wrap(rotationAngle += 1f, 0f, 360f);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
Raylib.DrawModelEx(model, Vector3.Zero, Vector3.UnitX, rotationAngle, Vector3.One, Color.White);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -1,38 +1,45 @@
/*******************************************************************************************
*
* raylib example - procedural mesh generation
* raylib [models] example - mesh generation
*
* This example has been created using raylib 1.8 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 2/4
*
* Copyright (c) 2017 Ramon Santamaria (Ray San)
* Example originally created with raylib 1.8, last time updated with raylib 4.0
*
* 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) 2017-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class MeshGeneration
public partial class MeshGeneration : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Mesh Generation";
public string Title => "raylib [models] example - mesh generation";
private Texture2D texture;
private Model[] models;
private Camera3D camera;
private Vector3 position;
private int currentModel;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh generation");
// We generate a isChecked image for texturing
Image isChecked = GenImageChecked(2, 2, 1, 1, Color.Red, Color.Green);
Texture2D texture = LoadTextureFromImage(isChecked);
// We generate a checked image for texturing
var isChecked = GenImageChecked(2, 2, 1, 1, Color.Red, Color.Green);
texture = LoadTextureFromImage(isChecked);
UnloadImage(isChecked);
Model[] models = new Model[9];
models = new Model[9];
models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 5, 5));
models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 4, 3));
models[1] = LoadModelFromMesh(GenMeshCube(2.0f, 1.0f, 2.0f));
models[2] = LoadModelFromMesh(GenMeshSphere(2, 32, 32));
models[3] = LoadModelFromMesh(GenMeshHemiSphere(2, 16, 16));
@ -42,15 +49,17 @@ public class MeshGeneration
models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f));
models[8] = LoadModelFromMesh(GenMeshCustom());
// Set isChecked texture as default diffuse component for all models material
for (int i = 0; i < models.Length; i++)
// NOTE: Generated meshes could be exported using ExportMesh()
// Set checked texture as default diffuse component for all models material
for (var i = 0; i < models.Length; i++)
{
// Set map diffuse texture
Raylib.SetMaterialTexture(ref models[i], 0, MaterialMapIndex.Albedo, ref texture);
}
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(5.0f, 5.0f, 5.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
@ -58,92 +67,102 @@ public class MeshGeneration
camera.Projection = CameraProjection.Perspective;
// Model drawing position
Vector3 position = new(0.0f, 0.0f, 0.0f);
position = new(0.0f, 0.0f, 0.0f);
int currentModel = 0;
currentModel = 0;
}
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Main game loop
while (!WindowShouldClose())
if (IsMouseButtonPressed(MouseButton.Left))
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsMouseButtonPressed(MouseButton.Left))
{
// Cycle between the textures
currentModel = (currentModel + 1) % models.Length;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(models[currentModel], position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawRectangle(30, 400, 310, 30, ColorAlpha(Color.SkyBlue, 0.5f));
DrawRectangleLines(30, 400, 310, 30, ColorAlpha(Color.DarkBlue, 0.5f));
DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL MODELS", 40, 410, 10, Color.Blue);
switch (currentModel)
{
case 0:
DrawText("PLANE", 680, 10, 20, Color.DarkBlue);
break;
case 1:
DrawText("CUBE", 680, 10, 20, Color.DarkBlue);
break;
case 2:
DrawText("SPHERE", 680, 10, 20, Color.DarkBlue);
break;
case 3:
DrawText("HEMISPHERE", 640, 10, 20, Color.DarkBlue);
break;
case 4:
DrawText("CYLINDER", 680, 10, 20, Color.DarkBlue);
break;
case 5:
DrawText("TORUS", 680, 10, 20, Color.DarkBlue);
break;
case 6:
DrawText("KNOT", 680, 10, 20, Color.DarkBlue);
break;
case 7:
DrawText("POLY", 680, 10, 20, Color.DarkBlue);
break;
case 8:
DrawText("Custom (triagnle)", 580, 10, 20, Color.DarkBlue);
break;
default:
break;
}
EndDrawing();
//----------------------------------------------------------------------------------
currentModel = (currentModel + 1) % models.Length; // Cycle between the textures
}
// De-Initialization
//--------------------------------------------------------------------------------------
for (int i = 0; i < models.Length; i++)
if (IsKeyPressed(KeyboardKey.Right))
{
currentModel++;
if (currentModel >= models.Length)
{
currentModel = 0;
}
}
else if (IsKeyPressed(KeyboardKey.Left))
{
currentModel--;
if (currentModel < 0)
{
currentModel = models.Length - 1;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(models[currentModel], position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawRectangle(30, 400, 310, 30, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(30, 400, 310, 30, Fade(Color.DarkBlue, 0.5f));
DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL MODELS", 40, 410, 10, Color.Blue);
switch (currentModel)
{
case 0:
DrawText("PLANE", 680, 10, 20, Color.DarkBlue);
break;
case 1:
DrawText("CUBE", 680, 10, 20, Color.DarkBlue);
break;
case 2:
DrawText("SPHERE", 680, 10, 20, Color.DarkBlue);
break;
case 3:
DrawText("HEMISPHERE", 640, 10, 20, Color.DarkBlue);
break;
case 4:
DrawText("CYLINDER", 680, 10, 20, Color.DarkBlue);
break;
case 5:
DrawText("TORUS", 680, 10, 20, Color.DarkBlue);
break;
case 6:
DrawText("KNOT", 680, 10, 20, Color.DarkBlue);
break;
case 7:
DrawText("POLY", 680, 10, 20, Color.DarkBlue);
break;
case 8:
DrawText("Custom (triangle)", 580, 10, 20, Color.DarkBlue);
break;
default:
break;
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture); // Unload texture
// Unload models data (GPU VRAM)
for (var i = 0; i < models.Length; i++)
{
UnloadModel(models[i]);
}
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
// Generate a simple triangle mesh from code
@ -153,9 +172,9 @@ public class MeshGeneration
mesh.AllocVertices();
mesh.AllocTexCoords();
mesh.AllocNormals();
Span<Vector3> vertices = mesh.VerticesAs<Vector3>();
Span<Vector2> texcoords = mesh.TexCoordsAs<Vector2>();
Span<Vector3> normals = mesh.NormalsAs<Vector3>();
var vertices = mesh.VerticesAs<Vector3>();
var texcoords = mesh.TexCoordsAs<Vector2>();
var normals = mesh.NormalsAs<Vector3>();
// Vertex at (0, 0, 0)
vertices[0] = new(0, 0, 0);
@ -177,4 +196,32 @@ public class MeshGeneration
return mesh;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh generation");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new MeshGeneration();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,34 +1,54 @@
/*******************************************************************************************
*
* raylib [models] example - Mesh picking in 3d mode, ground plane, triangle, mesh
* raylib [models] example - mesh picking
*
* This example has been created using raylib 1.7 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 3/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example contributed by Joel Davis (@joeld42)
* Example originally created with raylib 1.7, last time updated with raylib 4.0
*
* Example contributed by Joel Davis (@joeld42) 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) 2017-2025 Joel Davis (@joeld42) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath;
namespace Examples.Models;
public class MeshPicking
public partial class MeshPicking : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Mesh Picking";
public string Title => "raylib [models] example - mesh picking";
private Camera3D camera;
private Ray ray;
private Model tower;
private Texture2D texture;
private Vector3 towerPos;
private BoundingBox towerBBox;
private Vector3 g0;
private Vector3 g1;
private Vector3 g2;
private Vector3 g3;
private Vector3 ta;
private Vector3 tb;
private Vector3 tc;
private Vector3 bary;
private Vector3 sp;
private float sr;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh picking");
// Define the camera to look into our 3d world
Camera3D camera;
camera = new();
camera.Position = new Vector3(20.0f, 20.0f, 20.0f);
camera.Target = new Vector3(0.0f, 8.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.6f, 0.0f);
@ -36,210 +56,232 @@ public class MeshPicking
camera.Projection = CameraProjection.Perspective;
// Picking ray
Ray ray = new();
ray = new();
Model tower = LoadModel("resources/models/obj/turret.obj");
Texture2D texture = LoadTexture("resources/models/obj/turret_diffuse.png");
tower = LoadModel("resources/models/obj/turret.obj");
texture = LoadTexture("resources/models/obj/turret_diffuse.png");
Raylib.SetMaterialTexture(ref tower, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 towerPos = new(0.0f, 0.0f, 0.0f);
BoundingBox towerBBox = GetMeshBoundingBox(tower.Meshes[0]);
towerPos = new(0.0f, 0.0f, 0.0f);
towerBBox = GetMeshBoundingBox(tower.Meshes[0]);
// Ground quad
Vector3 g0 = new(-50.0f, 0.0f, -50.0f);
Vector3 g1 = new(-50.0f, 0.0f, 50.0f);
Vector3 g2 = new(50.0f, 0.0f, 50.0f);
Vector3 g3 = new(50.0f, 0.0f, -50.0f);
g0 = new(-50.0f, 0.0f, -50.0f);
g1 = new(-50.0f, 0.0f, 50.0f);
g2 = new(50.0f, 0.0f, 50.0f);
g3 = new(50.0f, 0.0f, -50.0f);
// Test triangle
Vector3 ta = new(-25.0f, 0.5f, 0.0f);
Vector3 tb = new(-4.0f, 2.5f, 1.0f);
Vector3 tc = new(-8.0f, 6.5f, 0.0f);
ta = new(-25.0f, 0.5f, 0.0f);
tb = new(-4.0f, 2.5f, 1.0f);
tc = new(-8.0f, 6.5f, 0.0f);
Vector3 bary = new(0.0f, 0.0f, 0.0f);
bary = new(0.0f, 0.0f, 0.0f);
// Test sphere
Vector3 sp = new(-30.0f, 5.0f, 5.0f);
float sr = 4.0f;
sp = new(-30.0f, 5.0f, 5.0f);
sr = 4.0f;
}
public unsafe void Update()
{
//----------------------------------------------------------------------------------
// Update
//----------------------------------------------------------------------------------
if (IsCursorHidden())
{
UpdateCamera(ref camera, CameraMode.FirstPerson);
}
// Toggle camera controls
if (IsMouseButtonPressed(MouseButton.Right))
{
if (IsCursorHidden())
{
EnableCursor();
}
else
{
DisableCursor();
}
}
// Display information about closest hit
RayCollision collision = new();
var hitObjectName = "None";
collision.Distance = float.MaxValue;
collision.Hit = false;
var cursorColor = Color.White;
// Get ray and test against objects
ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check ray collision against ground quad
var groundHitInfo = GetRayCollisionQuad(ray, g0, g1, g2, g3);
if (groundHitInfo.Hit && (groundHitInfo.Distance < collision.Distance))
{
collision = groundHitInfo;
cursorColor = Color.Green;
hitObjectName = "Ground";
}
// Check ray collision against test triangle
var triHitInfo = GetRayCollisionTriangle(ray, ta, tb, tc);
if (triHitInfo.Hit && (triHitInfo.Distance < collision.Distance))
{
collision = triHitInfo;
cursorColor = Color.Purple;
hitObjectName = "Triangle";
bary = Vector3Barycenter(collision.Point, ta, tb, tc);
}
// Check ray collision against test sphere
var sphereHitInfo = GetRayCollisionSphere(ray, sp, sr);
if ((sphereHitInfo.Hit) && (sphereHitInfo.Distance < collision.Distance))
{
collision = sphereHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Sphere";
}
// Check ray collision against bounding box first, before trying the full ray-mesh test
var boxHitInfo = GetRayCollisionBox(ray, towerBBox);
if (boxHitInfo.Hit && boxHitInfo.Distance < collision.Distance)
{
collision = boxHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Box";
// Check ray collision against model meshes
RayCollision meshHitInfo = new();
for (var m = 0; m < tower.MeshCount; m++)
{
// NOTE: We consider the model.Transform for the collision check but
// it can be checked against any transform matrix, used when checking against same
// model drawn multiple times with multiple transforms
meshHitInfo = GetRayCollisionMesh(ray, tower.Meshes[m], tower.Transform);
if (meshHitInfo.Hit)
{
// Save the closest hit mesh
if ((!collision.Hit) || (collision.Distance > meshHitInfo.Distance))
{
collision = meshHitInfo;
}
break;
}
}
if (meshHitInfo.Hit)
{
collision = meshHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Mesh";
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw the tower
// WARNING: If scale is different than 1.0f,
// not considered by GetRayCollisionModel()
DrawModel(tower, towerPos, 1.0f, Color.White);
// Draw the test triangle
DrawLine3D(ta, tb, Color.Purple);
DrawLine3D(tb, tc, Color.Purple);
DrawLine3D(tc, ta, Color.Purple);
// Draw the test sphere
DrawSphereWires(sp, sr, 8, 8, Color.Purple);
// Draw the mesh bbox if we hit it
if (boxHitInfo.Hit)
{
DrawBoundingBox(towerBBox, Color.Lime);
}
// If we hit something, draw the cursor at the hit point
if (collision.Hit)
{
DrawCube(collision.Point, 0.3f, 0.3f, 0.3f, cursorColor);
DrawCubeWires(collision.Point, 0.3f, 0.3f, 0.3f, Color.Red);
var normalEnd = collision.Point + collision.Normal;
DrawLine3D(collision.Point, normalEnd, Color.Red);
}
DrawRay(ray, Color.Maroon);
DrawGrid(10, 10.0f);
EndMode3D();
// Draw some debug GUI text
DrawText($"Hit Object: {hitObjectName}", 10, 50, 10, Color.Black);
if (collision.Hit)
{
var ypos = 70;
DrawText($"Distance: {collision.Distance}", 10, ypos, 10, Color.Black);
DrawText($"Hit Pos: {collision.Point}", 10, ypos + 15, 10, Color.Black);
DrawText($"Hit Norm: {collision.Normal}", 10, ypos + 30, 10, Color.Black);
if (triHitInfo.Hit && hitObjectName == "Triangle")
{
DrawText($"Barycenter: {bary}", 10, ypos + 45, 10, Color.Black);
}
}
DrawText("Right click mouse to toggle camera controls", 10, 430, 10, Color.Gray);
DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(tower);
UnloadTexture(texture);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh picking");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new MeshPicking();
game.Init();
//----------------------------------------------------------------------------------
// Main game loop
//--------------------------------------------------------------------------------------
while (!WindowShouldClose())
{
//----------------------------------------------------------------------------------
// Update
//----------------------------------------------------------------------------------
if (IsCursorHidden())
{
UpdateCamera(ref camera, CameraMode.FirstPerson);
}
// Toggle camera controls
if (IsMouseButtonPressed(MouseButton.Right))
{
if (IsCursorHidden())
{
EnableCursor();
}
else
{
DisableCursor();
}
}
// Display information about closest hit
RayCollision collision = new();
string hitObjectName = "None";
collision.Distance = float.MaxValue;
collision.Hit = false;
Color cursorColor = Color.White;
// Get ray and test against objects
ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check ray collision aginst ground quad
RayCollision groundHitInfo = GetRayCollisionQuad(ray, g0, g1, g2, g3);
if (groundHitInfo.Hit && (groundHitInfo.Distance < collision.Distance))
{
collision = groundHitInfo;
cursorColor = Color.Green;
hitObjectName = "Ground";
}
// Check ray collision against test triangle
RayCollision triHitInfo = GetRayCollisionTriangle(ray, ta, tb, tc);
if (triHitInfo.Hit && (triHitInfo.Distance < collision.Distance))
{
collision = triHitInfo;
cursorColor = Color.Purple;
hitObjectName = "Triangle";
bary = Vector3Barycenter(collision.Point, ta, tb, tc);
}
// Check ray collision against test sphere
RayCollision sphereHitInfo = GetRayCollisionSphere(ray, sp, sr);
if ((sphereHitInfo.Hit) && (sphereHitInfo.Distance < collision.Distance))
{
collision = sphereHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Sphere";
}
// Check ray collision against bounding box first, before trying the full ray-mesh test
RayCollision boxHitInfo = GetRayCollisionBox(ray, towerBBox);
if (boxHitInfo.Hit && boxHitInfo.Distance < collision.Distance)
{
collision = boxHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Box";
// Check ray collision against model meshes
RayCollision meshHitInfo = new();
for (int m = 0; m < tower.MeshCount; m++)
{
// NOTE: We consider the model.Transform for the collision check but
// it can be checked against any transform matrix, used when checking against same
// model drawn multiple times with multiple transforms
meshHitInfo = GetRayCollisionMesh(ray, tower.Meshes[m], tower.Transform);
if (meshHitInfo.Hit)
{
// Save the closest hit mesh
if ((!collision.Hit) || (collision.Distance > meshHitInfo.Distance))
{
collision = meshHitInfo;
}
break;
}
}
if (meshHitInfo.Hit)
{
collision = meshHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Mesh";
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw the tower
DrawModel(tower, towerPos, 1.0f, Color.White);
// Draw the test triangle
DrawLine3D(ta, tb, Color.Purple);
DrawLine3D(tb, tc, Color.Purple);
DrawLine3D(tc, ta, Color.Purple);
// Draw the test sphere
DrawSphereWires(sp, sr, 8, 8, Color.Purple);
// Draw the mesh bbox if we hit it
if (boxHitInfo.Hit)
{
DrawBoundingBox(towerBBox, Color.Lime);
}
// If we hit something, draw the cursor at the hit point
if (collision.Hit)
{
DrawCube(collision.Point, 0.3f, 0.3f, 0.3f, cursorColor);
DrawCubeWires(collision.Point, 0.3f, 0.3f, 0.3f, Color.Red);
Vector3 normalEnd = collision.Point + collision.Normal;
DrawLine3D(collision.Point, normalEnd, Color.Red);
}
DrawRay(ray, Color.Maroon);
DrawGrid(10, 10.0f);
EndMode3D();
// Draw some debug GUI text
DrawText($"Hit Object: {hitObjectName}", 10, 50, 10, Color.Black);
if (collision.Hit)
{
int ypos = 70;
DrawText($"Distance: {collision.Distance}", 10, ypos, 10, Color.Black);
DrawText($"Hit Pos: {collision.Point}", 10, ypos + 15, 10, Color.Black);
DrawText($"Hit Norm: {collision.Normal}", 10, ypos + 30, 10, Color.Black);
if (triHitInfo.Hit)
{
DrawText($"Barycenter: {bary}", 10, ypos + 45, 10, Color.Black);
}
}
DrawText("Right click mouse to toggle camera controls", 10, 430, 10, Color.Gray);
DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(tower);
UnloadTexture(texture);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -1,34 +1,36 @@
/*******************************************************************************************
*
* raylib [models] example - Draw textured cube
* raylib [models] example - textured cube
*
* Example complexity rating: [] 2/4
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* 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) 2022-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2022-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class ModelCubeTexture
public partial class ModelCubeTexture : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Model Cube Texture";
public string Title => "raylib [models] example - textured cube";
private Camera3D camera;
private Texture2D texture;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - draw cube texture");
// Define the camera to look into our 3d world
Camera3D camera;
camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
@ -36,60 +38,48 @@ public class ModelCubeTexture
camera.Projection = CameraProjection.Perspective;
// Load texture to be applied to the cubes sides
Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
texture = LoadTexture("resources/cubicmap_atlas.png");
}
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
BeginMode3D(camera);
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw cube with an applied texture
DrawCubeTexture(texture, new Vector3(-2.0f, 2.0f, 0.0f), 2.0f, 4.0f, 2.0f, Color.White);
BeginMode3D(camera);
// Draw cube with an applied texture, but only a defined rectangle piece of the texture
DrawCubeTextureRec(
texture,
new Rectangle(0, texture.Height / 2, texture.Width / 2, texture.Height / 2),
new Vector3(2.0f, 1.0f, 0.0f),
2.0f,
2.0f,
2.0f,
Color.White
);
// Draw cube with an applied texture
DrawCubeTexture(texture, new Vector3(-2.0f, 2.0f, 0.0f), 2.0f, 4.0f, 2.0f, Color.White);
DrawGrid(10, 1.0f);
// Draw cube with an applied texture, but only a defined rectangle piece of the texture
DrawCubeTextureRec(
texture,
new Rectangle(0, texture.Height / 2, texture.Width / 2, texture.Height / 2),
new Vector3(2.0f, 1.0f, 0.0f),
2.0f,
2.0f,
2.0f,
Color.White
);
EndMode3D();
DrawGrid(10, 1.0f);
EndDrawing();
//----------------------------------------------------------------------------------
}
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
public void Unload()
{
UnloadTexture(texture);
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
// Draw cube textured
// NOTE: Cube position is the center position
static void DrawCubeTexture(
private static void DrawCubeTexture(
Texture2D texture,
Vector3 position,
float width,
@ -98,9 +88,9 @@ public class ModelCubeTexture
Color color
)
{
float x = position.X;
float y = position.Y;
float z = position.Z;
var x = position.X;
var y = position.Y;
var z = position.Z;
// Set desired texture to be enabled while drawing following vertex data
Rlgl.SetTexture(texture.Id);
@ -218,7 +208,7 @@ public class ModelCubeTexture
}
// Draw cube with texture piece applied to all faces
static void DrawCubeTextureRec(
private static void DrawCubeTextureRec(
Texture2D texture,
Rectangle source,
Vector3 position,
@ -228,11 +218,11 @@ public class ModelCubeTexture
Color color
)
{
float x = position.X;
float y = position.Y;
float z = position.Z;
float texWidth = (float)texture.Width;
float texHeight = (float)texture.Height;
var x = position.X;
var y = position.Y;
var z = position.Z;
var texWidth = (float)texture.Width;
var texHeight = (float)texture.Height;
// Set desired texture to be enabled while drawing following vertex data
Rlgl.SetTexture(texture.Id);
@ -312,5 +302,32 @@ public class ModelCubeTexture
Rlgl.SetTexture(0);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - textured cube");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new ModelCubeTexture();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,157 +1,197 @@
/*******************************************************************************************
*
* raylib [models] example - Models loading
* raylib [models] example - loading
*
* raylib supports multiple models file formats:
* Example complexity rating: [] 1/4
*
* - OBJ > Text file, must include vertex position-texcoords-normals information,
* if files references some .mtl materials file, it will be loaded (or try to)
* - GLTF > Modern text/binary file format, includes lot of information and it could
* also reference external files, raylib will try loading mesh and materials data
* - IQM > Binary file format including mesh vertex data but also animation data,
* raylib can load .iqm animations.
* NOTE: raylib supports multiple models file formats:
*
* This example has been created using raylib 2.6 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* - OBJ > Text file format. Must include vertex position-texcoords-normals information,
* if .obj references some .mtl materials file, it will be tried to be loaded
* - GLTF/GLB > Text/binary file formats. Includes lot of information and it could
* also reference external files, mesh and materials data will be tried to be loaded
* - IQM > Binary file format. Includes mesh vertex data but also animation data,
* meshes and animation data can be loaded
* - VOX > Binary file format. MagikaVoxel mesh format:
* https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
* - M3D > Binary file format. Model 3D format:
* https://bztsrc.gitlab.io/model3d
*
* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
* Example originally created with raylib 2.0, last time updated with raylib 4.2
*
* 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) 2014-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class ModelLoading
public partial class ModelLoading : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Model Loading";
public string Title => "raylib [models] example - loading";
private Camera3D camera;
private Model model;
private Texture2D texture;
private Vector3 position;
private BoundingBox bounds;
private bool selected;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - models loading");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(50.0f, 50.0f, 50.0f);
camera.Target = new Vector3(0.0f, 10.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(50.0f, 50.0f, 50.0f); // Camera position
camera.Target = new Vector3(0.0f, 12.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera mode type
Model model = LoadModel("resources/models/obj/castle.obj");
Texture2D texture = LoadTexture("resources/models/obj/castle_diffuse.png");
model = LoadModel("resources/models/obj/castle.obj"); // Load model
texture = LoadTexture("resources/models/obj/castle_diffuse.png"); // Load model texture
// Set map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 position = new(0.0f, 0.0f, 0.0f);
BoundingBox bounds = GetMeshBoundingBox(model.Meshes[0]);
position = new(0.0f, 0.0f, 0.0f); // Set model position
bounds = GetMeshBoundingBox(model.Meshes[0]); // Set model bounds
// NOTE: bounds are calculated from the original size of the model,
// if model is scaled on drawing, bounds must be also scaled
bool selected = false;
selected = false;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
#if BROWSER
// NOTE: drag-and-drop model loading is not supported in the browser host; default loaded model is kept.
#else
// Load new models/textures on drag&drop
if (IsFileDropped())
{
var droppedFiles = Raylib.GetDroppedFiles();
if (droppedFiles.Length == 1) // Only support one file dropped
{
if (IsFileExtension(droppedFiles[0], ".obj") ||
IsFileExtension(droppedFiles[0], ".gltf") ||
IsFileExtension(droppedFiles[0], ".glb") ||
IsFileExtension(droppedFiles[0], ".vox") ||
IsFileExtension(droppedFiles[0], ".iqm") ||
IsFileExtension(droppedFiles[0], ".m3d") // Model file formats supported
)
{
UnloadModel(model); // Unload previous model
model = LoadModel(droppedFiles[0]); // Load new model
// Set current map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
bounds = GetMeshBoundingBox(model.Meshes[0]);
// Move camera position from target enough distance to visualize model properly
camera.Position.X = bounds.Max.X + 10.0f;
camera.Position.Y = bounds.Max.Y + 10.0f;
camera.Position.Z = bounds.Max.Z + 10.0f;
}
else if (IsFileExtension(droppedFiles[0], ".png")) // Texture file formats supported
{
// Unload current model texture and load new one
UnloadTexture(texture);
texture = LoadTexture(droppedFiles[0]);
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
}
}
}
#endif
// Select model on mouse click
if (IsMouseButtonPressed(MouseButton.Left))
{
// Check collision between ray and box
if (GetRayCollisionBox(GetScreenToWorldRay(GetMousePosition(), camera), bounds).Hit)
{
selected = !selected;
}
else
{
selected = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White); // Draw 3d model with texture
DrawGrid(20, 10.0f); // Draw a grid
if (selected)
{
DrawBoundingBox(bounds, Color.Green); // Draw selection box
}
EndMode3D();
DrawText("Drag & drop model to load mesh/texture.", 10, GetScreenHeight() - 20, 10, Color.DarkGray);
if (selected)
{
DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, Color.Green);
}
DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture);
UnloadModel(model);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new ModelLoading();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsFileDropped())
{
string[] droppedFiles = Raylib.GetDroppedFiles();
if (droppedFiles.Length == 1)
{
if (IsFileExtension(droppedFiles[0], ".obj") ||
IsFileExtension(droppedFiles[0], ".gltf") ||
IsFileExtension(droppedFiles[0], ".glb") ||
IsFileExtension(droppedFiles[0], ".vox") ||
IsFileExtension(droppedFiles[0], ".iqm") ||
IsFileExtension(droppedFiles[0], ".m3d")
)
{
UnloadModel(model);
model = LoadModel(droppedFiles[0]);
// Set current map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
bounds = GetMeshBoundingBox(model.Meshes[0]);
// TODO: Move camera position from target enough distance to visualize model properly
}
else if (IsFileExtension(droppedFiles[0], ".png"))
{
// Unload model texture and load new one
UnloadTexture(texture);
texture = LoadTexture(droppedFiles[0]);
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
}
}
}
// Select model on mouse click
if (IsMouseButtonPressed(MouseButton.Left))
{
// Check collision between ray and box
if (GetRayCollisionBox(GetScreenToWorldRay(GetMousePosition(), camera), bounds).Hit)
{
selected = !selected;
}
else
{
selected = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White);
DrawGrid(20, 10.0f);
if (selected)
{
DrawBoundingBox(bounds, Color.Green);
}
EndMode3D();
DrawText("Drag & drop model to load mesh/texture.", 10, GetScreenHeight() - 20, 10, Color.DarkGray);
if (selected)
{
DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, Color.Green);
}
DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -1,111 +1,135 @@
/*******************************************************************************************
*
* raylib [models] example - Show the difference between perspective and orthographic projection
* raylib [models] example - orthographic projection
*
* This program is heavily based on the geometric objects example
* Example complexity rating: [] 1/4
*
* This example has been created using raylib 1.9.7 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 2.0, last time updated with raylib 3.7
*
* Copyright (c) 2018 Max Danielsson ref Ramon Santamaria (@raysan5)
* Example contributed by Max Danielsson (@autious) 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) 2018-2025 Max Danielsson (@autious) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class OrthographicProjection
public partial class OrthographicProjection : IExample
{
public const float FOVY_PERSPECTIVE = 45.0f;
public const float WIDTH_ORTHOGRAPHIC = 10.0f;
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Orthographic Projection";
public string Title => "raylib [models] example - orthographic projection";
private Camera3D camera;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = FOVY_PERSPECTIVE;
camera.Projection = CameraProjection.Perspective;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
if (camera.Projection == CameraProjection.Perspective)
{
camera.FovY = WIDTH_ORTHOGRAPHIC;
camera.Projection = CameraProjection.Orthographic;
}
else
{
camera.FovY = FOVY_PERSPECTIVE;
camera.Projection = CameraProjection.Perspective;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red);
DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold);
DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon);
DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green);
DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime);
DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue);
DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue);
DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown);
DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold);
DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink);
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D();
DrawText("Press Spacebar to switch camera type", 10, GetScreenHeight() - 30, 20, Color.DarkGray);
if (camera.Projection == CameraProjection.Orthographic)
{
DrawText("ORTHOGRAPHIC", 10, 40, 20, Color.Black);
}
else if (camera.Projection == CameraProjection.Perspective)
{
DrawText("PERSPECTIVE", 10, 40, 20, Color.Black);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - orthographic projection");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new OrthographicProjection();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
if (camera.Projection == CameraProjection.Perspective)
{
camera.FovY = WIDTH_ORTHOGRAPHIC;
camera.Projection = CameraProjection.Orthographic;
}
else
{
camera.FovY = FOVY_PERSPECTIVE;
camera.Projection = CameraProjection.Perspective;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red);
DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold);
DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon);
DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green);
DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime);
DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue);
DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue);
DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown);
DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold);
DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Press Spacebar to switch camera type", 10, GetScreenHeight() - 30, 20, Color.DarkGray);
if (camera.Projection == CameraProjection.Orthographic)
{
DrawText("ORTHOGRAPHIC", 10, 40, 20, Color.Black);
}
else if (camera.Projection == CameraProjection.Perspective)
{
DrawText("PERSPECTIVE", 10, 40, 20, Color.Black);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,231 @@
/*******************************************************************************************
*
* raylib [models] example - point rendering
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example contributed by Reese Gallagher (@satchelfrost) 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) 2024-2025 Reese Gallagher (@satchelfrost)
*
********************************************************************************************/
namespace Examples.Models;
[ExcludeFromBrowser("rlEnablePointMode needs glPolygonMode, which OpenGL ES/WebGL lacks (renders as triangles)")]
public partial class PointRendering : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxPoints = 10000000; // 10 million
private const int MinPoints = 1000; // 1 thousand
public string Name => "Models / Point Rendering";
public string Title => "raylib [models] example - point rendering";
private static readonly Random Rand = new();
private Camera3D camera;
private Vector3 position;
private bool useDrawModelPoints;
private bool numPointsChanged;
private int numPoints;
private Mesh mesh;
private Model model;
public void Init()
{
camera = new()
{
Position = new Vector3(3.0f, 3.0f, 3.0f),
Target = new Vector3(0.0f, 0.0f, 0.0f),
Up = new Vector3(0.0f, 1.0f, 0.0f),
FovY = 45.0f,
Projection = CameraProjection.Perspective
};
position = new Vector3(0.0f, 0.0f, 0.0f);
useDrawModelPoints = true;
numPointsChanged = false;
numPoints = 1000;
mesh = GenMeshPoints(numPoints);
model = LoadModelFromMesh(mesh);
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.Space))
{
useDrawModelPoints = !useDrawModelPoints;
}
if (IsKeyPressed(KeyboardKey.Up))
{
numPoints = (numPoints * 10 > MaxPoints) ? MaxPoints : numPoints * 10;
numPointsChanged = true;
}
if (IsKeyPressed(KeyboardKey.Down))
{
numPoints = (numPoints / 10 < MinPoints) ? MinPoints : numPoints / 10;
numPointsChanged = true;
}
// Upload a different point cloud size
if (numPointsChanged)
{
UnloadModel(model);
mesh = GenMeshPoints(numPoints);
model = LoadModelFromMesh(mesh);
numPointsChanged = false;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Black);
BeginMode3D(camera);
// The new method only uploads the points once to the GPU
if (useDrawModelPoints)
{
DrawModelPoints(model, position, 1.0f, Color.White);
}
else
{
// The old method must continually draw the "points" (lines)
for (var i = 0; i < numPoints; i++)
{
Vector3 pos = new(
mesh.Vertices[i * 3 + 0],
mesh.Vertices[i * 3 + 1],
mesh.Vertices[i * 3 + 2]
);
Color color = new(
mesh.Colors[i * 4 + 0],
mesh.Colors[i * 4 + 1],
mesh.Colors[i * 4 + 2],
mesh.Colors[i * 4 + 3]
);
DrawPoint3D(pos, color);
}
}
// Draw a unit sphere for reference
DrawSphereWires(position, 1.0f, 10, 10, Color.Yellow);
EndMode3D();
// Draw UI text
DrawText($"Point Count: {numPoints}", 10, screenHeight - 50, 40, Color.White);
DrawText("UP - Increase points", 10, 40, 20, Color.White);
DrawText("DOWN - Decrease points", 10, 70, 20, Color.White);
DrawText("SPACE - Drawing function", 10, 100, 20, Color.White);
if (useDrawModelPoints)
{
DrawText("Using: DrawModelPoints()", 10, 130, 20, Color.Green);
}
else
{
DrawText("Using: DrawPoint3D()", 10, 130, 20, Color.Red);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model);
}
// Generate a spherical point cloud
private static unsafe Mesh GenMeshPoints(int numPoints)
{
Mesh mesh = new(numPoints, 1);
mesh.AllocVertices();
mesh.AllocColors();
// REF: https://en.wikipedia.org/wiki/Spherical_coordinate_system
for (var i = 0; i < numPoints; i++)
{
var theta = MathF.PI * (float)Rand.NextDouble();
var phi = 2.0f * MathF.PI * (float)Rand.NextDouble();
var r = 10.0f * (float)Rand.NextDouble();
mesh.Vertices[i * 3 + 0] = r * MathF.Sin(theta) * MathF.Cos(phi);
mesh.Vertices[i * 3 + 1] = r * MathF.Sin(theta) * MathF.Sin(phi);
mesh.Vertices[i * 3 + 2] = r * MathF.Cos(theta);
var color = ColorFromHSV(r * 360.0f, 1.0f, 1.0f);
mesh.Colors[i * 4 + 0] = color.R;
mesh.Colors[i * 4 + 1] = color.G;
mesh.Colors[i * 4 + 2] = color.B;
mesh.Colors[i * 4 + 3] = color.A;
}
// Upload mesh data from CPU (RAM) to GPU (VRAM) memory
UploadMesh(ref mesh, false);
return mesh;
}
// Draw a model points
// WARNING: OpenGL ES 2.0 does not support point mode drawing
private static void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
{
Rlgl.EnablePointMode();
Rlgl.DisableBackfaceCulling();
DrawModel(model, position, scale, tint);
Rlgl.EnableBackfaceCulling();
Rlgl.DisablePointMode();
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - point rendering");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new PointRendering();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,119 @@
/*******************************************************************************************
*
* raylib [models] example - rotating cube
*
* Example complexity rating: [] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Jopestpe (@jopestpe)
*
* 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 Jopestpe (@jopestpe)
*
********************************************************************************************/
namespace Examples.Models;
public partial class RotatingCube : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Rotating Cube";
public string Title => "raylib [models] example - rotating cube";
private Camera3D camera;
private Model model;
private Texture2D texture;
private float rotation;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(0.0f, 3.0f, 3.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Load image to create texture for the cube
model = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
var img = LoadImage("resources/cubicmap_atlas.png");
var crop = ImageFromImage(img, new Rectangle(0, img.Height / 2.0f, img.Width / 2.0f, img.Height / 2.0f));
texture = LoadTextureFromImage(crop);
UnloadImage(img);
UnloadImage(crop);
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture;
rotation = 0.0f;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
rotation += 1.0f;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw model defining: position, size, rotation-axis, rotation (degrees), size, and tint-color
DrawModelEx(model, new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.5f, 1.0f, 0.0f),
rotation, new Vector3(1.0f, 1.0f, 1.0f), Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture); // Unload texture
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - rotating cube");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new RotatingCube();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,32 +1,50 @@
/*******************************************************************************************
*
* raylib [models] example - Skybox loading and drawing
* raylib [models] example - skybox rendering
*
* This example has been created using raylib 1.8 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 2/4
*
* Copyright (c) 2017 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.8, last time updated with raylib 4.0
*
* 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) 2017-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class SkyboxDemo
[ExcludeFromBrowser("cubemap generation is too memory-heavy on web (upstream note)")]
public partial class SkyboxDemo : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
// GLSL version used for shaders (330 desktop, 100 web/GLES)
#if BROWSER
public const int GlslVersion = 100;
#else
public const int GlslVersion = 330;
#endif
public string Name => "Models / Skybox Demo";
public string Title => "raylib [models] example - skybox rendering";
public bool CursorDisabled => true;
private Camera3D camera;
private Model skybox;
private bool useHdr;
private Shader shdrCubemap;
private string skyboxFileName;
private Texture2D panorama;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox loading and drawing");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(1.0f, 1.0f, 1.0f);
camera.Target = new Vector3(4.0f, 1.0f, 4.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
@ -34,14 +52,19 @@ public class SkyboxDemo
camera.Projection = CameraProjection.Perspective;
// Load skybox model
Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
Model skybox = LoadModelFromMesh(cube);
var cube = GenMeshCube(1.0f, 1.0f, 1.0f);
skybox = LoadModelFromMesh(cube);
bool useHdr = false;
// Set this to true to use an HDR Texture
// NOTE: raylib must be built with HDR Support for this to work: SUPPORT_FILEFORMAT_HDR
useHdr = false;
// Load skybox shader and set required locations
// NOTE: Some locations are automatically set at shader loading
Shader shdrSkybox = LoadShader("resources/shaders/glsl330/skybox.vs", "resources/shaders/glsl330/skybox.fs");
var shdrSkybox = LoadShader(
$"resources/shaders/glsl{GlslVersion}/skybox.vs",
$"resources/shaders/glsl{GlslVersion}/skybox.fs"
);
Raylib.SetShaderValue(
shdrSkybox,
@ -67,9 +90,9 @@ public class SkyboxDemo
Raylib.SetMaterialShader(ref skybox, 0, ref shdrSkybox);
// Load cubemap shader and setup required shader locations
Shader shdrCubemap = LoadShader(
"resources/shaders/glsl330/cubemap.vs",
"resources/shaders/glsl330/cubemap.fs"
shdrCubemap = LoadShader(
$"resources/shaders/glsl{GlslVersion}/cubemap.vs",
$"resources/shaders/glsl{GlslVersion}/cubemap.fs"
);
Raylib.SetShaderValue(
shdrCubemap,
@ -79,14 +102,12 @@ public class SkyboxDemo
);
// Load skybox
string skyboxFileName = "resources/dresden_square_2k.hdr";
Texture2D panorama;
skyboxFileName = "resources/dresden_square_2k.hdr";
if (useHdr)
{
panorama = LoadTexture(skyboxFileName);
Texture2D cubemap = GenTextureCubemap(
var cubemap = GenTextureCubemap(
shdrCubemap,
panorama,
1024,
@ -97,108 +118,130 @@ public class SkyboxDemo
}
else
{
Image img = LoadImage("resources/skybox.png");
Texture2D cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect);
// 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()
var img = LoadImage("resources/skybox.png");
var cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadImage(img);
}
}
DisableCursor();
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.FirstPerson);
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose())
// Load new cubemap texture on drag & drop
if (IsFileDropped())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.FirstPerson);
var files = Raylib.GetDroppedFiles();
// Load new cubemap texture on drag & drop
if (IsFileDropped())
if (files.Length == 1)
{
string[] files = Raylib.GetDroppedFiles();
if (files.Length == 1)
if (IsFileExtension(files[0], ".png;.jpg;.hdr;.bmp;.tga"))
{
if (IsFileExtension(files[0], ".png;.jpg;.hdr;.bmp;.tga"))
// Unload cubemap texture and load new one
UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap));
if (useHdr)
{
// Unload cubemap texture and load new one
UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap));
if (useHdr)
{
panorama = LoadTexture(files[0]);
Texture2D cubemap = GenTextureCubemap(
shdrCubemap,
panorama,
1024,
PixelFormat.UncompressedR8G8B8A8
);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadTexture(panorama);
}
else
{
Image img = LoadImage(files[0]);
Texture2D cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadImage(img);
}
skyboxFileName = files[0];
panorama = LoadTexture(files[0]);
var cubemap = GenTextureCubemap(
shdrCubemap,
panorama,
1024,
PixelFormat.UncompressedR8G8B8A8
);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadTexture(panorama);
}
else
{
var img = LoadImage(files[0]);
var cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadImage(img);
}
skyboxFileName = files[0];
}
}
//----------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginMode3D(camera);
// We are inside the cube, we need to disable backface culling!
Rlgl.DisableBackfaceCulling();
Rlgl.DisableDepthMask();
DrawModel(skybox, Vector3.Zero, 1.0f, Color.White);
Rlgl.EnableBackfaceCulling();
Rlgl.EnableDepthMask();
// We are inside the cube, we need to disable backface culling!
Rlgl.DisableBackfaceCulling();
Rlgl.DisableDepthMask();
DrawModel(skybox, Vector3.Zero, 1.0f, Color.White);
Rlgl.EnableBackfaceCulling();
Rlgl.EnableDepthMask();
DrawGrid(10, 1.0f);
DrawGrid(10, 1.0f);
EndMode3D();
EndMode3D();
if (useHdr)
{
DrawText(
$"Panorama image from hdrihaven.com: {skyboxFileName}",
10,
GetScreenHeight() - 20,
10,
Color.Black
);
}
else
{
DrawText($": {skyboxFileName}", 10, GetScreenHeight() - 20, 10, Color.Black);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
if (useHdr)
{
DrawText(
$"Panorama image from hdrihaven.com: {skyboxFileName}",
10,
GetScreenHeight() - 20,
10,
Color.Black
);
}
else
{
DrawText($": {skyboxFileName}", 10, GetScreenHeight() - 20, 10, Color.Black);
}
// De-Initialization
//--------------------------------------------------------------------------------------
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(Raylib.GetMaterial(ref skybox, 0).Shader);
UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap));
UnloadModel(skybox);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox rendering");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new SkyboxDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
@ -215,10 +258,10 @@ public class SkyboxDemo
// STEP 1: Setup framebuffer
//------------------------------------------------------------------------------------------
uint rbo = Rlgl.LoadTextureDepth(size, size, true);
var rbo = Rlgl.LoadTextureDepth(size, size, true);
cubemap.Id = Rlgl.LoadTextureCubemap(null, size, format, 1);
uint fbo = Rlgl.LoadFramebuffer();
var fbo = Rlgl.LoadFramebuffer();
Rlgl.FramebufferAttach(
fbo,
rbo,
@ -235,7 +278,7 @@ public class SkyboxDemo
);
// Check if framebuffer is complete with attachments (valid)
if (Rlgl.FramebufferComplete(fbo))
if (Rlgl.FramebufferComplete(fbo) != 0)
{
Console.WriteLine($"FBO: [ID {fbo}] Framebuffer object created successfully");
}
@ -247,7 +290,7 @@ public class SkyboxDemo
Rlgl.EnableShader(shader.Id);
// Define projection matrix and send it to shader
Matrix4x4 matFboProjection = Raymath.MatrixPerspective(
var matFboProjection = Raymath.MatrixPerspective(
90.0f * DEG2RAD,
1.0f,
Rlgl.CULL_DISTANCE_NEAR,
@ -256,14 +299,14 @@ public class SkyboxDemo
Rlgl.SetUniformMatrix(shader.Locs[(int)ShaderLocationIndex.MatrixProjection], matFboProjection);
// Define view matrix for every side of the cubemap
Matrix4x4[] fboViews = new[]
var fboViews = new[]
{
Raymath.MatrixLookAt(Vector3.Zero, new Vector3(-1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3(-1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 1.0f, 0.0f), new Vector3( 0.0f, 0.0f, 1.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, -1.0f, 0.0f), new Vector3( 0.0f, 0.0f, -1.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, -1.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, 1.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, -1.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
};
// Set viewport to current fbo dimensions
@ -273,7 +316,7 @@ public class SkyboxDemo
Rlgl.ActiveTextureSlot(0);
Rlgl.EnableTexture(panorama.Id);
for (int i = 0; i < 6; i++)
for (var i = 0; i < 6; i++)
{
// Set the view matrix for the current cube face
Rlgl.SetUniformMatrix(shader.Locs[(int)ShaderLocationIndex.MatrixView], fboViews[i]);

View file

@ -1,161 +1,153 @@
/*******************************************************************************************
*
* raylib [models] example - rlgl module usage with push/pop matrix transformations
* raylib [models] example - rlgl solar system
*
* This example uses [rlgl] module funtionality (pseudo-OpenGL 1.1 style coding)
* Example complexity rating: [] 4/4
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* NOTE: This example uses [rlgl] module functionality (pseudo-OpenGL 1.1 style coding)
*
* Copyright (c) 2018 Ramon Santamaria (@raysan5)
* Example originally created with raylib 2.5, last time updated with raylib 4.0
*
* 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) 2018-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class SolarSystem
public partial class SolarSystem : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
private const float sunRadius = 4.0f;
private const float earthRadius = 0.6f;
private const float earthOrbitRadius = 8.0f;
private const float moonRadius = 0.16f;
private const float moonOrbitRadius = 1.5f;
public string Name => "Models / Solar System";
public string Title => "raylib [models] example - rlgl solar system";
private Camera3D camera;
private float rotationSpeed;
private float earthRotation;
private float earthOrbitRotation;
private float moonRotation;
private float moonOrbitRotation;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
const float sunRadius = 4.0f;
const float earthRadius = 0.6f;
const float earthOrbitRadius = 8.0f;
const float moonRadius = 0.16f;
const float moonOrbitRadius = 1.5f;
InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl module usage with push/pop matrix transformations");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(16.0f, 16.0f, 16.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(16.0f, 16.0f, 16.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// General system rotation speed
float rotationSpeed = 0.2f;
// Rotation of earth around itself (days) in degrees
float earthRotation = 0.0f;
// Rotation of earth around the Sun (years) in degrees
float earthOrbitRotation = 0.0f;
// Rotation of moon around itself
float moonRotation = 0.0f;
// Rotation of moon around earth in degrees
float moonOrbitRotation = 0.0f;
rotationSpeed = 0.2f; // General system rotation speed
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
earthRotation = 0.0f; // Rotation of earth around itself (days) in degrees
earthOrbitRotation = 0.0f; // Rotation of earth around the Sun (years) in degrees
moonRotation = 0.0f; // Rotation of moon around itself
moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees
}
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
earthRotation += (5.0f * rotationSpeed);
earthOrbitRotation += (365 / 360.0f * (5.0f * rotationSpeed) * rotationSpeed);
moonRotation += (2.0f * rotationSpeed);
moonOrbitRotation += (8.0f * rotationSpeed);
//----------------------------------------------------------------------------------
earthRotation += (5.0f * rotationSpeed);
earthOrbitRotation += (365 / 360.0f * (5.0f * rotationSpeed) * rotationSpeed);
moonRotation += (2.0f * rotationSpeed);
moonOrbitRotation += (8.0f * rotationSpeed);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginMode3D(camera);
Rlgl.PushMatrix();
// Scale Sun
Rlgl.Scalef(sunRadius, sunRadius, sunRadius);
// Draw the Sun
DrawSphereBasic(Color.Gold);
Rlgl.PopMatrix();
Rlgl.PushMatrix();
// Scale Sun
Rlgl.Scalef(sunRadius, sunRadius, sunRadius);
// Draw the Sun
DrawSphereBasic(Color.Gold);
Rlgl.PopMatrix();
Rlgl.PushMatrix();
// Rotation for Earth orbit around Sun
Rlgl.Rotatef(earthOrbitRotation, 0.0f, 1.0f, 0.0f);
// Translation for Earth orbit
Rlgl.Translatef(earthOrbitRadius, 0.0f, 0.0f);
Rlgl.PushMatrix();
// Rotation for Earth orbit around Sun
Rlgl.Rotatef(earthOrbitRotation, 0.0f, 1.0f, 0.0f);
// Translation for Earth orbit
Rlgl.Translatef(earthOrbitRadius, 0.0f, 0.0f);
// Rotation for Earth orbit around Sun inverted
Rlgl.Rotatef(-earthOrbitRotation, 0.0f, 1.0f, 0.0f);
Rlgl.PushMatrix();
// Rotation for Earth itself
Rlgl.Rotatef(earthRotation, 0.25f, 1.0f, 0.0f);
// Scale Earth
Rlgl.Scalef(earthRadius, earthRadius, earthRadius);
Rlgl.PushMatrix();
// Rotation for Earth itself
Rlgl.Rotatef(earthRotation, 0.25f, 1.0f, 0.0f);
// Scale Earth
Rlgl.Scalef(earthRadius, earthRadius, earthRadius);
// Draw the Earth
DrawSphereBasic(Color.Blue);
Rlgl.PopMatrix();
// Draw the Earth
DrawSphereBasic(Color.Blue);
Rlgl.PopMatrix();
// Rotation for Moon orbit around Earth
Rlgl.Rotatef(moonOrbitRotation, 0.0f, 1.0f, 0.0f);
// Translation for Moon orbit
Rlgl.Translatef(moonOrbitRadius, 0.0f, 0.0f);
// Rotation for Moon itself
Rlgl.Rotatef(moonRotation, 0.0f, 1.0f, 0.0f);
// Scale Moon
Rlgl.Scalef(moonRadius, moonRadius, moonRadius);
// Rotation for Moon orbit around Earth
Rlgl.Rotatef(moonOrbitRotation, 0.0f, 1.0f, 0.0f);
// Translation for Moon orbit
Rlgl.Translatef(moonOrbitRadius, 0.0f, 0.0f);
// Rotation for Moon orbit around Earth inverted
Rlgl.Rotatef(-moonOrbitRotation, 0.0f, 1.0f, 0.0f);
// Rotation for Moon itself
Rlgl.Rotatef(moonRotation, 0.0f, 1.0f, 0.0f);
// Scale Moon
Rlgl.Scalef(moonRadius, moonRadius, moonRadius);
// Draw the Moon
DrawSphereBasic(Color.LightGray);
Rlgl.PopMatrix();
// Draw the Moon
DrawSphereBasic(Color.LightGray);
Rlgl.PopMatrix();
// Some reference elements (not affected by previous matrix transformations)
DrawCircle3D(
new Vector3(0.0f, 0.0f, 0.0f),
earthOrbitRadius,
new Vector3(1, 0, 0),
90.0f,
Fade(Color.Red, 0.5f)
);
DrawGrid(20, 1.0f);
// Some reference elements (not affected by previous matrix transformations)
DrawCircle3D(
new Vector3(0.0f, 0.0f, 0.0f),
earthOrbitRadius,
new Vector3(1, 0, 0),
90.0f,
ColorAlpha(Color.Red, 0.5f)
);
DrawGrid(20, 1.0f);
EndMode3D();
EndMode3D();
DrawText("EARTH ORBITING AROUND THE SUN!", 400, 10, 20, Color.Maroon);
DrawFPS(10, 10);
DrawText("EARTH ORBITING AROUND THE SUN!", 400, 10, 20, Color.Maroon);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
public void Unload()
{
}
// Draw sphere without any matrix transformation
// NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f
static void DrawSphereBasic(Color color)
private static void DrawSphereBasic(Color color)
{
int rings = 16;
int slices = 16;
var rings = 16;
var slices = 16;
Rlgl.Begin(DrawMode.Triangles);
Rlgl.Color4ub(color.R, color.G, color.B, color.A);
for (int i = 0; i < (rings + 2); i++)
for (var i = 0; i < (rings + 2); i++)
{
for (int j = 0; j < slices; j++)
for (var j = 0; j < slices; j++)
{
Rlgl.Vertex3f(
MathF.Cos(DEG2RAD * (270 + (180 / (rings + 1)) * i)) * MathF.Sin(DEG2RAD * (j * 360 / slices)),
@ -192,5 +184,32 @@ public class SolarSystem
}
Rlgl.End();
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl solar system");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new SolarSystem();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,163 @@
/*******************************************************************************************
*
* raylib [models] example - tesseract view
*
* NOTE: This example only works on platforms that support drag & drop (Windows, Linux, OSX, Html5?)
*
* Example complexity rating: [] 2/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Timothy van der Valk (@arceryz) 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) 2024-2025 Timothy van der Valk (@arceryz) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class TesseractView : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Tesseract View";
public string Title => "raylib [models] example - tesseract view";
// Define the camera to look into our 3d world
private Camera3D camera;
// Find the coordinates by setting XYZW to +-1
private Vector4[] tesseract;
private float rotation;
private Vector3[] transformed;
private float[] wValues;
public void Init()
{
// Define the camera to look into our 3d world
camera = new Camera3D();
camera.Position = new Vector3(4.0f, 4.0f, 4.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 0.0f, 1.0f); // Camera up vector (rotation towards target)
camera.FovY = 50.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera mode type
// Find the coordinates by setting XYZW to +-1
tesseract = new Vector4[16]
{
new( 1, 1, 1, 1 ), new( 1, 1, 1, -1 ),
new( 1, 1, -1, 1 ), new( 1, 1, -1, -1 ),
new( 1, -1, 1, 1 ), new( 1, -1, 1, -1 ),
new( 1, -1, -1, 1 ), new( 1, -1, -1, -1 ),
new(-1, 1, 1, 1 ), new(-1, 1, 1, -1 ),
new(-1, 1, -1, 1 ), new(-1, 1, -1, -1 ),
new(-1, -1, 1, 1 ), new(-1, -1, 1, -1 ),
new(-1, -1, -1, 1 ), new(-1, -1, -1, -1 ),
};
rotation = 0.0f;
transformed = new Vector3[16];
wValues = new float[16];
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
rotation = DEG2RAD * 45.0f * (float)GetTime();
for (int i = 0; i < 16; i++)
{
Vector4 p = tesseract[i];
// Rotate the XW part of the vector
Vector2 rotXW = Vector2Rotate(new Vector2(p.X, p.W), rotation);
p.X = rotXW.X;
p.W = rotXW.Y;
// Projection from XYZW to XYZ from perspective point (0, 0, 0, 3)
// NOTE: Trace a ray from (0, 0, 0, 3) > p and continue until W = 0
float c = 3.0f / (3.0f - p.W);
p.X = c * p.X;
p.Y = c * p.Y;
p.Z = c * p.Z;
// Split XYZ coordinate and W values later for drawing
transformed[i] = new Vector3(p.X, p.Y, p.Z);
wValues[i] = p.W;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
for (int i = 0; i < 16; i++)
{
// Draw spheres to indicate the W value
DrawSphere(transformed[i], MathF.Abs(wValues[i] * 0.1f), Color.Red);
for (int j = 0; j < 16; j++)
{
// Two lines are connected if they differ by 1 coordinate
// This way we dont have to keep an edge list
Vector4 v1 = tesseract[i];
Vector4 v2 = tesseract[j];
int diff = (v1.X == v2.X ? 1 : 0) + (v1.Y == v2.Y ? 1 : 0) + (v1.Z == v2.Z ? 1 : 0) + (v1.W == v2.W ? 1 : 0);
// Draw only differing by 1 coordinate and the lower index only (duplicate lines)
if (diff == 3 && i < j)
{
DrawLine3D(transformed[i], transformed[j], Color.Maroon);
}
}
}
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - tesseract view");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new TesseractView();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,117 +1,141 @@
/*******************************************************************************************
*
* raylib [models] example - Waving cubes
* raylib [models] example - waving cubes
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 2.5, last time updated with raylib 3.7
*
* Example contributed by Codecat (@codecat) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 Codecat (@codecat) and 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) 2019-2025 Codecat (@codecat) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class WavingCubes
public partial class WavingCubes : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
// Specify the amount of blocks in each direction
private const int numBlocks = 15;
public string Name => "Models / Waving Cubes";
public string Title => "raylib [models] example - waving cubes";
private Camera3D camera;
public void Init()
{
// Initialize the camera
camera = new();
camera.Position = new Vector3(30.0f, 20.0f, 30.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 70.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
var time = GetTime();
// Calculate time scale for cube position and size
var scale = (2.0f + (float)Math.Sin(time)) * 0.7f;
// Move camera around the scene
var cameraTime = time * 0.3;
camera.Position.X = (float)Math.Cos(cameraTime) * 40.0f;
camera.Position.Z = (float)Math.Sin(cameraTime) * 40.0f;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 5.0f);
for (var x = 0; x < numBlocks; x++)
{
for (var y = 0; y < numBlocks; y++)
{
for (var z = 0; z < numBlocks; z++)
{
// Scale of the blocks depends on x/y/z positions
var blockScale = (x + y + z) / 30.0f;
// Scatter makes the waving effect by adding blockScale over time
var scatter = (float)Math.Sin(blockScale * 20.0f + (float)(time * 4.0f));
// Calculate the cube position
Vector3 cubePos = new(
(float)(x - numBlocks / 2) * (scale * 3.0f) + scatter,
(float)(y - numBlocks / 2) * (scale * 2.0f) + scatter,
(float)(z - numBlocks / 2) * (scale * 3.0f) + scatter
);
// Pick a color with a hue depending on cube position for the rainbow color effect
// NOTE: This function is quite costly to be done per cube and frame,
// pre-catching the results into a separate array could improve performance
var cubeColor = ColorFromHSV((float)(((x + y + z) * 18) % 360), 0.75f, 0.9f);
// Calculate cube size
var cubeSize = (2.4f - scale) * blockScale;
// And finally, draw the cube!
DrawCube(cubePos, cubeSize, cubeSize, cubeSize, cubeColor);
}
}
}
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - waving cubes");
// Initialize the camera
Camera3D camera = new();
camera.Position = new Vector3(30.0f, 20.0f, 30.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 70.0f;
camera.Projection = CameraProjection.Perspective;
// Specify the amount of blocks in each direction
const int numBlocks = 15;
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new WavingCubes();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
double time = GetTime();
// Calculate time scale for cube position and size
float scale = (2.0f + (float)Math.Sin(time)) * 0.7f;
// Move camera around the scene
double cameraTime = time * 0.3;
camera.Position.X = (float)Math.Cos(cameraTime) * 40.0f;
camera.Position.Z = (float)Math.Sin(cameraTime) * 40.0f;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 5.0f);
for (int x = 0; x < numBlocks; x++)
{
for (int y = 0; y < numBlocks; y++)
{
for (int z = 0; z < numBlocks; z++)
{
// Scale of the blocks depends on x/y/z positions
float blockScale = (x + y + z) / 30.0f;
// Scatter makes the waving effect by adding blockScale over time
float scatter = (float)Math.Sin(blockScale * 20.0f + (float)(time * 4.0f));
// Calculate the cube position
Vector3 cubePos = new(
(float)(x - numBlocks / 2) * (scale * 3.0f) + scatter,
(float)(y - numBlocks / 2) * (scale * 2.0f) + scatter,
(float)(z - numBlocks / 2) * (scale * 3.0f) + scatter
);
// Pick a color with a hue depending on cube position for the rainbow color effect
Color cubeColor = ColorFromHSV((float)(((x + y + z) * 18) % 360), 0.75f, 0.9f);
// Calculate cube size
float cubeSize = (2.4f - scale) * blockScale;
// And finally, draw the cube!
DrawCube(cubePos, cubeSize, cubeSize, cubeSize, cubeColor);
}
}
}
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,162 +1,196 @@
/*******************************************************************************************
*
* raylib [models] example - Plane rotations (yaw, pitch, roll)
* raylib [models] example - yaw pitch roll
*
* This example has been created using raylib 1.8 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 2/4
*
* Example originally created with raylib 1.8, last time updated with raylib 4.0
*
* Example contributed by Berni (@Berni8k) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2017-2021 Berni (@Berni8k) and 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) 2017-2025 Berni (@Berni8k) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath;
namespace Examples.Models;
public class YawPitchRoll
public partial class YawPitchRoll : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Yaw Pitch Roll";
public string Title => "raylib [models] example - yaw pitch roll";
private Camera3D camera;
private Model model;
private Texture2D texture;
private float pitch;
private float roll;
private float yaw;
public unsafe void Init()
{
camera = new();
camera.Position = new Vector3(0.0f, 50.0f, -120.0f);// Camera position perspective
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 30.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera type
model = LoadModel("resources/models/obj/plane.obj"); // Load model
texture = LoadTexture("resources/models/obj/plane_diffuse.png"); // Load model texture
SetTextureWrap(texture, TextureWrap.Repeat); // Force Repeat to avoid issue on Web version
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture; // Set map diffuse texture
pitch = 0.0f;
roll = 0.0f;
yaw = 0.0f;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Plane pitch (x-axis) controls
if (IsKeyDown(KeyboardKey.Down))
{
pitch += 0.6f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
pitch -= 0.6f;
}
else
{
if (pitch > 0.3f)
{
pitch -= 0.3f;
}
else if (pitch < -0.3f)
{
pitch += 0.3f;
}
}
// Plane yaw (y-axis) controls
if (IsKeyDown(KeyboardKey.S))
{
yaw += 1.0f;
}
else if (IsKeyDown(KeyboardKey.A))
{
yaw -= 1.0f;
}
else
{
if (yaw > 0.0f)
{
yaw -= 0.5f;
}
else if (yaw < 0.0f)
{
yaw += 0.5f;
}
}
// Plane roll (z-axis) controls
if (IsKeyDown(KeyboardKey.Left))
{
roll += 1.0f;
}
else if (IsKeyDown(KeyboardKey.Right))
{
roll -= 1.0f;
}
else
{
if (roll > 0.0f)
{
roll -= 0.5f;
}
else if (roll < 0.0f)
{
roll += 0.5f;
}
}
// Tranformation matrix for rotations
model.Transform = MatrixRotateXYZ(new Vector3(DEG2RAD * pitch, DEG2RAD * yaw, DEG2RAD * roll));
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw 3D model (recomended to draw 3D always before 2D)
BeginMode3D(camera);
// Draw 3d model with texture
DrawModel(model, new Vector3(0.0f, -8.0f, 0.0f), 1.0f, Color.White);
DrawGrid(10, 10.0f);
EndMode3D();
// Draw controls info
DrawRectangle(30, 370, 260, 70, Fade(Color.Green, 0.5f));
DrawRectangleLines(30, 370, 260, 70, Fade(Color.DarkGreen, 0.5f));
DrawText("Pitch controlled with: KEY_UP / KEY_DOWN", 40, 380, 10, Color.DarkGray);
DrawText("Roll controlled with: KEY_LEFT / KEY_RIGHT", 40, 400, 10, Color.DarkGray);
DrawText("Yaw controlled with: KEY_A / KEY_S", 40, 420, 10, Color.DarkGray);
DrawText(
"(c) WWI Plane Model created by GiaHanLam",
screenWidth - 240,
screenHeight - 20,
10,
Color.DarkGray
);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - plane rotations (yaw, pitch, roll)");
Camera3D camera = new();
camera.Position = new Vector3(0.0f, 50.0f, -120.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 30.0f;
camera.Projection = CameraProjection.Perspective;
// Model loading
Model model = LoadModel("resources/models/obj/plane.obj");
Texture2D texture = LoadTexture("resources/models/obj/plane_diffuse.png");
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture;
float pitch = 0.0f;
float roll = 0.0f;
float yaw = 0.0f;
InitWindow(screenWidth, screenHeight, "raylib [models] example - yaw pitch roll");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
while (!WindowShouldClose())
var game = new YawPitchRoll();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Plane roll (x-axis) controls
if (IsKeyDown(KeyboardKey.Down))
{
pitch += 0.6f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
pitch -= 0.6f;
}
else
{
if (pitch > 0.3f)
{
pitch -= 0.3f;
}
else if (pitch < -0.3f)
{
pitch += 0.3f;
}
}
// Plane yaw (y-axis) controls
if (IsKeyDown(KeyboardKey.S))
{
yaw += 1.0f;
}
else if (IsKeyDown(KeyboardKey.A))
{
yaw -= 1.0f;
}
else
{
if (yaw > 0.0f)
{
yaw -= 0.5f;
}
else if (yaw < 0.0f)
{
yaw += 0.5f;
}
}
// Plane pitch (z-axis) controls
if (IsKeyDown(KeyboardKey.Left))
{
roll += 1.0f;
}
else if (IsKeyDown(KeyboardKey.Right))
{
roll -= 1.0f;
}
else
{
if (roll > 0.0f)
{
roll -= 0.5f;
}
else if (roll < 0.0f)
{
roll += 0.5f;
}
}
// Tranformation matrix for rotations
model.Transform = MatrixRotateXYZ(new Vector3(DEG2RAD * pitch, DEG2RAD * yaw, DEG2RAD * roll));
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw 3D model (recomended to draw 3D always before 2D)
BeginMode3D(camera);
// Draw 3d model with texture
DrawModel(model, new Vector3(0.0f, -8.0f, 0.0f), 1.0f, Color.White);
DrawGrid(10, 10.0f);
EndMode3D();
// Draw controls info
DrawRectangle(30, 370, 260, 70, Fade(Color.Green, 0.5f));
DrawRectangleLines(30, 370, 260, 70, Fade(Color.DarkGreen, 0.5f));
DrawText("Pitch controlled with: KEY_UP / KEY_DOWN", 40, 380, 10, Color.DarkGray);
DrawText("Roll controlled with: KEY_LEFT / KEY_RIGHT", 40, 400, 10, Color.DarkGray);
DrawText("Yaw controlled with: KEY_A / KEY_S", 40, 420, 10, Color.DarkGray);
DrawText(
"(c) WWI Plane Model created by GiaHanLam",
screenWidth - 240,
screenHeight - 20,
10,
Color.DarkGray
);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;