diff --git a/examples/Makefile b/examples/Makefile
index 93b05a0c7caa..904917f7c806 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -582,6 +582,7 @@ MODELS = \
models/models_mesh_generation \
models/models_mesh_picking \
models/models_orthographic_projection \
+ models/models_point_rendering \
models/models_rlgl_solar_system \
models/models_skybox \
models/models_waving_cubes \
diff --git a/examples/Makefile.Web b/examples/Makefile.Web
index 46a8a98ea077..7057da1c03d9 100644
--- a/examples/Makefile.Web
+++ b/examples/Makefile.Web
@@ -447,6 +447,7 @@ MODELS = \
models/models_mesh_generation \
models/models_mesh_picking \
models/models_orthographic_projection \
+ models/models_point_rendering \
models/models_rlgl_solar_system \
models/models_skybox \
models/models_waving_cubes \
diff --git a/examples/README.md b/examples/README.md
index 317f43c899a3..d573ff23ef54 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -147,11 +147,12 @@ Examples using raylib models functionality, including models loading/generation
| 91 | [models_loading_vox](models/models_loading_vox.c) | | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) |
| 92 | [models_loading_m3d](models/models_loading_m3d.c) | | ⭐️☆☆☆ | **4.2** | **4.2** | [bzt](https://bztsrc.gitlab.io/model3d) |
| 93 | [models_orthographic_projection](models/models_orthographic_projection.c) | | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) |
-| 94 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) |
-| 95 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) |
-| 96 | [models_waving_cubes](models/models_waving_cubes.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) |
-| 97 | [models_heightmap](models/models_heightmap.c) | | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
-| 98 | [models_skybox](models/models_skybox.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) |
+| 94 | [models_point_rendering](models/models_point_rendering.c) | | ⭐️⭐️☆☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) |
+| 95 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) |
+| 96 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) |
+| 97 | [models_waving_cubes](models/models_waving_cubes.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) |
+| 98 | [models_heightmap](models/models_heightmap.c) | | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
+| 99 | [models_skybox](models/models_skybox.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) |
### category: shaders
@@ -159,26 +160,25 @@ Examples using raylib shaders functionality, including shaders loading, paramete
| ## | example | image | difficulty
level | version
created | last version
updated | original
developer |
|----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------|
-| 99 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) |
-| 100 | [shaders_model_shader](shaders/shaders_model_shader.c) | | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) |
-| 101 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
-| 102 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
-| 103 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
-| 104 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) |
-| 105 | [shaders_raymarching](shaders/shaders_raymarching.c) | | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) |
-| 106 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) |
-| 107 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) |
-| 108 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) |
-| 109 | [shaders_julia_set](shaders/shaders_julia_set.c) | | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) |
-| 110 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) |
-| 111 | [shaders_fog](shaders/shaders_fog.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
-| 112 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
-| 113 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) |
-| 114 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) |
-| 115 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) |
-| 116 | [shaders_spotlight](shaders/shaders_spotlight.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
-| 117 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) |
-| 118 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | | ⭐️☆☆☆ | 1.5 | 1.5 | [Alex ZH](https://github.com/ZzzhHe) |
+| 100 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) |
+| 101 | [shaders_model_shader](shaders/shaders_model_shader.c) | | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) |
+| 102 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
+| 103 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
+| 104 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
+| 105 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) |
+| 106 | [shaders_raymarching](shaders/shaders_raymarching.c) | | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) |
+| 107 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) |
+| 108 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) |
+| 109 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) |
+| 110 | [shaders_julia_set](shaders/shaders_julia_set.c) | | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) |
+| 111 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) |
+| 112 | [shaders_fog](shaders/shaders_fog.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
+| 113 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
+| 114 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) |
+| 115 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) |
+| 116 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) |
+| 117 | [shaders_spotlight](shaders/shaders_spotlight.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
+| 118 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) |
### category: audio
diff --git a/examples/models/models_point_rendering.c b/examples/models/models_point_rendering.c
new file mode 100644
index 000000000000..128a70f57ee1
--- /dev/null
+++ b/examples/models/models_point_rendering.c
@@ -0,0 +1,171 @@
+/*******************************************************************************************
+*
+* raylib example - point rendering
+*
+* 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 Reese Gallagher (@satchelfrost)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include // Required for: rand()
+#include // Required for: cos(), sin()
+
+#define MAX_POINTS 10000000 // 10 million
+#define MIN_POINTS 1000 // 1 thousand
+
+static float RandFloat();
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main()
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+ InitWindow(screenWidth, screenHeight, "raylib [models] example - point rendering");
+ SetTargetFPS(60);
+
+ Camera camera = {
+ .position = {3.0f, 3.0f, 3.0f},
+ .target = {0.0f, 0.0f, 0.0f},
+ .up = {0.0f, 1.0f, 0.0f},
+ .fovy = 45.0f,
+ .projection = CAMERA_PERSPECTIVE,
+ };
+
+ Vector3 position = {0.0f, 0.0f, 0.0f};
+ bool useDrawModelPoints = true;
+ bool numPointsChanged = false;
+ int numPoints = 1000;
+ Mesh mesh = GenPoints(numPoints);
+ Model model = LoadModelFromMesh(mesh);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while(!WindowShouldClose())
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ UpdateCamera(&camera, CAMERA_ORBITAL);
+
+ if (IsKeyPressed(KEY_SPACE)) useDrawModelPoints = !useDrawModelPoints;
+ if (IsKeyPressed(KEY_UP))
+ {
+ numPoints = (numPoints * 10 > MAX_POINTS) ? MAX_POINTS : numPoints * 10;
+ numPointsChanged = true;
+ TraceLog(LOG_INFO, "num points %d", numPoints);
+ }
+ if (IsKeyPressed(KEY_DOWN))
+ {
+ numPoints = (numPoints / 10 < MIN_POINTS) ? MIN_POINTS : numPoints / 10;
+ numPointsChanged = true;
+ TraceLog(LOG_INFO, "num points %d", numPoints);
+ }
+
+ // upload a different point cloud size
+ if (numPointsChanged)
+ {
+ UnloadModel(model);
+ mesh = GenPoints(numPoints);
+ model = LoadModelFromMesh(mesh);
+ numPointsChanged = false;
+ }
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+ ClearBackground(BLACK);
+ BeginMode3D(camera);
+
+ // The new method only uploads the points once to the GPU
+ if (useDrawModelPoints)
+ {
+ DrawModelPoints(model, position, 1.0f, WHITE);
+ }
+ // The old method must continually draw the "points" (lines)
+ else
+ {
+ for (int i = 0; i < numPoints; i++)
+ {
+ Vector3 pos = {
+ .x = mesh.vertices[i * 3 + 0],
+ .y = mesh.vertices[i * 3 + 1],
+ .z = mesh.vertices[i * 3 + 2],
+ };
+ Color color = {
+ .r = mesh.colors[i * 4 + 0],
+ .g = mesh.colors[i * 4 + 1],
+ .b = mesh.colors[i * 4 + 2],
+ .a = mesh.colors[i * 4 + 3],
+ };
+ DrawPoint3D(pos, color);
+ }
+ }
+
+ // Draw a unit sphere for reference
+ DrawSphereWires(position, 1.0f, 10, 10, YELLOW);
+ EndMode3D();
+
+ // Text formatting
+ Color color = WHITE;
+ int fps = GetFPS();
+ if ((fps < 30) && (fps >= 15)) color = ORANGE;
+ else if (fps < 15) color = RED;
+ DrawText(TextFormat("%2i FPS", fps), 20, 20, 40, color);
+ DrawText(TextFormat("Point Count: %d", numPoints), 20, screenHeight - 50, 40, WHITE);
+ DrawText("Up - increase points", 20, 70, 20, WHITE);
+ DrawText("Down - decrease points", 20, 100, 20, WHITE);
+ DrawText("Space - drawing function", 20, 130, 20, WHITE);
+ if (useDrawModelPoints) DrawText("DrawModelPoints()", 20, 160, 20, GREEN);
+ else DrawText("DrawPoint3D()", 20, 160, 20, RED);
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadModel(model);
+ CloseWindow();
+ //--------------------------------------------------------------------------------------
+ return 0;
+}
+
+// Generate a spherical point cloud
+Mesh GenPoints(int numPoints)
+{
+ Mesh mesh = {
+ .triangleCount = 1,
+ .vertexCount = numPoints,
+ .vertices = (float *)MemAlloc(numPoints * 3 * sizeof(float)),
+ .colors = (unsigned char*)MemAlloc(numPoints * 4 * sizeof(unsigned char)),
+ };
+
+ // https://en.wikipedia.org/wiki/Spherical_coordinate_system
+ for (int i = 0; i < numPoints; i++)
+ {
+ float theta = PI * rand() / RAND_MAX;
+ float phi = 2.0f * PI * rand() / RAND_MAX;
+ float r = 10.0f * rand() / RAND_MAX;
+ mesh.vertices[i * 3 + 0] = r * sin(theta) * cos(phi);
+ mesh.vertices[i * 3 + 1] = r * sin(theta) * sin(phi);
+ mesh.vertices[i * 3 + 2] = r * cos(theta);
+ Color 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(&mesh, false);
+ return mesh;
+}
diff --git a/examples/models/models_point_rendering.png b/examples/models/models_point_rendering.png
new file mode 100644
index 000000000000..a1fc718e4dd5
Binary files /dev/null and b/examples/models/models_point_rendering.png differ
diff --git a/src/raylib.h b/src/raylib.h
index 0759c259f18e..f51a444e1f07 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -1545,6 +1545,8 @@ RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint);
RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters
RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set)
RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
+RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points
+RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters
RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires)
RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture
RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source
diff --git a/src/rmodels.c b/src/rmodels.c
index 8afa3df574c5..966a8665c622 100644
--- a/src/rmodels.c
+++ b/src/rmodels.c
@@ -3631,6 +3631,30 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float
rlDisableWireMode();
}
+// Draw a model points
+void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
+{
+ rlEnablePointMode();
+ rlDisableBackfaceCulling();
+
+ DrawModel(model, position, scale, tint);
+
+ rlEnableBackfaceCulling();
+ rlDisableWireMode();
+}
+
+// Draw a model points
+void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
+{
+ rlEnablePointMode();
+ rlDisableBackfaceCulling();
+
+ DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
+
+ rlEnableBackfaceCulling();
+ rlDisableWireMode();
+}
+
// Draw a billboard
void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint)
{