From 4bceddd4dee834cb5aa73a8e6567d89807860058 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Jan 2025 16:19:32 +0100 Subject: [PATCH] ADDED: example: `models_tesseract` --- examples/models/models_tesseract_view.c | 124 ++++++++++++++++++++++ examples/models/models_tesseract_view.png | Bin 0 -> 18900 bytes 2 files changed, 124 insertions(+) create mode 100644 examples/models/models_tesseract_view.c create mode 100644 examples/models/models_tesseract_view.png diff --git a/examples/models/models_tesseract_view.c b/examples/models/models_tesseract_view.c new file mode 100644 index 000000000..411b59497 --- /dev/null +++ b/examples/models/models_tesseract_view.c @@ -0,0 +1,124 @@ +/******************************************************************************************* +* +* raylib [models] example - Tesseract view +* +* NOTE: This example only works on platforms that support drag & drop (Windows, Linux, OSX, Html5?) +* +* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* +* 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 raylib contributor (?) & Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include "raymath.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - tesseract view"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 0.0f, 1.0f }; // Camera up vector (rotation towards target) + camera.fovy = 50.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera mode type + + // Find the coordinates by setting XYZW to +-1 + Vector4 tesseract[16] = { + { 1, 1, 1, 1 }, { 1, 1, 1, -1 }, + { 1, 1, -1, 1 }, { 1, 1, -1, -1 }, + { 1, -1, 1, 1 }, { 1, -1, 1, -1 }, + { 1, -1, -1, 1 }, { 1, -1, -1, -1 }, + { -1, 1, 1, 1 }, { -1, 1, 1, -1 }, + { -1, 1, -1, 1 }, { -1, 1, -1, -1 }, + { -1, -1, 1, 1 }, { -1, -1, 1, -1 }, + { -1, -1, -1, 1 }, { -1, -1, -1, -1 }, + }; + + float rotation = 0.0f; + Vector3 transformed[16] = { 0 }; + float wValues[16] = { 0 }; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + rotation = DEG2RAD*45.0f*GetTime(); + + for (int i = 0; i < 16; i++) + { + Vector4 p = tesseract[i]; + + // Rotate the XW part of the vector + Vector2 rotXW = Vector2Rotate((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/(3 - 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] = (Vector3){ p.x, p.y, p.z }; + wValues[i] = p.w; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + for (int i = 0; i < 16; i++) + { + // Draw spheres to indicate the W value + DrawSphere(transformed[i], fabsf(wValues[i]*0.1), 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 = (int)(v1.x == v2.x) + (int)(v1.y == v2.y) + (int)(v1.z == v2.z) + (int)(v1.w == v2.w); + + // Draw only differing by 1 coordinate and the lower index only (duplicate lines) + if (diff == 3 && i < j) DrawLine3D(transformed[i], transformed[j], MAROON); + } + } + EndMode3D(); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/models/models_tesseract_view.png b/examples/models/models_tesseract_view.png new file mode 100644 index 0000000000000000000000000000000000000000..664e560b2487c0f6ef9393e4b731ffb7658b4619 GIT binary patch literal 18900 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 DUW%+8 literal 0 HcmV?d00001