From 41c9975118c7e1065ff9311f62904bc557898188 Mon Sep 17 00:00:00 2001 From: iann Date: Tue, 11 Nov 2025 03:48:20 +0900 Subject: [PATCH] reduce LOC --- examples/Makefile | 2 +- examples/core/core_3d_camera_view.c | 265 ++++++ examples/core/core_3d_camera_view_opengl33.c | 407 --------- .../core/core_3d_camera_view_opengl33.png | Bin 103636 -> 0 bytes examples/core/core_3d_that_one_example.c | 852 ++++++++++++++++++ 5 files changed, 1118 insertions(+), 408 deletions(-) create mode 100644 examples/core/core_3d_camera_view.c delete mode 100644 examples/core/core_3d_camera_view_opengl33.c delete mode 100644 examples/core/core_3d_camera_view_opengl33.png create mode 100644 examples/core/core_3d_that_one_example.c diff --git a/examples/Makefile b/examples/Makefile index 06be40e5e..b41de1944 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -523,7 +523,7 @@ CORE = \ core/core_3d_picking \ core/core_3d_fixed_function_didactic \ core/core_3d_camera_view_opengl11 \ - core/core_3d_camera_view_opengl33 \ + core/core_3d_camera_view \ core/core_automation_events \ core/core_basic_screen_manager \ core/core_basic_window \ diff --git a/examples/core/core_3d_camera_view.c b/examples/core/core_3d_camera_view.c new file mode 100644 index 000000000..12537f14a --- /dev/null +++ b/examples/core/core_3d_camera_view.c @@ -0,0 +1,265 @@ +/******************************************************************************************* +* +* raylib [core] example - Camera View +* Example complexity rating: [★★★★] 4/4 +* +* Example originally created with raylib 6.0? (target) +* +* Example contributed by IANN (@meisei4) and reviewed by Ramon Santamaria (@raysan5) and community +* +* 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-2025 @meisei4 +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" +#include "rlgl.h" +#include + +#define BAHAMA_BLUE CLITERAL(Color){ 0, 102, 153, 255 } +#define SUNFLOWER CLITERAL(Color){ 255, 204, 153, 255 } +#define ANAKIWA CLITERAL(Color){ 153, 204, 255, 255 } +#define MARINER CLITERAL(Color){ 51, 102, 204, 255 } +#define NEON_CARROT CLITERAL(Color){ 255, 153, 51, 255 } +#define EGGPLANT CLITERAL(Color){ 102, 68, 102, 255 } +#define HOPBUSH CLITERAL(Color){ 204, 102, 153, 255 } + +typedef unsigned short Triangle[3]; + +enum Flags +{ + FLAG_PAUSE = 1u<<1, + FLAG_JUGEMU = 1u<<2, + FLAG_ORTHO = 1u<<3, + GEN_CUBE = 1u<<4, + GEN_SPHERE = 1u<<5, + GEN_KNOT = 1u<<6 +}; + +static unsigned int gflags = FLAG_JUGEMU | GEN_CUBE; + +#define PAUSED() ((gflags & FLAG_PAUSE) != 0) +#define JUGEMU_MODE() ((gflags & FLAG_JUGEMU) != 0) +#define ORTHO_MODE() ((gflags & FLAG_ORTHO) != 0) +#define TOGGLE(K, F) do { if (IsKeyPressed(K)) { gflags ^= (F); } } while (0) + +static unsigned int targetMesh = 0; +#define NUM_MODELS 3 +#define CYCLE_MESH(K, I, F) do { if (IsKeyPressed(K)) { targetMesh = (I); gflags = (gflags & ~(GEN_CUBE|GEN_SPHERE|GEN_KNOT)) | (F); } } while (0) + +static float fovyPerspective = 60.0f; +static float nearPlaneHeightOrthographic = 1.0f; +static Vector3 yAxis = { 0.0f, 1.0f, 0.0f }; +static Vector3 modelPos = { 0.0f, 0.0f, 0.0f }; +static Vector3 modelScale = { 1.0f, 1.0f, 1.0f }; +static Vector3 mainPos = { 0.0f, 0.0f, 2.0f }; +static Vector3 jugemuPos = { 3.0f, 1.0f, 3.0f }; + +static Vector3 *ComputeFrustumCorners(const Camera3D *main, float aspect, float near, float far); +static void OrbitSpace(Camera3D *jugemu, float dt); +static float OrthoBlendFactor(float dt); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - camera view"); + RenderTexture2D perspectiveCorrectRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); + float near = 1.0f; + float far = 3.0f; + float aspect = (float)GetScreenWidth()/(float)GetScreenHeight(); + nearPlaneHeightOrthographic = 2.0f*near*tanf(DEG2RAD*fovyPerspective*0.5f); + float meshRotation = 0.0f; + + Camera3D main = { 0 }; + main.position = mainPos; + main.target = modelPos; + main.up = yAxis; + main.projection = (ORTHO_MODE())? CAMERA_ORTHOGRAPHIC : CAMERA_PERSPECTIVE; + main.fovy = (ORTHO_MODE())? nearPlaneHeightOrthographic: fovyPerspective; + + Camera3D jugemu = (Camera3D){ 0 }; + jugemu.position = jugemuPos; + jugemu.target = modelPos; + jugemu.up = yAxis; + jugemu.fovy = fovyPerspective; + jugemu.projection = CAMERA_PERSPECTIVE; + + Model models[NUM_MODELS] = { 0 }; + models[0] = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); + models[1] = LoadModelFromMesh(GenMeshSphere(0.5f, 8, 8)); + models[2] = LoadModelFromMesh(GenMeshKnot(1.0f, 1.0f, 16, 128)); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + aspect = (float)GetScreenWidth()/(float)GetScreenHeight(); + TOGGLE(KEY_SPACE, FLAG_PAUSE); + TOGGLE(KEY_J, FLAG_JUGEMU); + TOGGLE(KEY_O, FLAG_ORTHO); + CYCLE_MESH(KEY_ONE, 0, GEN_CUBE); + CYCLE_MESH(KEY_TWO, 1, GEN_SPHERE); + CYCLE_MESH(KEY_THREE, 2, GEN_KNOT); + + OrthoBlendFactor(GetFrameTime()); + + if (!PAUSED()) meshRotation -= 1.25f*GetFrameTime(); + + OrbitSpace(&jugemu, GetFrameTime()); + main.projection = (ORTHO_MODE())? CAMERA_ORTHOGRAPHIC : CAMERA_PERSPECTIVE; + main.fovy = (ORTHO_MODE())? nearPlaneHeightOrthographic : fovyPerspective; + + Model *displayModel = &models[targetMesh]; + + BeginTextureMode(perspectiveCorrectRenderTexture); + ClearBackground(BLANK); + BeginMode3D(main); + DrawModelWiresEx(*displayModel, modelPos, yAxis, RAD2DEG*meshRotation, modelScale, MARINER); + EndMode3D(); + EndTextureMode(); + + Vector3 *corners = ComputeFrustumCorners(&main, aspect, near, far); + Vector3 *nearPts = corners; + Vector3 *farPts = corners + 4; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(BLACK); + if (JUGEMU_MODE()) BeginMode3D(jugemu); else BeginMode3D(main); + Vector3 depth = Vector3Normalize(Vector3Subtract(main.target, main.position)); + Vector3 right = Vector3Normalize(Vector3CrossProduct(depth, main.up)); + Vector3 up = Vector3Normalize(Vector3CrossProduct(right, depth)); + DrawLine3D(main.position, Vector3Add(main.position, right), NEON_CARROT); + DrawLine3D(main.position, Vector3Add(main.position, up), NEON_CARROT); + DrawLine3D(main.position, Vector3Add(main.position, depth), MARINER); + + DrawModelWiresEx(*displayModel, modelPos, yAxis, RAD2DEG*meshRotation, modelScale, MARINER); + + if (JUGEMU_MODE()) + { + // Draw Frustum wires + DrawLine3D(nearPts[0], nearPts[1], NEON_CARROT); + DrawLine3D(nearPts[1], nearPts[2], NEON_CARROT); + DrawLine3D(nearPts[2], nearPts[3], NEON_CARROT); + DrawLine3D(nearPts[3], nearPts[0], NEON_CARROT); + + DrawLine3D(farPts[0], farPts[1], EGGPLANT); + DrawLine3D(farPts[1], farPts[2], EGGPLANT); + DrawLine3D(farPts[2], farPts[3], EGGPLANT); + DrawLine3D(farPts[3], farPts[0], EGGPLANT); + + DrawLine3D(nearPts[0], farPts[0], HOPBUSH); + DrawLine3D(nearPts[1], farPts[1], HOPBUSH); + DrawLine3D(nearPts[2], farPts[2], HOPBUSH); + DrawLine3D(nearPts[3], farPts[3], HOPBUSH); + + //Capture RenderTexture for near clip plane + rlSetTexture(perspectiveCorrectRenderTexture.texture.id); + rlBegin(RL_QUADS); + rlColor4ub(255, 255, 255, 255); + rlTexCoord2f(0.0f, 1.0f); rlVertex3f(nearPts[0].x, nearPts[0].y, nearPts[0].z); + rlTexCoord2f(0.0f, 0.0f); rlVertex3f(nearPts[3].x, nearPts[3].y, nearPts[3].z); + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(nearPts[2].x, nearPts[2].y, nearPts[2].z); + rlTexCoord2f(1.0f, 1.0f); rlVertex3f(nearPts[1].x, nearPts[1].y, nearPts[1].z); + rlEnd(); + rlSetTexture(0); + } + + EndMode3D(); + + DrawText("[1]: CUBE [2]: SPHERE [3]: KNOT", 12, 12, 20, NEON_CARROT); + DrawText("ARROWS: MOVE | SPACEBAR: PAUSE", 12, 38, 20, NEON_CARROT); + DrawText("W S : ZOOM ", 12, 64, 20, NEON_CARROT); + DrawText((targetMesh == 0)? "GEN_CUBE" : (targetMesh == 1)? "GEN_SPHERE" : "GEN_KNOT", 12, 205, 20, NEON_CARROT); + DrawText("LENS [ O ]:", 510, 366, 20, SUNFLOWER); + DrawText((ORTHO_MODE())? "ORTHOGRAPHIC" : "PERSPECTIVE", 630, 366, 20, (ORTHO_MODE())? BAHAMA_BLUE : ANAKIWA); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(models[0]); + UnloadModel(models[1]); + UnloadModel(models[2]); + if (perspectiveCorrectRenderTexture.id) UnloadRenderTexture(perspectiveCorrectRenderTexture); + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} + +static Vector3 *ComputeFrustumCorners(const Camera3D *main, float aspect, float near, float far) +{ + + Vector3 depth = Vector3Normalize(Vector3Subtract(main->target, main->position)); + Vector3 right = Vector3Normalize(Vector3CrossProduct(depth, main->up)); + Vector3 up = Vector3Normalize(Vector3CrossProduct(right, depth)); + + float perspHalfHNear = near*tanf(DEG2RAD*fovyPerspective*0.5f); + float orthoHalfH = 0.5f*nearPlaneHeightOrthographic; + float halfHNear = Lerp(perspHalfHNear, orthoHalfH, OrthoBlendFactor(0.0f)); + float halfWNear = halfHNear*aspect; + + float perspHalfHFar = far*tanf(DEG2RAD*fovyPerspective*0.5f); + float halfHFar = Lerp(perspHalfHFar, orthoHalfH, OrthoBlendFactor(0.0f)); + float halfWFar = halfHFar*aspect; + + Vector3 centerNear = Vector3Add(main->position, Vector3Scale(depth, near)); + Vector3 centerFar = Vector3Add(main->position, Vector3Scale(depth, far)); + + static Vector3 corners[8]; + corners[0] = Vector3Add(centerNear, Vector3Add(Vector3Scale(up, halfHNear), Vector3Scale(right, -halfWNear))); + corners[1] = Vector3Add(centerNear, Vector3Add(Vector3Scale(up, halfHNear), Vector3Scale(right, halfWNear))); + corners[2] = Vector3Add(centerNear, Vector3Add(Vector3Scale(up, -halfHNear), Vector3Scale(right, halfWNear))); + corners[3] = Vector3Add(centerNear, Vector3Add(Vector3Scale(up, -halfHNear), Vector3Scale(right, -halfWNear))); + + corners[4] = Vector3Add(centerFar, Vector3Add(Vector3Scale(up, halfHFar), Vector3Scale(right, -halfWFar))); + corners[5] = Vector3Add(centerFar, Vector3Add(Vector3Scale(up, halfHFar), Vector3Scale(right, halfWFar))); + corners[6] = Vector3Add(centerFar, Vector3Add(Vector3Scale(up, -halfHFar), Vector3Scale(right, halfWFar))); + corners[7] = Vector3Add(centerFar, Vector3Add(Vector3Scale(up, -halfHFar), Vector3Scale(right, -halfWFar))); + + return corners; +} + +static void OrbitSpace(Camera3D *jugemu, float dt) +{ + float radius = Vector3Length(jugemu->position); + float azimuth = atan2f(jugemu->position.z, jugemu->position.x); + float horizontalRadius = sqrtf(jugemu->position.x*jugemu->position.x + jugemu->position.z*jugemu->position.z); + float elevation = atan2f(jugemu->position.y, horizontalRadius); + if (IsKeyDown(KEY_LEFT)) azimuth += 1.0f*dt; + if (IsKeyDown(KEY_RIGHT)) azimuth -= 1.0f*dt; + if (IsKeyDown(KEY_UP)) elevation += 1.0f*dt; + if (IsKeyDown(KEY_DOWN)) elevation -= 1.0f*dt; + if (IsKeyDown(KEY_W)) radius -= 1.0f*dt; + if (IsKeyDown(KEY_S)) radius += 1.0f*dt; + elevation = Clamp(elevation, -PI/2 + 0.1f, PI/2 - 0.1f); + jugemu->position.x = Clamp(radius, 0.25f, 10.0f)*cosf(elevation)*cosf(azimuth); + jugemu->position.y = Clamp(radius, 0.25f, 10.0f)*sinf(elevation); + jugemu->position.z = Clamp(radius, 0.25f, 10.0f)*cosf(elevation)*sinf(azimuth); +} + +static float OrthoBlendFactor(float dt) +{ + static float blend = 0.0f; + if (dt > 0.0f) blend = Clamp(blend + ((ORTHO_MODE())? 1.0f : -1.0f)*5.0f*dt, 0.0f, 1.0f); + return blend; +} \ No newline at end of file diff --git a/examples/core/core_3d_camera_view_opengl33.c b/examples/core/core_3d_camera_view_opengl33.c deleted file mode 100644 index 7ab5e20db..000000000 --- a/examples/core/core_3d_camera_view_opengl33.c +++ /dev/null @@ -1,407 +0,0 @@ -/******************************************************************************************* -* -* raylib [core] example - Camera View -* Example complexity rating: [★★★★] 4/4 -* -* Example originally created with raylib 6.0? (target) -* -* Example contributed by IANN (@meisei4) and reviewed by Ramon Santamaria (@raysan5) and community -* -* 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-2025 @meisei4 -* -********************************************************************************************/ - -#include "raylib.h" -#include "raymath.h" -#include "rlgl.h" -#include -#include -#include - -static const char *vert = - "#version 330\n" - "in vec3 vertexPosition;\n" - "in vec2 vertexTexCoord;\n" - "in vec3 vertexNormal;\n" - "in vec4 vertexColor;\n" - "uniform mat4 mvp;\n" - "out vec2 fragTexCoord;\n" - "out vec4 fragColor;\n" - "uniform int useVertexColors;\n" - "void main()\n" - "{\n" - " if (useVertexColors == 1) {\n" - " fragColor = vertexColor;\n" - " } else {\n" - " fragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" - " }\n" - " fragTexCoord = vertexTexCoord;\n" - " gl_Position = mvp * vec4(vertexPosition, 1.0);\n" - "}\n"; - -static const char *frag = - "#version 330\n" - "in vec2 fragTexCoord;\n" - "in vec4 fragColor;\n" - "uniform sampler2D texture0;\n" - "uniform vec4 colDiffuse;\n" - "out vec4 finalColor;\n" - "void main()\n" - "{\n" - " vec4 texelColor = texture(texture0, fragTexCoord);\n" - " vec4 outColor = texelColor*fragColor*colDiffuse;\n" - " if (outColor.a <= 0.0) discard; \n" - " finalColor = outColor;\n" - "}\n"; - -static Shader customShader = { 0 }; -static int useVertexColorsLoc = -1; - -#define BAHAMA_BLUE CLITERAL(Color){ 0, 102, 153, 255 } -#define SUNFLOWER CLITERAL(Color){ 255, 204, 153, 255 } -#define ANAKIWA CLITERAL(Color){ 153, 204, 255, 255 } -#define MARINER CLITERAL(Color){ 51, 102, 204, 255 } -#define NEON_CARROT CLITERAL(Color){ 255, 153, 51, 255 } -#define EGGPLANT CLITERAL(Color){ 102, 68, 102, 255 } -#define HOPBUSH CLITERAL(Color){ 204, 102, 153, 255 } -#define LILAC CLITERAL(Color){ 204, 153, 204, 255 } -#define RED_DAMASK CLITERAL(Color){ 221, 102, 68, 255 } -#define CHESTNUT_ROSE CLITERAL(Color){ 204, 102, 102, 255 } - -typedef unsigned short Triangle[3]; - -enum Flags -{ - FLAG_ASPECT = 1u<<0, - FLAG_PAUSE = 1u<<1, - FLAG_JUGEMU = 1u<<2, - FLAG_ORTHO = 1u<<3, - GEN_CUBE = 1u<<4, - GEN_SPHERE = 1u<<5, - GEN_KNOT = 1u<<6 -}; - -static unsigned int gflags = FLAG_ASPECT | FLAG_JUGEMU | GEN_CUBE; - -#define ASPECT_CORRECT() ((gflags & FLAG_ASPECT) != 0) -#define PAUSED() ((gflags & FLAG_PAUSE) != 0) -#define JUGEMU_MODE() ((gflags & FLAG_JUGEMU) != 0) -#define ORTHO_MODE() ((gflags & FLAG_ORTHO) != 0) -#define TOGGLE(K, F) do { if (IsKeyPressed(K)) { gflags ^= (F); } } while (0) - -static unsigned int targetMesh = 0; -#define NUM_MODELS 3 -#define CYCLE_MESH(K, I, F) do { if (IsKeyPressed(K)) { targetMesh = (I); gflags = (gflags & ~(GEN_CUBE|GEN_SPHERE|GEN_KNOT)) | (F); } } while (0) - -static int fontSize = 20; -static float angularVelocity = 1.25f; -static float fovyPerspective = 60.0f; -static float nearPlaneHeightOrthographic = 1.0f; -static float blendScalar = 5.0f; -static Vector3 yAxis = { 0.0f, 1.0f, 0.0f }; -static Vector3 modelPos = { 0.0f, 0.0f, 0.0f }; -static Vector3 modelScale = { 1.0f, 1.0f, 1.0f }; -static Vector3 mainPos = { 0.0f, 0.0f, 2.0f }; -static Vector3 jugemuPosIso = { 3.0f, 1.0f, 3.0f }; - -static void UpdateSpatialFrame(Camera3D *main, float aspect, float near, float far, Mesh *spatialFrame); -static void DrawSpatialFrame(Mesh *spatialFrame); -static void PerspectiveCorrectCapture(Camera3D *main, Model *model, RenderTexture2D *perspectiveCorrectRenderTexture, float rotation); -static void FillVertexColors(Mesh *mesh); -static void OrbitSpace(Camera3D *jugemu, float dt); -static Vector3 TranslateRotateScale(Vector3 coordinate, Vector3 pos, Vector3 scale, float rotation); -static Vector3 Intersect(Camera3D *main, float near, Vector3 worldCoord); -static float OrthoBlendFactor(float dt); - -//------------------------------------------------------------------------------------ -// Program main entry point -//------------------------------------------------------------------------------------ -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - fixed function didactic"); - customShader = LoadShaderFromMemory(vert, frag); - useVertexColorsLoc = GetShaderLocation(customShader, "useVertexColors"); - RenderTexture2D perspectiveCorrectRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); - float near = 1.0f; - float far = 3.0f; - float aspect = (float)GetScreenWidth()/(float)GetScreenHeight(); - nearPlaneHeightOrthographic = 2.0f*near*tanf(DEG2RAD*fovyPerspective*0.5f); - float meshRotation = 0.0f; - - Camera3D main = { 0 }; - main.position = mainPos; - main.target = modelPos; - main.up = yAxis; - main.projection = (ORTHO_MODE())? CAMERA_ORTHOGRAPHIC : CAMERA_PERSPECTIVE; - main.fovy = (ORTHO_MODE())? nearPlaneHeightOrthographic: fovyPerspective; - - Camera3D jugemu = (Camera3D){ 0 }; - jugemu.position = jugemuPosIso; - jugemu.target = modelPos; - jugemu.up = yAxis; - jugemu.fovy = fovyPerspective; - jugemu.projection = CAMERA_PERSPECTIVE; - - Model models[NUM_MODELS] = { 0 }; - models[0] = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); - models[1] = LoadModelFromMesh(GenMeshSphere(0.5f, 8, 8)); - models[2] = LoadModelFromMesh(GenMeshKnot(1.0f, 1.0f, 16, 128)); - for (int i = 0; i < NUM_MODELS; i++) - { - Mesh *mesh = &models[i].meshes[0]; - mesh->vaoId = 0; - if (!mesh->indices) - { - mesh->indices = RL_CALLOC(mesh->vertexCount, sizeof(unsigned short)); - for (int j = 0; j < mesh->vertexCount; j++) mesh->indices[j] = (unsigned short)j; - mesh->triangleCount = mesh->vertexCount/3; - } - FillVertexColors(mesh); - UploadMesh(mesh, true); - } - - Mesh tempCube = GenMeshCube(1.0f, 1.0f, 1.0f); - Mesh spatialFrame = { 0 }; - spatialFrame.vertexCount = tempCube.vertexCount; - spatialFrame.triangleCount = tempCube.triangleCount; - spatialFrame.vertices = RL_MALLOC(spatialFrame.vertexCount * 3 * sizeof(float)); - spatialFrame.normals = RL_MALLOC(spatialFrame.vertexCount * 3 * sizeof(float)); - spatialFrame.texcoords = RL_MALLOC(spatialFrame.vertexCount * 2 * sizeof(float)); - spatialFrame.indices = RL_MALLOC(spatialFrame.triangleCount * 3 * sizeof(unsigned short)); - spatialFrame.colors = RL_CALLOC(spatialFrame.vertexCount, sizeof(Color)); - memcpy(spatialFrame.vertices, tempCube.vertices, spatialFrame.vertexCount * 3 * sizeof(float)); - memcpy(spatialFrame.normals, tempCube.normals, spatialFrame.vertexCount * 3 * sizeof(float)); - memcpy(spatialFrame.texcoords, tempCube.texcoords, spatialFrame.vertexCount * 2 * sizeof(float)); - memcpy(spatialFrame.indices, tempCube.indices, spatialFrame.triangleCount * 3 * sizeof(unsigned short)); - for (int i = 0; i < spatialFrame.vertexCount; i++) ((Color *)spatialFrame.colors)[i] = (Color){ 255, 255, 255, 0 }; - for (int i = 0; i < 4; i++) ((Color *)spatialFrame.colors)[i].a = 255; - UnloadMesh(tempCube); - UploadMesh(&spatialFrame, true); - Model spatialFrameModel = LoadModelFromMesh(spatialFrame); - spatialFrameModel.materials[0].shader = customShader; - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - while (!WindowShouldClose()) - { - // Update - //---------------------------------------------------------------------------------- - aspect = (float)GetScreenWidth()/(float)GetScreenHeight(); - TOGGLE(KEY_Q, FLAG_ASPECT); - TOGGLE(KEY_SPACE, FLAG_PAUSE); - TOGGLE(KEY_J, FLAG_JUGEMU); - TOGGLE(KEY_O, FLAG_ORTHO); - CYCLE_MESH(KEY_ONE, 0, GEN_CUBE); - CYCLE_MESH(KEY_TWO, 1, GEN_SPHERE); - CYCLE_MESH(KEY_THREE, 2, GEN_KNOT); - - OrthoBlendFactor(GetFrameTime()); - - if (!PAUSED()) meshRotation -= angularVelocity*GetFrameTime(); - - OrbitSpace(&jugemu, GetFrameTime()); - main.projection = (ORTHO_MODE())? CAMERA_ORTHOGRAPHIC : CAMERA_PERSPECTIVE; - main.fovy = (ORTHO_MODE())? nearPlaneHeightOrthographic : fovyPerspective; - - Model *displayModel = &models[targetMesh]; - - PerspectiveCorrectCapture(&main, displayModel, &perspectiveCorrectRenderTexture, meshRotation); - - UpdateSpatialFrame(&main, aspect, near, far, &spatialFrame); - UpdateMeshBuffer(spatialFrameModel.meshes[0], RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, spatialFrame.vertices, spatialFrame.vertexCount*sizeof(Vector3), 0); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(BLACK); - if (JUGEMU_MODE()) BeginMode3D(jugemu); else BeginMode3D(main); - Vector3 depth = Vector3Normalize(Vector3Subtract(main.target, main.position)); - Vector3 right = Vector3Normalize(Vector3CrossProduct(depth, main.up)); - Vector3 up = Vector3Normalize(Vector3CrossProduct(right, depth)); - DrawLine3D(main.position, Vector3Add(main.position, right), NEON_CARROT); - DrawLine3D(main.position, Vector3Add(main.position, up), LILAC); - DrawLine3D(main.position, Vector3Add(main.position, depth), MARINER); - - if (JUGEMU_MODE()) DrawSpatialFrame(&spatialFrame); - - DrawModelEx(*displayModel, modelPos, yAxis, RAD2DEG*meshRotation, modelScale, WHITE); - - unsigned int cacheID = displayModel->materials[0].maps[MATERIAL_MAP_ALBEDO].texture.id; - displayModel->materials[0].maps[MATERIAL_MAP_ALBEDO].texture.id = rlGetTextureIdDefault(); - DrawModelWiresEx(*displayModel, modelPos, yAxis, RAD2DEG*meshRotation, modelScale, MARINER); - rlSetPointSize(4.0f); - DrawModelPointsEx(*displayModel, modelPos, yAxis, RAD2DEG*meshRotation, modelScale, LILAC); - displayModel->materials[0].maps[MATERIAL_MAP_ALBEDO].texture.id = cacheID; - - if (JUGEMU_MODE()) - { - Vector3 *vertices = (Vector3 *)displayModel->meshes[0].vertices; - Triangle *triangles = (Triangle *)displayModel->meshes[0].indices; - for (int i = 0; i < displayModel->meshes[0].triangleCount; i++) - { - Vector3 a = TranslateRotateScale(vertices[triangles[i][0]], modelPos, modelScale, meshRotation); - Vector3 b = TranslateRotateScale(vertices[triangles[i][1]], modelPos, modelScale, meshRotation); - Vector3 c = TranslateRotateScale(vertices[triangles[i][2]], modelPos, modelScale, meshRotation); - if (Vector3DotProduct(Vector3Normalize(Vector3CrossProduct(Vector3Subtract(b, a), Vector3Subtract(c, a))), depth) > 0.0f) continue; - DrawLine3D(a, Intersect(&main, near, a), (Color){ RED_DAMASK.r, RED_DAMASK.g, RED_DAMASK.b, 20 }); - DrawLine3D(b, Intersect(&main, near, b), (Color){ RED_DAMASK.r, RED_DAMASK.g, RED_DAMASK.b, 20 }); - DrawLine3D(c, Intersect(&main, near, c), (Color){ RED_DAMASK.r, RED_DAMASK.g, RED_DAMASK.b, 20 }); - } - } - spatialFrameModel.materials[0].maps[MATERIAL_MAP_ALBEDO].texture = perspectiveCorrectRenderTexture.texture; - //NOTE: there is still the alpha threshold issue without custom frag shader... - int useColors = 1; - SetShaderValue(spatialFrameModel.materials[0].shader, useVertexColorsLoc, &useColors, SHADER_UNIFORM_INT); - if (JUGEMU_MODE()) DrawModel(spatialFrameModel, modelPos, 1.0f, WHITE); - - EndMode3D(); - - DrawText("[1]: CUBE [2]: SPHERE [3]: KNOT", 12, 12, fontSize, NEON_CARROT); - DrawText("ARROWS: MOVE | SPACEBAR: PAUSE", 12, 38, fontSize, NEON_CARROT); - DrawText("W S : ZOOM ", 12, 64, fontSize, NEON_CARROT); - DrawText((targetMesh == 0)? "GEN_CUBE" : (targetMesh == 1)? "GEN_SPHERE" : "GEN_KNOT", 12, 205, fontSize, NEON_CARROT); - DrawText("ASPECT [ Q ]:", 12, 392, fontSize, SUNFLOWER); - DrawText((ASPECT_CORRECT())? "CORRECT" : "INCORRECT", 230, 392, fontSize, (ASPECT_CORRECT())? ANAKIWA : CHESTNUT_ROSE); - DrawText("LENS [ O ]:", 510, 366, fontSize, SUNFLOWER); - DrawText((ORTHO_MODE())? "ORTHOGRAPHIC" : "PERSPECTIVE", 630, 366, fontSize, (ORTHO_MODE())? BAHAMA_BLUE : ANAKIWA); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadModel(models[0]); - UnloadModel(models[1]); - UnloadModel(models[2]); - UnloadModel(spatialFrameModel); - if (perspectiveCorrectRenderTexture.id) UnloadRenderTexture(perspectiveCorrectRenderTexture); - UnloadShader(customShader); - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; -} - -static void UpdateSpatialFrame(Camera3D *main, float aspect, float near, float far, Mesh *spatialFrame) -{ - Vector3 depth = Vector3Normalize(Vector3Subtract(main->target, main->position)); - Vector3 right = Vector3Normalize(Vector3CrossProduct(depth, main->up)); - Vector3 up = Vector3Normalize(Vector3CrossProduct(right, depth)); - float halfHNear = Lerp(near*tanf(DEG2RAD*fovyPerspective*0.5f), 0.5f*nearPlaneHeightOrthographic, OrthoBlendFactor(0.0f)); - float halfWNear = halfHNear*aspect; - float halfHFar = Lerp(far*tanf(DEG2RAD*fovyPerspective*0.5f), 0.5f*nearPlaneHeightOrthographic, OrthoBlendFactor(0.0f)); - float halfWFar = halfHFar*aspect; - float halfDepth = 0.5f*(far - near); - Vector3 centerNear = Vector3Add(main->position, Vector3Scale(depth, near)); - - for (int i = 0; i < spatialFrame->vertexCount; ++i) - { - Vector3 offset = Vector3Subtract(((Vector3 *)spatialFrame->vertices)[i], centerNear); - float xSign = (Vector3DotProduct(offset, right) >= 0.0f)? 1.0f : -1.0f; - float ySign = (Vector3DotProduct(offset, up) >= 0.0f)? 1.0f : -1.0f; - float farMask = (Vector3DotProduct(offset, depth) > halfDepth)? 1.0f : 0.0f; - float finalHalfW = halfWNear + farMask*(halfWFar - halfWNear); - float finalHalfH = halfHNear + farMask*(halfHFar - halfHNear); - Vector3 center = Vector3Add(centerNear, Vector3Scale(depth, farMask*2.0f*halfDepth)); - ((Vector3 *)spatialFrame->vertices)[i] = Vector3Add(center, Vector3Add(Vector3Scale(right, xSign*finalHalfW), Vector3Scale(up, ySign*finalHalfH))); - } -} - -static void DrawSpatialFrame(Mesh *spatialFrame) -{ - static int frontFaces[4][2] = { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 } }; - static int backFaces[4][2] = { { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 } }; - static int ribFaces[4][2] = { { 0, 4 }, { 1, 7 }, { 2, 6 }, { 3, 5 } }; - static int (*faces[3])[2] = { frontFaces, backFaces, ribFaces }; - - for (int i = 0; i < 3; i++) - for (int j = 0; j < 4; j++) - { - Vector3 startPosition = ((Vector3 *)spatialFrame->vertices)[faces[i][j][0]]; - Vector3 endPosition = ((Vector3 *)spatialFrame->vertices)[faces[i][j][1]]; - DrawLine3D(startPosition, endPosition, (i == 0)? NEON_CARROT : (i == 1)? EGGPLANT : HOPBUSH); - } -} - -static void PerspectiveCorrectCapture(Camera3D *main, Model *model, RenderTexture2D *perspectiveCorrectRenderTexture, float rotation) -{ - BeginTextureMode(*perspectiveCorrectRenderTexture); - ClearBackground(BLANK); - BeginMode3D(*main); - DrawModelEx(*model, modelPos, yAxis, RAD2DEG*rotation, modelScale, WHITE); - DrawModelWiresEx(*model, modelPos, yAxis, RAD2DEG*rotation, modelScale, MARINER); - EndMode3D(); - EndTextureMode(); -} - -static void OrbitSpace(Camera3D *jugemu, float dt) -{ - float radius = Vector3Length(jugemu->position); - float azimuth = atan2f(jugemu->position.z, jugemu->position.x); - float horizontalRadius = sqrtf(jugemu->position.x*jugemu->position.x + jugemu->position.z*jugemu->position.z); - float elevation = atan2f(jugemu->position.y, horizontalRadius); - if (IsKeyDown(KEY_LEFT)) azimuth += 1.0f*dt; - if (IsKeyDown(KEY_RIGHT)) azimuth -= 1.0f*dt; - if (IsKeyDown(KEY_UP)) elevation += 1.0f*dt; - if (IsKeyDown(KEY_DOWN)) elevation -= 1.0f*dt; - if (IsKeyDown(KEY_W)) radius -= 1.0f*dt; - if (IsKeyDown(KEY_S)) radius += 1.0f*dt; - elevation = Clamp(elevation, -PI/2 + 0.1f, PI/2 - 0.1f); - jugemu->position.x = Clamp(radius, 0.25f, 10.0f)*cosf(elevation)*cosf(azimuth); - jugemu->position.y = Clamp(radius, 0.25f, 10.0f)*sinf(elevation); - jugemu->position.z = Clamp(radius, 0.25f, 10.0f)*cosf(elevation)*sinf(azimuth); -} - -static void FillVertexColors(Mesh *mesh) -{ - if (!mesh->colors) mesh->colors = RL_CALLOC(mesh->vertexCount, sizeof(Color)); - Color *colors = (Color *)mesh->colors; - Vector3 *vertices = (Vector3 *)mesh->vertices; - BoundingBox bounds = GetMeshBoundingBox(*mesh); - - for (int i = 0; i < mesh->vertexCount; ++i) - { - Vector3 vertex = vertices[i]; - float nx = (vertex.x - 0.5f*(bounds.min.x + bounds.max.x))/(0.5f*(bounds.max.x - bounds.min.x)); - float ny = (vertex.y - 0.5f*(bounds.min.y + bounds.max.y))/(0.5f*(bounds.max.y - bounds.min.y)); - float nz = (vertex.z - 0.5f*(bounds.min.z + bounds.max.z))/(0.5f*(bounds.max.z - bounds.min.z)); - float len = sqrtf(nx*nx + ny*ny + nz*nz); - colors[i] = (Color){ lrintf(127.5f*(nx/len + 1.0f)), lrintf(127.5f*(ny/len + 1.0f)), lrintf(127.5f*(nz/len + 1.0f)), 255 }; - } -} - -static Vector3 TranslateRotateScale(Vector3 coordinate, Vector3 pos, Vector3 scale, float rotation) -{ - return Vector3Transform(coordinate, MatrixMultiply(MatrixMultiply(MatrixScale(scale.x, scale.y, scale.z), MatrixRotateY(rotation)), MatrixTranslate(pos.x, pos.y, pos.z))); -} - -static Vector3 Intersect(Camera3D *main, float near, Vector3 worldCoord) -{ - Vector3 viewDir = Vector3Normalize(Vector3Subtract(main->target, main->position)); - Vector3 mainCameraToPoint = Vector3Subtract(worldCoord, main->position); - float depthAlongView = Vector3DotProduct(mainCameraToPoint, viewDir); - Vector3 centerNearPlane = Vector3Add(main->position, Vector3Scale(viewDir, near)); - if (depthAlongView <= 0.0f) return centerNearPlane; - float scaleToNear = near/depthAlongView; - Vector3 resultPerspective = Vector3Add(main->position, Vector3Scale(mainCameraToPoint, scaleToNear)); - Vector3 resultOrtho = Vector3Add(worldCoord, Vector3Scale(viewDir, Vector3DotProduct(Vector3Subtract(centerNearPlane, worldCoord), viewDir))); - return Vector3Lerp(resultPerspective, resultOrtho, OrthoBlendFactor(0.0f)); -} - -static float OrthoBlendFactor(float dt) -{ - static float blend = 0.0f; - if (dt > 0.0f) blend = Clamp(blend + ((ORTHO_MODE())? 1.0f : -1.0f)*blendScalar*dt, 0.0f, 1.0f); - return blend; -} \ No newline at end of file diff --git a/examples/core/core_3d_camera_view_opengl33.png b/examples/core/core_3d_camera_view_opengl33.png deleted file mode 100644 index cdac66493e0fcfce95189973e7dafa8a0de39076..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103636 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 +#include +#include + +#if defined(GRAPHICS_API_OPENGL_33) +static const char *vert = + "#version 330\n" + "in vec3 vertexPosition;\n" + "in vec2 vertexTexCoord;\n" + "in vec3 vertexNormal;\n" + "in vec4 vertexColor;\n" + "uniform mat4 mvp;\n" + "out vec2 fragTexCoord;\n" + "out vec4 fragColor;\n" + "uniform int useVertexColors;\n" + "uniform int flipTexcoordY;\n" + "void main()\n" + "{\n" + " if (useVertexColors == 1) {\n" + " fragColor = vertexColor;\n" + " } else {\n" + " fragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" + " }\n" + " fragTexCoord = vec2(vertexTexCoord.x, (flipTexcoordY == 1)? (1.0 - vertexTexCoord.y) : vertexTexCoord.y);\n" + " gl_Position = mvp * vec4(vertexPosition, 1.0);\n" + "}\n"; + +static const char *frag = + "#version 330\n" + "in vec2 fragTexCoord;\n" + "in vec4 fragColor;\n" + "uniform sampler2D texture0;\n" + "uniform vec4 colDiffuse;\n" + "out vec4 finalColor;\n" + "void main()\n" + "{\n" + " vec4 texelColor = texture(texture0, fragTexCoord);\n" + " vec4 outColor = texelColor*fragColor*colDiffuse;\n" + " if (outColor.a <= 0.0) discard; \n" + " finalColor = outColor;\n" + "}\n"; + +static Shader customShader = { 0 }; +static int useVertexColorsLoc = -1; +static int flipTexcoordYLoc = -1; +#endif + +#define BAHAMA_BLUE CLITERAL(Color){ 0, 102, 153, 255 } +#define SUNFLOWER CLITERAL(Color){ 255, 204, 153, 255 } +#define PALE_CANARY CLITERAL(Color){ 255, 255, 153, 255 } +#define ANAKIWA CLITERAL(Color){ 153, 204, 255, 255 } +#define MARINER CLITERAL(Color){ 51, 102, 204, 255 } +#define NEON_CARROT CLITERAL(Color){ 255, 153, 51, 255 } +#define EGGPLANT CLITERAL(Color){ 102, 68, 102, 255 } +#define HOPBUSH CLITERAL(Color){ 204, 102, 153, 255 } +#define LILAC CLITERAL(Color){ 204, 153, 204, 255 } +#define RED_DAMASK CLITERAL(Color){ 221, 102, 68, 255 } +#define CHESTNUT_ROSE CLITERAL(Color){ 204, 102, 102, 255 } + +typedef unsigned short Triangle[3]; + +enum Flags +{ + FLAG_NDC = 1u<<0, + FLAG_REFLECT_Y = 1u<<1, FLAG_ASPECT = 1u<<2, + FLAG_PERSPECTIVE_CORRECT = 1u<<3, + FLAG_PAUSE = 1u<<4, + FLAG_COLOR_MODE = 1u<<5, FLAG_TEXTURE_MODE = 1u<<6, + FLAG_JUGEMU = 1u<<7, + FLAG_ORTHO = 1u<<8, + FLAG_CLIP = 1u<<9, + GEN_CUBE = 1u<<10, LOAD_CUBE = 1u<<11, + GEN_SPHERE = 1u<<12, LOAD_SPHERE = 1u<<13, + GEN_KNOT = 1u<<14 +}; + +static unsigned int gflags = FLAG_ASPECT | FLAG_COLOR_MODE | FLAG_JUGEMU | GEN_CUBE; + +#define NDC_SPACE() ((gflags & FLAG_NDC) != 0) +#define REFLECT_Y() ((gflags & FLAG_REFLECT_Y) != 0) +#define ASPECT_CORRECT() ((gflags & FLAG_ASPECT) != 0) +#define PERSPECTIVE_CORRECT() ((gflags & FLAG_PERSPECTIVE_CORRECT) != 0) +#define PAUSED() ((gflags & FLAG_PAUSE) != 0) +#define COLOR_MODE() ((gflags & FLAG_COLOR_MODE) != 0) +#define TEXTURE_MODE() ((gflags & FLAG_TEXTURE_MODE) != 0) +#define JUGEMU_MODE() ((gflags & FLAG_JUGEMU) != 0) +#define ORTHO_MODE() ((gflags & FLAG_ORTHO) != 0) +#define CLIP_MODE() ((gflags & FLAG_CLIP) != 0) +#define TOGGLE(K, F) do { if (IsKeyPressed(K)) { gflags ^= (F); } } while (0) + +static unsigned int targetMesh = 0; +#define NUM_MODELS 5 +#define TARGET_GEN_CUBE() ((gflags & GEN_CUBE) != 0) +#define TARGET_LOAD_CUBE() ((gflags & LOAD_CUBE) != 0) +#define TARGET_GEN_SPHERE() ((gflags & GEN_SPHERE) != 0) +#define TARGET_LOAD_SPHERE() ((gflags & LOAD_SPHERE) != 0) +#define TARGET_GEN_KNOT() ((gflags & GEN_KNOT) != 0) +#define CYCLE_MESH(K, I, F) do { if (IsKeyPressed(K)) { targetMesh = (I); gflags = (gflags & ~(GEN_CUBE|LOAD_CUBE|GEN_SPHERE|LOAD_SPHERE|GEN_KNOT)) | (F); } } while (0) + +static int fontSize = 20; +static float angularVelocity = 1.25f; +static float fovyPerspective = 60.0f; +static float nearPlaneHeightOrthographic = 1.0f; +static float blendScalar = 5.0f; +static Vector3 yAxis = { 0.0f, 1.0f, 0.0f }; +static Vector3 modelPos = { 0.0f, 0.0f, 0.0f }; +static Vector3 modelScale = { 1.0f, 1.0f, 1.0f }; +static Vector3 mainPos = { 0.0f, 0.0f, 2.0f }; +static Vector3 jugemuPos = { 3.0f, 1.0f, 3.0f }; + +static void BasisVector(Camera3D *main, Vector3 *depthOut, Vector3 *rightOut, Vector3 *upOut); +static void WorldToNDCSpace(Camera3D *main, float aspect, float near, float far, Model *world, Model *ndc, float rotation); + +static void DrawModelFilled(Model *model, Texture2D texture, float rotation); +static void DrawModelWiresAndPoints(Model *model, float rotation); +static void DrawNearPlanePoints(Camera3D *main, float aspect, float near, Model *nearPlanePointsModel, Model *displayModel, float rotation); + +static void UpdateSpatialFrame(Camera3D *main, float aspect, float near, float far, Mesh *spatialFrame); +static void DrawSpatialFrame(Mesh *spatialFrame); + +static void PerspectiveIncorrectCapture(Camera3D *main, float aspect, float near, Model *displayModel, Texture2D meshTexture, float rotation); +#if defined(GRAPHICS_API_OPENGL_33) +static void PerspectiveCorrectCapture(Camera3D *main, Model *model, Texture2D meshTexture, RenderTexture2D *perspectiveCorrectRenderTexture, float rotation); +#else +static void PerspectiveCorrectCapture(Camera3D *main, Model *model, Texture2D meshTexture, Texture2D *perspectiveCorrectTexture, float rotation); +#endif +static void AlphaMaskPunchOut(Image *rgba, Image *mask, unsigned char threshold); +static void FillPlanarTexCoords(Mesh *mesh); +static void FillVertexColors(Mesh *mesh); +static void OrbitSpace(Camera3D *jugemu, float dt); +static Vector3 AspectCorrectAndReflectNearPlane(Vector3 intersect, Vector3 center, Vector3 right, Vector3 up, float xAspect, float yReflect); +static Vector3 TranslateRotateScale(int inverse, Vector3 coordinate, Vector3 pos, Vector3 scale, float rotation); +static Vector3 Intersect(Camera3D *main, float near, Vector3 worldCoord); +static float SpaceBlendFactor(float dt); +static float AspectBlendFactor(float dt); +static float ReflectBlendFactor(float dt); +static float OrthoBlendFactor(float dt); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - fixed function didactic"); +#if defined(GRAPHICS_API_OPENGL_33) + customShader = LoadShaderFromMemory(vert, frag); + useVertexColorsLoc = GetShaderLocation(customShader, "useVertexColors"); + flipTexcoordYLoc = GetShaderLocation(customShader, "flipTexcoordY"); + RenderTexture2D perspectiveCorrectRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); +#else + Texture2D perspectiveCorrectTexture = (Texture2D){ 0 }; +#endif + float near = 1.0f; + float far = 3.0f; + float aspect = (float)GetScreenWidth()/(float)GetScreenHeight(); + nearPlaneHeightOrthographic = 2.0f*near*tanf(DEG2RAD*fovyPerspective*0.5f); + float meshRotation = 0.0f; + + Camera3D main = { 0 }; + main.position = mainPos; + main.target = modelPos; + main.up = yAxis; + main.projection = (ORTHO_MODE())? CAMERA_ORTHOGRAPHIC : CAMERA_PERSPECTIVE; + main.fovy = (ORTHO_MODE())? nearPlaneHeightOrthographic: fovyPerspective; + + Camera3D jugemu = (Camera3D){ 0 }; + jugemu.position = jugemuPos; + jugemu.target = modelPos; + jugemu.up = yAxis; + jugemu.fovy = fovyPerspective; + jugemu.projection = CAMERA_PERSPECTIVE; + + Model worldModels[NUM_MODELS] = { 0 }; + Model ndcModels[NUM_MODELS] = { 0 }; + Model nearPlanePointsModels[NUM_MODELS] = { 0 }; + Texture2D meshTextures[NUM_MODELS] = { 0 }; + int textureConfig[NUM_MODELS] = { 4, 4, 16, 16, 32 }; + + for (int i = 0; i < NUM_MODELS; i++) + { + if (i == 0) worldModels[0] = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); + if (i == 1) worldModels[1] = LoadModel("resources/models/unit_cube.obj"); + if (i == 2) worldModels[2] = LoadModelFromMesh(GenMeshSphere(0.5f, 8, 8)); + if (i == 3) worldModels[3] = LoadModel("resources/models/unit_sphere.obj"); + if (i == 4) worldModels[4] = LoadModelFromMesh(GenMeshKnot(1.0f, 1.0f, 16, 128)); + + Mesh *worldMesh = &worldModels[i].meshes[0]; + if (!worldMesh->indices) + { + worldMesh->indices = RL_CALLOC(worldMesh->vertexCount, sizeof(unsigned short)); + for (int j = 0; j < worldMesh->vertexCount; j++) worldMesh->indices[j] = (unsigned short)j; + worldMesh->triangleCount = worldMesh->vertexCount/3; + } + FillPlanarTexCoords(worldMesh); + FillVertexColors(worldMesh); + + Image textureImage = GenImageChecked(textureConfig[i], textureConfig[i], 1, 1, BLACK, WHITE); + meshTextures[i] = LoadTextureFromImage(textureImage); + UnloadImage(textureImage); + worldModels[i].materials[0].maps[MATERIAL_MAP_ALBEDO].texture = meshTextures[i]; + + Mesh ndcMesh = (Mesh){ 0 }; + ndcMesh.vertexCount = worldMesh->vertexCount; + ndcMesh.triangleCount = worldMesh->triangleCount; + ndcMesh.vertices = RL_CALLOC(ndcMesh.vertexCount, sizeof(Vector3)); + ndcMesh.indices = RL_CALLOC(ndcMesh.triangleCount, sizeof(Triangle)); + memcpy(ndcMesh.indices, worldMesh->indices, ndcMesh.triangleCount*sizeof(Triangle)); + //NOTE: test things by toggling through the LOADED MESHES VS GEN MESHES when removing planar texcoord fill above + if (worldMesh->texcoords) ndcMesh.texcoords = RL_CALLOC(ndcMesh.vertexCount, sizeof(Vector2)); + if (worldMesh->texcoords) memcpy(ndcMesh.texcoords, worldMesh->texcoords, ndcMesh.vertexCount*sizeof(Vector2)); + if (worldMesh->colors) ndcMesh.colors = RL_CALLOC(ndcMesh.vertexCount, sizeof(Color)); + if (worldMesh->colors) memcpy(ndcMesh.colors, worldMesh->colors, ndcMesh.vertexCount*sizeof(Color)); + UploadMesh(&ndcMesh, true); //this allows for UpdateMeshBuffer later on, but its rought just to work around genmesh upload being static + ndcModels[i] = LoadModelFromMesh(ndcMesh); + ndcModels[i].materials[0].maps[MATERIAL_MAP_ALBEDO].texture = meshTextures[i]; + +#if defined(GRAPHICS_API_OPENGL_33) + worldModels[i].materials[0].shader = customShader; + ndcModels[i].materials[0].shader = customShader; +#endif + Mesh nearPlanePoints = (Mesh){ 0 }; + nearPlanePoints.vertexCount = worldMesh->triangleCount*3; + nearPlanePoints.vertices = RL_CALLOC(nearPlanePoints.vertexCount, sizeof(Vector3)); + UploadMesh(&nearPlanePoints, true); + nearPlanePointsModels[i] = LoadModelFromMesh(nearPlanePoints); + } + + Mesh tempCube = GenMeshCube(1.0f, 1.0f, 1.0f); + Mesh spatialFrame = { 0 }; + spatialFrame.vertexCount = tempCube.vertexCount; + spatialFrame.triangleCount = tempCube.triangleCount; + spatialFrame.vertices = RL_MALLOC(spatialFrame.vertexCount * 3 * sizeof(float)); + spatialFrame.normals = RL_MALLOC(spatialFrame.vertexCount * 3 * sizeof(float)); + spatialFrame.texcoords = RL_MALLOC(spatialFrame.vertexCount * 2 * sizeof(float)); + spatialFrame.indices = RL_MALLOC(spatialFrame.triangleCount * 3 * sizeof(unsigned short)); + spatialFrame.colors = RL_CALLOC(spatialFrame.vertexCount, sizeof(Color)); + memcpy(spatialFrame.vertices, tempCube.vertices, spatialFrame.vertexCount * 3 * sizeof(float)); + memcpy(spatialFrame.normals, tempCube.normals, spatialFrame.vertexCount * 3 * sizeof(float)); + memcpy(spatialFrame.texcoords, tempCube.texcoords, spatialFrame.vertexCount * 2 * sizeof(float)); + memcpy(spatialFrame.indices, tempCube.indices, spatialFrame.triangleCount * 3 * sizeof(unsigned short)); + for (int i = 0; i < spatialFrame.vertexCount; i++) ((Color *)spatialFrame.colors)[i] = (Color){ 255, 255, 255, 0 }; + for (int i = 0; i < 4; i++) ((Color *)spatialFrame.colors)[i].a = 255; + UnloadMesh(tempCube); //NOTE: to clean up static mesh -- better would be to allow for gen mesh to have option for dynamic or static + UploadMesh(&spatialFrame, true); + Model spatialFrameModel = LoadModelFromMesh(spatialFrame); +#if defined(GRAPHICS_API_OPENGL_33) + spatialFrameModel.materials[0].shader = customShader; +#endif + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + aspect = (float)GetScreenWidth()/(float)GetScreenHeight(); + TOGGLE(KEY_N, FLAG_NDC); + if (NDC_SPACE()) TOGGLE(KEY_F, FLAG_REFLECT_Y); + TOGGLE(KEY_Q, FLAG_ASPECT); + TOGGLE(KEY_P, FLAG_PERSPECTIVE_CORRECT); + TOGGLE(KEY_SPACE, FLAG_PAUSE); + TOGGLE(KEY_C, FLAG_COLOR_MODE); + TOGGLE(KEY_T, FLAG_TEXTURE_MODE); + TOGGLE(KEY_J, FLAG_JUGEMU); + TOGGLE(KEY_O, FLAG_ORTHO); + TOGGLE(KEY_X, FLAG_CLIP); + CYCLE_MESH(KEY_ONE, 0, GEN_CUBE); + CYCLE_MESH(KEY_TWO, 1, LOAD_CUBE); + CYCLE_MESH(KEY_THREE, 2, GEN_SPHERE); + CYCLE_MESH(KEY_FOUR, 3, LOAD_SPHERE); + CYCLE_MESH(KEY_FIVE, 4, GEN_KNOT); + + float sBlend = SpaceBlendFactor(GetFrameTime()); + AspectBlendFactor(GetFrameTime()); + ReflectBlendFactor(GetFrameTime()); + OrthoBlendFactor(GetFrameTime()); + + if (!PAUSED()) meshRotation -= angularVelocity*GetFrameTime(); + + OrbitSpace(&jugemu, GetFrameTime()); + main.projection = (ORTHO_MODE())? CAMERA_ORTHOGRAPHIC : CAMERA_PERSPECTIVE; + main.fovy = (ORTHO_MODE())? nearPlaneHeightOrthographic : fovyPerspective; + WorldToNDCSpace(&main, aspect, near, far, &worldModels[targetMesh], &ndcModels[targetMesh], meshRotation); + + for (int i = 0; i < ndcModels[targetMesh].meshes[0].vertexCount; i++) + { + Vector3 *worldVertices = (Vector3 *)worldModels[targetMesh].meshes[0].vertices; + Vector3 *ndcVertices = (Vector3 *)ndcModels[targetMesh].meshes[0].vertices; + ndcVertices[i].x = Lerp(worldVertices[i].x, ndcVertices[i].x, sBlend); + ndcVertices[i].y = Lerp(worldVertices[i].y, ndcVertices[i].y, sBlend); + ndcVertices[i].z = Lerp(worldVertices[i].z, ndcVertices[i].z, sBlend); + } + + UpdateMeshBuffer(ndcModels[targetMesh].meshes[0], RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, ndcModels[targetMesh].meshes[0].vertices, ndcModels[targetMesh].meshes[0].vertexCount*sizeof(Vector3), 0); + Model *displayModel = &ndcModels[targetMesh]; + + if (PERSPECTIVE_CORRECT() && TEXTURE_MODE()) + { + #if defined(GRAPHICS_API_OPENGL_33) + PerspectiveCorrectCapture(&main, displayModel, meshTextures[targetMesh], &perspectiveCorrectRenderTexture, meshRotation); + #else + PerspectiveCorrectCapture(&main, displayModel, meshTextures[targetMesh], &perspectiveCorrectTexture, meshRotation); + #endif + } + + UpdateSpatialFrame(&main, aspect, near, far, &spatialFrame); + UpdateMeshBuffer(spatialFrameModel.meshes[0], RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, spatialFrame.vertices, spatialFrame.vertexCount*sizeof(Vector3), 0); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(BLACK); + if (JUGEMU_MODE()) BeginMode3D(jugemu); else BeginMode3D(main); + Vector3 depth, right, up; + BasisVector(&main, &depth, &right, &up); + + DrawLine3D(main.position, Vector3Add(main.position, right), NEON_CARROT); + DrawLine3D(main.position, Vector3Add(main.position, up), LILAC); + DrawLine3D(main.position, Vector3Add(main.position, depth), MARINER); + + if (JUGEMU_MODE()) DrawSpatialFrame(&spatialFrame); + + DrawModelFilled(displayModel, meshTextures[targetMesh], meshRotation); + DrawModelWiresAndPoints(displayModel, meshRotation); + + if (JUGEMU_MODE()) DrawNearPlanePoints(&main, aspect, near, &nearPlanePointsModels[targetMesh], displayModel, meshRotation); + + if (PERSPECTIVE_CORRECT() && TEXTURE_MODE()) + { + #if defined(GRAPHICS_API_OPENGL_33) + spatialFrameModel.materials[0].maps[MATERIAL_MAP_ALBEDO].texture = perspectiveCorrectRenderTexture.texture; + int useColors = 1; + SetShaderValue(spatialFrameModel.materials[0].shader, useVertexColorsLoc, &useColors, SHADER_UNIFORM_INT); + int flipTexcoordYValue = (NDC_SPACE() && REFLECT_Y())? 1 : 0; + SetShaderValue(spatialFrameModel.materials[0].shader, flipTexcoordYLoc, &flipTexcoordYValue, SHADER_UNIFORM_INT); + + if (JUGEMU_MODE()) DrawModel(spatialFrameModel, modelPos, 1.0f, WHITE); + + flipTexcoordYValue = 0; + SetShaderValue(spatialFrameModel.materials[0].shader, flipTexcoordYLoc, &flipTexcoordYValue, SHADER_UNIFORM_INT); + #else + spatialFrameModel.materials[0].maps[MATERIAL_MAP_ALBEDO].texture = perspectiveCorrectTexture; + if (JUGEMU_MODE()) DrawModel(spatialFrameModel, modelPos, 1.0f, WHITE); + #endif + } + else + { + if (JUGEMU_MODE()) PerspectiveIncorrectCapture(&main, aspect, near, displayModel, meshTextures[targetMesh], meshRotation); + } + EndMode3D(); + + DrawText("[1-2]: CUBE [3-4]: SPHERE [5]: KNOT", 12, 12, fontSize, NEON_CARROT); + DrawText("ARROWS: MOVE | SPACEBAR: PAUSE", 12, 38, fontSize, NEON_CARROT); + DrawText("W A : ZOOM ", 12, 64, fontSize, NEON_CARROT); + DrawText("CLIP [ X ]:", 12, 94, fontSize, SUNFLOWER); + DrawText((CLIP_MODE())? "ON" : "OFF", 120, 94, fontSize, (CLIP_MODE())? BAHAMA_BLUE : ANAKIWA); + DrawText((targetMesh == 0)? "GEN_CUBE" : (targetMesh == 1)? "LOAD_CUBE" : (targetMesh == 2)? "GEN_SPHERE" : (targetMesh == 3)? "LOAD_SPHERE" : "GEN_KNOT", 12, 205, fontSize, NEON_CARROT); + DrawText("TEXTURE [ T ]:", 570, 12, fontSize, SUNFLOWER); + DrawText((TEXTURE_MODE())? "ON" : "OFF", 740, 12, fontSize, (TEXTURE_MODE())? ANAKIWA : CHESTNUT_ROSE); + DrawText("COLORS [ C ]:", 570, 38, fontSize, SUNFLOWER); + DrawText((COLOR_MODE())? "ON" : "OFF", 740, 38, fontSize, (COLOR_MODE())? ANAKIWA : CHESTNUT_ROSE); + DrawText("ASPECT [ Q ]:", 12, 392, fontSize, SUNFLOWER); + DrawText((ASPECT_CORRECT())? "CORRECT" : "INCORRECT", 230, 392, fontSize, (ASPECT_CORRECT())? ANAKIWA : CHESTNUT_ROSE); + DrawText("PERSPECTIVE [ P ]:", 12, 418, fontSize, SUNFLOWER); + DrawText((PERSPECTIVE_CORRECT())? "CORRECT" : "INCORRECT", 230, 418, fontSize, (PERSPECTIVE_CORRECT())? ANAKIWA : CHESTNUT_ROSE); + DrawText("LENS [ O ]:", 510, 366, fontSize, SUNFLOWER); + DrawText((ORTHO_MODE())? "ORTHOGRAPHIC" : "PERSPECTIVE", 630, 366, fontSize, (ORTHO_MODE())? BAHAMA_BLUE : ANAKIWA); + DrawText("SPACE [ N ]:", 520, 392, fontSize, SUNFLOWER); + DrawText((NDC_SPACE())? "NDC" : "WORLD", 655, 392, fontSize, (NDC_SPACE())? BAHAMA_BLUE : ANAKIWA); + if (NDC_SPACE()) + { + DrawText("REFLECT [ F ]:", 530, 418, fontSize, SUNFLOWER); + DrawText((REFLECT_Y())? "Y_DOWN" : "Y_UP", 695, 418, fontSize, (REFLECT_Y())? ANAKIWA : CHESTNUT_ROSE); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + for (int i = 0; i < NUM_MODELS; i++) + { + UnloadModel(worldModels[i]); + UnloadModel(ndcModels[i]); + UnloadModel(nearPlanePointsModels[i]); + if (meshTextures[i].id) UnloadTexture(meshTextures[i]); + } + UnloadModel(spatialFrameModel); +#if defined(GRAPHICS_API_OPENGL_33) + if (perspectiveCorrectRenderTexture.id) UnloadRenderTexture(perspectiveCorrectRenderTexture); + UnloadShader(customShader); +#endif + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +static void BasisVector(Camera3D *main, Vector3 *depthOut, Vector3 *rightOut, Vector3 *upOut) +{ + Vector3 depth = Vector3Normalize(Vector3Subtract(main->target, main->position)); + Vector3 right = Vector3Normalize(Vector3CrossProduct(depth, main->up)); + Vector3 up = Vector3Normalize(Vector3CrossProduct(right, depth)); + *depthOut = depth; + *rightOut = right; + *upOut = up; +} + +static void WorldToNDCSpace(Camera3D *main, float aspect, float near, float far, Model *world, Model *ndc, float rotation) +{ + Vector3 depth, right, up; + BasisVector(main, &depth, &right, &up); + float halfHNear = Lerp(near*tanf(DEG2RAD*fovyPerspective*0.5f), 0.5f*nearPlaneHeightOrthographic, OrthoBlendFactor(0.0f)); + float halfWNear = Lerp(halfHNear, halfHNear*aspect, AspectBlendFactor(0.0f)); + float halfDepthNDC = Lerp(halfHNear, 0.5f*(far - near), Lerp(AspectBlendFactor(0.0f), 0.0f, OrthoBlendFactor(0.0f))); + Vector3 centerNearPlane = Vector3Add(main->position, Vector3Scale(depth, near)); + Vector3 centerNDCCube = Vector3Add(centerNearPlane, Vector3Scale(depth, halfDepthNDC)); + + for (int i = 0; i < world->meshes[0].vertexCount; i++) + { + Vector3 worldVertex = TranslateRotateScale(0, ((Vector3 *)world->meshes[0].vertices)[i], modelPos, modelScale, rotation); + float signedDepth = Vector3DotProduct(Vector3Subtract(worldVertex, main->position), depth); + Vector3 intersectionCoord = Intersect(main, near, worldVertex); + Vector3 clipPlaneVector = Vector3Subtract(intersectionCoord, centerNearPlane); + float xNDC = Vector3DotProduct(clipPlaneVector, right)/halfWNear; + float yNDC = Vector3DotProduct(clipPlaneVector, up)/halfHNear; + float zNDC = Lerp((far + near - 2.0f*far*near/signedDepth)/(far - near), 2.0f*(signedDepth - near)/(far - near) - 1.0f, OrthoBlendFactor(0.0f)); + Vector3 scaledRight = Vector3Scale(right, xNDC*halfWNear); + Vector3 scaledUp = Vector3Scale(up, yNDC*halfHNear); + Vector3 scaledDepth = Vector3Scale(depth, zNDC*halfDepthNDC); + Vector3 offset = Vector3Add(Vector3Add(scaledRight, scaledUp), scaledDepth); + Vector3 scaledNDCCoord = Vector3Add(centerNDCCube, offset); + ((Vector3 *)ndc->meshes[0].vertices)[i] = TranslateRotateScale(1, scaledNDCCoord, modelPos, modelScale, rotation); + } +} + +static void DrawModelFilled(Model *model, Texture2D texture, float rotation) +{ + if (!(COLOR_MODE() || TEXTURE_MODE())) return; +#if defined(GRAPHICS_API_OPENGL_33) + int useColors = COLOR_MODE() ? 1 : 0; + SetShaderValue(model->materials[0].shader, useVertexColorsLoc, &useColors, SHADER_UNIFORM_INT); +#else + Color *cacheColors = (Color *)model->meshes[0].colors; + if (TEXTURE_MODE() && !COLOR_MODE()) model->meshes[0].colors = NULL; +#endif + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture.id = (TEXTURE_MODE())? texture.id : rlGetTextureIdDefault(); + DrawModelEx(*model, modelPos, yAxis, RAD2DEG*rotation, modelScale, WHITE); + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture.id = rlGetTextureIdDefault(); + +#if defined(GRAPHICS_API_OPENGL_11) + model->meshes[0].colors = (unsigned char *)cacheColors; +#endif +} + +static void DrawModelWiresAndPoints(Model *model, float rotation) +{ +#if defined(GRAPHICS_API_OPENGL_33) + int useColors = (CLIP_MODE())? 1 : 0; + SetShaderValue(model->materials[0].shader, useVertexColorsLoc, &useColors, SHADER_UNIFORM_INT); +#else + Color *cacheColors = (Color *)model->meshes[0].colors; + if (!CLIP_MODE()) model->meshes[0].colors = NULL; +#endif + unsigned int cacheID = model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture.id; + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture.id = rlGetTextureIdDefault(); + + DrawModelWiresEx(*model, modelPos, yAxis, RAD2DEG*rotation, modelScale, MARINER); + rlSetPointSize(4.0f); + DrawModelPointsEx(*model, modelPos, yAxis, RAD2DEG*rotation, modelScale, LILAC); + + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture.id = cacheID; +#if defined(GRAPHICS_API_OPENGL_11) + model->meshes[0].colors = (unsigned char *)cacheColors; +#endif +} + +static void UpdateSpatialFrame(Camera3D *main, float aspect, float near, float far, Mesh *spatialFrame) +{ + Vector3 depth, right, up; + BasisVector(main, &depth, &right, &up); + float halfHNear = Lerp(near*tanf(DEG2RAD*fovyPerspective*0.5f), 0.5f*nearPlaneHeightOrthographic, OrthoBlendFactor(0.0f)); + float halfWNear = Lerp(halfHNear, halfHNear*aspect, AspectBlendFactor(0.0f)); + float halfHFar = Lerp(far*tanf(DEG2RAD*fovyPerspective*0.5f), 0.5f*nearPlaneHeightOrthographic, OrthoBlendFactor(0.0f)); + float halfWFar = Lerp(halfHFar, halfHFar*aspect, AspectBlendFactor(0.0f)); + float halfDepthNdc = Lerp(halfHNear, 0.5f*(far - near), Lerp(AspectBlendFactor(0.0f), 0.0f, OrthoBlendFactor(0.0f))); + float halfDepth = Lerp(0.5f*(far - near), halfDepthNdc, SpaceBlendFactor(0.0f)); + float farHalfW = Lerp(halfWFar, halfWNear, SpaceBlendFactor(0.0f)); + float farHalfH = Lerp(halfHFar, halfHNear, SpaceBlendFactor(0.0f)); + Vector3 centerNear = Vector3Add(main->position, Vector3Scale(depth, near)); + + for (int i = 0; i < spatialFrame->vertexCount; ++i) + { + Vector3 offset = Vector3Subtract(((Vector3 *)spatialFrame->vertices)[i], centerNear); + float xSign = (Vector3DotProduct(offset, right) >= 0.0f)? 1.0f : -1.0f; + float ySign = (Vector3DotProduct(offset, up) >= 0.0f)? 1.0f : -1.0f; + float farMask = (Vector3DotProduct(offset, depth) > halfDepth)? 1.0f : 0.0f; + float finalHalfW = halfWNear + farMask*(farHalfW - halfWNear); + float finalHalfH = halfHNear + farMask*(farHalfH - halfHNear); + Vector3 center = Vector3Add(centerNear, Vector3Scale(depth, farMask*2.0f*halfDepth)); + ((Vector3 *)spatialFrame->vertices)[i] = Vector3Add(center, Vector3Add(Vector3Scale(right, xSign*finalHalfW), Vector3Scale(up, ySign*finalHalfH))); + } +} + +static void DrawSpatialFrame(Mesh *spatialFrame) +{ + static int frontFaces[4][2] = { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 } }; + static int backFaces[4][2] = { { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 } }; + static int ribFaces[4][2] = { { 0, 4 }, { 1, 7 }, { 2, 6 }, { 3, 5 } }; + static int (*faces[3])[2] = { frontFaces, backFaces, ribFaces }; + + for (int i = 0; i < 3; i++) + for (int j = 0; j < 4; j++) + { + Vector3 startPosition = ((Vector3 *)spatialFrame->vertices)[faces[i][j][0]]; + Vector3 endPosition = ((Vector3 *)spatialFrame->vertices)[faces[i][j][1]]; + DrawLine3D(startPosition, endPosition, (i == 0)? NEON_CARROT : (i == 1)? EGGPLANT : HOPBUSH); + } +} + +static void DrawNearPlanePoints(Camera3D *main, float aspect, float near, Model *nearPlanePointsModel, Model *displayModel, float rotation) +{ + Vector3 depth, right, up; + BasisVector(main, &depth, &right, &up); + Mesh *displayMesh = &displayModel->meshes[0]; + int nearPlaneVertexCount = 0; + int capacity = displayMesh->triangleCount*3; + Mesh *nearPlanePointsMesh = &nearPlanePointsModel->meshes[0]; + Vector3 centerNearPlane = Vector3Add(main->position, Vector3Scale(depth, near)); + float xAspect = Lerp(1.0f/aspect, 1.0f, AspectBlendFactor(0.0f)); + float yReflect = Lerp(1.0f, -1.0f, ReflectBlendFactor(0.0f)); + + for (int i = 0; i < displayMesh->triangleCount; i++) + { + Vector3 *vertices = (Vector3 *)displayMesh->vertices; + Triangle *triangles = (Triangle *)displayMesh->indices; + + Vector3 a = TranslateRotateScale(0, vertices[triangles[i][0]], modelPos, modelScale, rotation); + Vector3 b = TranslateRotateScale(0, vertices[triangles[i][1]], modelPos, modelScale, rotation); + Vector3 c = TranslateRotateScale(0, vertices[triangles[i][2]], modelPos, modelScale, rotation); + + // test if front facing or not (ugly one-liner -- comment out will ~double the rays, which is fine) + if (Vector3DotProduct(Vector3Normalize(Vector3CrossProduct(Vector3Subtract(b, a), Vector3Subtract(c, a))), depth) > 0.0f) continue; + Vector3 intersectionPoints[3] = { Intersect(main, near, a), Intersect(main, near, b), Intersect(main, near, c) }; + + for (int j = 0; j < 3 && nearPlaneVertexCount < capacity; ++j) + { + Vector3 corrected = AspectCorrectAndReflectNearPlane(intersectionPoints[j], centerNearPlane, right, up, xAspect, yReflect); + DrawLine3D((Vector3[]){ a, b, c }[j], corrected, (Color){ RED_DAMASK.r, RED_DAMASK.g, RED_DAMASK.b, 20 }); + ((Vector3 *)nearPlanePointsMesh->vertices)[nearPlaneVertexCount] = corrected; + nearPlaneVertexCount++; + } + } + + nearPlanePointsMesh->vertexCount = nearPlaneVertexCount; + UpdateMeshBuffer(*nearPlanePointsMesh, RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, nearPlanePointsMesh->vertices, nearPlanePointsMesh->vertexCount*sizeof(Vector3), 0); + rlSetPointSize(3.0f); + DrawModelPoints(*nearPlanePointsModel, modelPos, 1.0f, LILAC); +} + +static void PerspectiveIncorrectCapture(Camera3D *main, float aspect, float near, Model *displayModel, Texture2D meshTexture, float rotation) +{ + Vector3 depth, right, up; + BasisVector(main, &depth, &right, &up); + Mesh *displayMesh = &displayModel->meshes[0]; + Vector3 centerNearPlane = Vector3Add(main->position, Vector3Scale(depth, near)); + float xAspect = Lerp(1.0f/aspect, 1.0f, AspectBlendFactor(0.0f)); + float yReflect = Lerp(1.0f, -1.0f, ReflectBlendFactor(0.0f)); + rlColor4ub(WHITE.r, WHITE.g, WHITE.b, WHITE.a); // just to emphasize raylib Colors are ub 0~255 not floats + if (TEXTURE_MODE() && displayMesh->texcoords) + { + rlSetTexture(meshTexture.id); + rlEnableTexture(meshTexture.id); + } + else + { + rlDisableTexture(); + } + if (!TEXTURE_MODE() && !COLOR_MODE()) + { + rlEnableWireMode(); + rlColor4ub(MARINER.r, MARINER.g, MARINER.b, MARINER.a); + } + rlBegin(RL_TRIANGLES); + + for (int i = 0; i < displayMesh->triangleCount; i++) + { + Triangle *triangles = (Triangle *)displayMesh->indices; + Vector3 *vertices = (Vector3 *)displayMesh->vertices; + Color *colors = (Color *)displayMesh->colors; + Vector2 *texcoords = (Vector2 *)displayMesh->texcoords; + + Vector3 a = TranslateRotateScale(0, vertices[triangles[i][0]], modelPos, modelScale, rotation); + Vector3 b = TranslateRotateScale(0, vertices[triangles[i][1]], modelPos, modelScale, rotation); + Vector3 c = TranslateRotateScale(0, vertices[triangles[i][2]], modelPos, modelScale, rotation); + + a = AspectCorrectAndReflectNearPlane(Intersect(main, near, a), centerNearPlane, right, up, xAspect, yReflect); + b = AspectCorrectAndReflectNearPlane(Intersect(main, near, b), centerNearPlane, right, up, xAspect, yReflect); + c = AspectCorrectAndReflectNearPlane(Intersect(main, near, c), centerNearPlane, right, up, xAspect, yReflect); + + if (COLOR_MODE() && displayMesh->colors) rlColor4ub(colors[triangles[i][0]].r, colors[triangles[i][0]].g, colors[triangles[i][0]].b, colors[triangles[i][0]].a); + if (TEXTURE_MODE() && displayMesh->texcoords) rlTexCoord2f(texcoords[triangles[i][0]].x, texcoords[triangles[i][0]].y); + rlVertex3f(a.x, a.y, a.z); + // vertex winding!! to account for reflection toggle (will draw the inside of the geometry otherwise) + int secondIndex = (NDC_SPACE() && REFLECT_Y())? triangles[i][2] : triangles[i][1]; + Vector3 secondVertex = (NDC_SPACE() && REFLECT_Y())? c : b; + if (COLOR_MODE() && displayMesh->colors) rlColor4ub(colors[secondIndex].r, colors[secondIndex].g, colors[secondIndex].b, colors[secondIndex].a); + if (TEXTURE_MODE() && displayMesh->texcoords) rlTexCoord2f(texcoords[secondIndex].x, texcoords[secondIndex].y); + rlVertex3f(secondVertex.x, secondVertex.y, secondVertex.z); + + int thirdIndex = (NDC_SPACE() && REFLECT_Y())? triangles[i][1] : triangles[i][2]; + Vector3 thirdVertex = (NDC_SPACE() && REFLECT_Y())? b : c; + if (COLOR_MODE() && displayMesh->colors) rlColor4ub(colors[thirdIndex].r, colors[thirdIndex].g, colors[thirdIndex].b, colors[thirdIndex].a); + if (TEXTURE_MODE() && displayMesh->texcoords) rlTexCoord2f(texcoords[thirdIndex].x, texcoords[thirdIndex].y); + rlVertex3f(thirdVertex.x, thirdVertex.y, thirdVertex.z); + } + + rlEnd(); + rlDrawRenderBatchActive(); //NOTE: this is what allows lines in opengl33 + rlSetTexture(rlGetTextureIdDefault()); + rlDisableTexture(); + rlDisableWireMode(); +} + +#if defined(GRAPHICS_API_OPENGL_33) +static void PerspectiveCorrectCapture(Camera3D *main, Model *model, Texture2D meshTexture, RenderTexture2D *perspectiveCorrectRenderTexture, float rotation) +{ + BeginTextureMode(*perspectiveCorrectRenderTexture); + ClearBackground(BLANK); + BeginMode3D(*main); + int useColors = (COLOR_MODE())? 1 : 0; + SetShaderValue(model->materials[0].shader, useVertexColorsLoc, &useColors, SHADER_UNIFORM_INT); + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture = meshTexture; + DrawModelEx(*model, modelPos, yAxis, RAD2DEG*rotation, modelScale, WHITE); + EndMode3D(); + EndTextureMode(); +} +#endif + +#if defined(GRAPHICS_API_OPENGL_11) +static void PerspectiveCorrectCapture(Camera3D *main, Model *model, Texture2D meshTexture, Texture2D *perspectiveCorrectTexture, float rotation) +{ + unsigned char *cacheColors = model->meshes[0].colors; + if (TEXTURE_MODE() && !COLOR_MODE()) model->meshes[0].colors = NULL; + + ClearBackground(BLACK); + + BeginMode3D(*main); + Texture2D previousTexture = model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture; + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture = meshTexture; + DrawModelEx(*model, modelPos, yAxis, RAD2DEG*rotation, modelScale, WHITE); + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture = previousTexture; + EndMode3D(); + + Image rgba = LoadImageFromScreen(); + ImageFormat(&rgba, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); + model->meshes[0].colors = cacheColors; + ClearBackground(BLACK); + + BeginMode3D(*main); + Texture2D cacheTexture = model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture; + Color cacheMaterialColor = model->materials[0].maps[MATERIAL_MAP_ALBEDO].color; + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture = (Texture2D){ 0 }; + model->materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE; + DrawModelEx(*model, modelPos, yAxis, RAD2DEG*rotation, modelScale, WHITE); + model->materials[0].maps[MATERIAL_MAP_ALBEDO].texture = cacheTexture; + model->materials[0].maps[MATERIAL_MAP_ALBEDO].color = cacheMaterialColor; + EndMode3D(); + + Image mask = LoadImageFromScreen(); + AlphaMaskPunchOut(&rgba, &mask, 1); + ImageFlipVertical(&rgba); + if ((NDC_SPACE() && REFLECT_Y())) ImageFlipVertical(&rgba); // FLIP AGAIN.. it works visually, but is not clear and feels hacked/ugly + if ((perspectiveCorrectTexture->id != 0)) + UpdateTexture(*perspectiveCorrectTexture, rgba.data); + else + *perspectiveCorrectTexture = LoadTextureFromImage(rgba); + UnloadImage(mask); + UnloadImage(rgba); +} +#endif + +static void OrbitSpace(Camera3D *jugemu, float dt) +{ + float radius = Vector3Length(jugemu->position); + float azimuth = atan2f(jugemu->position.z, jugemu->position.x); + float horizontalRadius = sqrtf(jugemu->position.x*jugemu->position.x + jugemu->position.z*jugemu->position.z); + float elevation = atan2f(jugemu->position.y, horizontalRadius); + if (IsKeyDown(KEY_LEFT)) azimuth += 1.0f*dt; + if (IsKeyDown(KEY_RIGHT)) azimuth -= 1.0f*dt; + if (IsKeyDown(KEY_UP)) elevation += 1.0f*dt; + if (IsKeyDown(KEY_DOWN)) elevation -= 1.0f*dt; + if (IsKeyDown(KEY_W)) radius -= 1.0f*dt; + if (IsKeyDown(KEY_S)) radius += 1.0f*dt; + elevation = Clamp(elevation, -PI/2 + 0.1f, PI/2 - 0.1f); + jugemu->position.x = Clamp(radius, 0.25f, 10.0f)*cosf(elevation)*cosf(azimuth); + jugemu->position.y = Clamp(radius, 0.25f, 10.0f)*sinf(elevation); + jugemu->position.z = Clamp(radius, 0.25f, 10.0f)*cosf(elevation)*sinf(azimuth); +} + +static void AlphaMaskPunchOut(Image *rgba, Image *mask, unsigned char threshold) +{ + Image maskCopy = ImageCopy(*mask); + ImageFormat(&maskCopy, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE); + ImageFormat(rgba, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); + unsigned char *maskGrayScale = maskCopy.data; + Color *colors = rgba->data; + int pixelCount = rgba->width*rgba->height; + for (size_t i = 0; i < pixelCount; ++i) colors[i].a = (maskGrayScale[i] > threshold)? 255 : 0; + UnloadImage(maskCopy); +} + +static void FillPlanarTexCoords(Mesh *mesh) +{ + //NOTE: opengl33, please just always provide texcoords for the obj, opengl11 allows null because its easy and works with ps2 isolation tests, + // but otherwise they are always assumed to exist + if (!mesh->texcoords) + { + mesh->texcoords = RL_CALLOC(mesh->vertexCount, sizeof(Vector2)); + // Demonstrate planar mapping ("reasonable" default): https://en.wikipedia.org/wiki/Planar_projection. + BoundingBox bounds = GetMeshBoundingBox(*mesh); + Vector3 extents = Vector3Subtract(bounds.max, bounds.min); + for (int j = 0; j < mesh->vertexCount; j++) + { + float x = ((Vector3 *)mesh->vertices)[j].x; + float y = ((Vector3 *)mesh->vertices)[j].y; + ((Vector2 *)mesh->texcoords)[j].x = (x - bounds.min.x)/extents.x; + ((Vector2 *)mesh->texcoords)[j].y = (y - bounds.min.y)/extents.y; + } + } +} + +static void FillVertexColors(Mesh *mesh) +{ + if (!mesh->colors) mesh->colors = RL_CALLOC(mesh->vertexCount, sizeof(Color)); + Color *colors = (Color *)mesh->colors; + Vector3 *vertices = (Vector3 *)mesh->vertices; + BoundingBox bounds = GetMeshBoundingBox(*mesh); + + for (int i = 0; i < mesh->vertexCount; ++i) + { + Vector3 vertex = vertices[i]; + float nx = (vertex.x - 0.5f*(bounds.min.x + bounds.max.x))/(0.5f*(bounds.max.x - bounds.min.x)); + float ny = (vertex.y - 0.5f*(bounds.min.y + bounds.max.y))/(0.5f*(bounds.max.y - bounds.min.y)); + float nz = (vertex.z - 0.5f*(bounds.min.z + bounds.max.z))/(0.5f*(bounds.max.z - bounds.min.z)); + float len = sqrtf(nx*nx + ny*ny + nz*nz); + colors[i] = (Color){ lrintf(127.5f*(nx/len + 1.0f)), lrintf(127.5f*(ny/len + 1.0f)), lrintf(127.5f*(nz/len + 1.0f)), 255 }; + } +} + +static Vector3 AspectCorrectAndReflectNearPlane(Vector3 intersect, Vector3 center, Vector3 right, Vector3 up, float xAspect, float yReflect) +{ + Vector3 centerDistance = Vector3Subtract(intersect, center); + float x = Vector3DotProduct(centerDistance, right); + float y = Vector3DotProduct(centerDistance, up); + return Vector3Add(center, Vector3Add(Vector3Scale(right, x*xAspect), Vector3Scale(up, y*yReflect))); +} + +static Vector3 TranslateRotateScale(int inverse, Vector3 coordinate, Vector3 pos, Vector3 scale, float rotation) +{ + Matrix matrix = MatrixMultiply(MatrixMultiply(MatrixScale(scale.x, scale.y, scale.z), MatrixRotateY(rotation)), MatrixTranslate(pos.x, pos.y, pos.z)); + Matrix result = inverse ? MatrixInvert(matrix) : matrix; + return Vector3Transform(coordinate, result); +} + +static Vector3 Intersect(Camera3D *main, float near, Vector3 worldCoord) +{ + Vector3 viewDir = Vector3Normalize(Vector3Subtract(main->target, main->position)); + Vector3 mainCameraToPoint = Vector3Subtract(worldCoord, main->position); + float depthAlongView = Vector3DotProduct(mainCameraToPoint, viewDir); + Vector3 centerNearPlane = Vector3Add(main->position, Vector3Scale(viewDir, near)); + if (depthAlongView <= 0.0f) return centerNearPlane; + float scaleToNear = near/depthAlongView; + Vector3 resultPerspective = Vector3Add(main->position, Vector3Scale(mainCameraToPoint, scaleToNear)); + Vector3 resultOrtho = Vector3Add(worldCoord, Vector3Scale(viewDir, Vector3DotProduct(Vector3Subtract(centerNearPlane, worldCoord), viewDir))); + Vector3 result = Vector3Lerp(resultPerspective,resultOrtho, OrthoBlendFactor(0.0f)); + return result; +} + +static float SpaceBlendFactor(float dt) +{ + static float blend = 0.0f; + if (dt > 0.0f) blend = Clamp(blend + ((NDC_SPACE())? 1.0f : -1.0f)*blendScalar*dt, 0.0f, 1.0f); + return blend; +} + +static float AspectBlendFactor(float dt) +{ + static float blend = 0.0f; + if (dt > 0.0f) blend = Clamp(blend + ((ASPECT_CORRECT())? 1.0f : -1.0f)*blendScalar*dt, 0.0f, 1.0f); + return blend; +} + +static float ReflectBlendFactor(float dt) +{ + static float blend = 0.0f; + if (dt > 0.0f) + { + float target = (NDC_SPACE() && REFLECT_Y())? 1.0f : 0.0f; + float direction = (blend < target)? 1.0f : (blend > target)? -1.0f : 0.0f; + blend = Clamp(blend + direction*blendScalar*dt, 0.0f, 1.0f); + } + return blend; +} + +static float OrthoBlendFactor(float dt) +{ + static float blend = 0.0f; + if (dt > 0.0f) blend = Clamp(blend + ((ORTHO_MODE())? 1.0f : -1.0f)*blendScalar*dt, 0.0f, 1.0f); + return blend; +} \ No newline at end of file