3 Commits

Author SHA1 Message Date
e1efefb7f4 [cmake] Propagate Cocoa backend frameworks to consumers of static raylib on macOS (#6086)
* [cmake] Fix standalone configuration of examples project

Configuring examples/ as a standalone project (cd examples && cmake .)
failed with 'Unknown CMake command add_if_flag_compiles' and a missing
cmake_minimum_required() error on CMake 4.x.

- Add cmake_minimum_required(VERSION 3.22), matching the root project
- Include raylib's AddIfFlagCompiles helper via ../cmake
- Default PLATFORM to Desktop when not inherited from the raylib build

* [cmake] Propagate Cocoa backend frameworks to consumers of static raylib on macOS

Bundled GLFW links '-framework Cocoa/IOKit/QuartzCore' privately, and the
static-install export block removes glfw from the exported dependencies.
As a result the installed CMake package only carried OpenGL.framework,
and linking any consumer of libraylib.a failed with undefined Objective-C
symbols (_objc_retain etc.).

Append the three frameworks to the static install interface when the
bundled GLFW is used, so installed consumers get a complete link line.

* Gate macOS Cocoa framework propagation to the Desktop platform
2026-08-24 16:19:56 +02:00
2c0021ad89 [cmake] Fix standalone configuration of examples project (#6079)
Configuring examples/ as a standalone project (cd examples && cmake .)
failed with 'Unknown CMake command add_if_flag_compiles' and a missing
cmake_minimum_required() error on CMake 4.x.

- Add cmake_minimum_required(VERSION 3.22), matching the root project
- Include raylib's AddIfFlagCompiles helper via ../cmake
- Default PLATFORM to Desktop when not inherited from the raylib build
2026-08-24 13:34:47 +02:00
9bde204f1d [examples] Add textures_portal_window (#6082)
Add a example showing a portal like effect
2026-08-24 13:33:43 +02:00
4 changed files with 250 additions and 0 deletions

View File

@ -1,6 +1,18 @@
# Setup the project and settings
cmake_minimum_required(VERSION 3.22)
project(examples)
# Include raylib's cmake helper functions, so the examples can also be
# configured standalone (`cd examples && cmake .`) and not only as part
# of the raylib project.
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake")
include(AddIfFlagCompiles)
# Default to the Desktop platform when configured standalone.
if (NOT DEFINED PLATFORM)
set(PLATFORM "Desktop" CACHE STRING "Platform to build examples for")
endif ()
# Directories that contain examples
set(example_dirs
audio

View File

@ -0,0 +1,224 @@
/*******************************************************************************************
*
* raylib [textures] example - portal window
*
* Example demonstrates rendering a second scene to a texture and projecting it onto a
* quad to create the illusion of a portal window looking into another place. The second
* scene is rendered with an off-axis ("oblique frustum") projection matched to the
* viewer's actual position relative to the window, so the illusion holds up correctly
* as the player moves and looks around, rather than only looking right from one spot
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by PanicTitan (@PanicTitan) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 PanicTitan (@PanicTitan)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "rlgl.h"
#include <math.h>
//------------------------------------------------------------------------------------
// Module Functions Declaration
//------------------------------------------------------------------------------------
static void BeginPortalMode3D(Vector3 eye, Vector3 bottomLeft, Vector3 bottomRight, Vector3 topLeft, float nearPlane, float farPlane);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [textures] example - portal window");
// Camera to navigate the "real world" (Dimension A)
Camera3D camera = { 0 };
camera.position = (Vector3){ 0.0f, 2.5f, 7.0f };
camera.target = (Vector3){ 0.0f, 1.8f, 0.0f };
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
// The archway opening, in Dimension A world space (used both to draw the frame and
// as the window rectangle the oblique projection is built from)
Vector3 archBottomLeft = { -1.5f, 0.0f, 0.0f };
Vector3 archBottomRight = { 1.5f, 0.0f, 0.0f };
Vector3 archTopLeft = { -1.5f, 4.0f, 0.0f };
// The archway sits at portalA and looks out onto portalB, far away in world space.
// Every frame, Dimension B gets rendered to a texture using the same relative eye
// and window position, shifted by the offset between the two portals
Vector3 portalA = { 0.0f, 0.0f, 0.0f };
Vector3 portalB = { 0.0f, 0.0f, -60.0f };
RenderTexture2D portalView = LoadRenderTexture(480, 640);
DisableCursor(); // Lock cursor for first-person free camera controls
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_FREE);
float time = (float)GetTime();
// Eye and window corners, shifted into Dimension B so they match the player's
// actual position and viewing angle relative to the archway
Vector3 offset = Vector3Subtract(portalB, portalA);
Vector3 eyeInB = Vector3Add(camera.position, offset);
Vector3 blInB = Vector3Add(archBottomLeft, offset);
Vector3 brInB = Vector3Add(archBottomRight, offset);
Vector3 tlInB = Vector3Add(archTopLeft, offset);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Render Dimension B into an offscreen texture, using an oblique projection so
// its perspective lines up with the archway exactly as the real camera sees it
BeginTextureMode(portalView);
ClearBackground((Color){ 10, 5, 20, 255 });
BeginPortalMode3D(eyeInB, blInB, brInB, tlInB, 0.05f, 100.0f);
DrawGrid(30, 0.8f);
// Floating pulsing core sphere
Vector3 corePos = Vector3Add(portalB, (Vector3){ 0.0f, 2.0f + sinf(time * 2.5f) * 0.4f, -4.0f });
DrawSphere(corePos, 1.2f, PURPLE);
DrawSphereWires(corePos, 1.25f, 16, 16, MAGENTA);
// Orbiting cubes
for (int i = 0; i < 4; i++)
{
float angle = time * 1.5f + i * (PI / 2.0f);
Vector3 pos = Vector3Add(portalB, (Vector3){
sinf(angle) * 2.5f,
2.0f + cosf(time * 3.0f + i) * 0.5f,
-4.0f + cosf(angle) * 2.5f });
DrawCube(pos, 0.5f, 0.5f, 0.5f, LIME);
DrawCubeWires(pos, 0.52f, 0.52f, 0.52f, DARKGREEN);
}
EndMode3D();
EndTextureMode();
BeginDrawing();
ClearBackground((Color){ 15, 18, 26, 255 });
BeginMode3D(camera);
DrawGrid(20, 1.0f);
// Side pillars
DrawCube((Vector3){ -3.5f, 2.0f, 0.0f }, 0.8f, 4.0f, 0.8f, DARKGRAY);
DrawCubeWires((Vector3){ -3.5f, 2.0f, 0.0f }, 0.8f, 4.0f, 0.8f, ORANGE);
DrawCube((Vector3){ 3.5f, 2.0f, 0.0f }, 0.8f, 4.0f, 0.8f, DARKGRAY);
DrawCubeWires((Vector3){ 3.5f, 2.0f, 0.0f }, 0.8f, 4.0f, 0.8f, ORANGE);
// Golden archway frame and solid base
DrawCubeWires((Vector3){ 0.0f, 2.0f, 0.0f }, 3.2f, 4.2f, 0.2f, GOLD);
DrawCube((Vector3){ 0.0f, 0.05f, 0.0f }, 3.4f, 0.1f, 0.6f, MAROON);
// Solid backing wall, only ever seen if looking at the archway from behind
DrawCube((Vector3){ 0.0f, 2.0f, -0.05f }, 3.1f, 4.1f, 0.05f, DARKBLUE);
// The portal opening itself: a plain quad textured with the Dimension B
// render, filling the archway exactly, so nothing "leaks" outside its shape
rlSetTexture(portalView.texture.id);
rlBegin(RL_QUADS);
rlColor4ub(255, 255, 255, 255);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(archBottomLeft.x, archBottomLeft.y, archBottomLeft.z);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(archBottomRight.x, archBottomRight.y, archBottomRight.z);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(archBottomRight.x, 4.0f, archBottomRight.z);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(archTopLeft.x, archTopLeft.y, archTopLeft.z);
rlEnd();
rlSetTexture(0);
EndMode3D();
// HUD overlay
DrawRectangle(15, 15, 400, 85, Fade(BLACK, 0.75f));
DrawRectangleLines(15, 15, 400, 85, GOLD);
DrawText("PORTAL WINDOW", 28, 25, 20, GOLD);
DrawText("Look through the golden arch into Dimension B", 28, 52, 14, RAYWHITE);
DrawText("Controls: Mouse to look | WASD to move", 28, 72, 12, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadRenderTexture(portalView);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Starts a 3D mode using an off-axis ("oblique frustum") projection, built directly from
// an eye point and 3 corners of a rectangular window, instead of a fovy centered straight
// ahead of the camera. This is the standard technique for rendering a scene as seen through
// a window that isn't necessarily faced head-on (also used for multi-monitor and VR
// rendering) - the key difference from a normal Camera3D is that the frustum is allowed
// to be asymmetric, so perspective lines through the window line up correctly from any
// eye position, instead of behaving like a flat image pasted onto the window
static void BeginPortalMode3D(Vector3 eye, Vector3 bottomLeft, Vector3 bottomRight, Vector3 topLeft, float nearPlane, float farPlane)
{
Vector3 right = Vector3Normalize(Vector3Subtract(bottomRight, bottomLeft));
Vector3 up = Vector3Normalize(Vector3Subtract(topLeft, bottomLeft));
Vector3 normal = Vector3Normalize(Vector3CrossProduct(right, up));
// Vectors from the eye to 3 corners of the window, used to project the window onto
// the near plane and read off how far it extends left/right/bottom/top of the eye
Vector3 toBL = Vector3Subtract(bottomLeft, eye);
Vector3 toBR = Vector3Subtract(bottomRight, eye);
Vector3 toTL = Vector3Subtract(topLeft, eye);
float dist = -Vector3DotProduct(toBL, normal);
if (dist < 0.01f) dist = 0.01f; // Keep the eye from crossing the window plane
float scale = nearPlane/dist;
rlDrawRenderBatchActive();
rlMatrixMode(RL_PROJECTION);
rlPushMatrix();
rlSetMatrixProjection(MatrixFrustum(
Vector3DotProduct(right, toBL)*scale, Vector3DotProduct(right, toBR)*scale,
Vector3DotProduct(up, toBL)*scale, Vector3DotProduct(up, toTL)*scale,
nearPlane, farPlane));
// View orientation is fixed to the window's own plane (looking straight through it
// along its normal), NOT aimed at any target - that's what the asymmetric frustum
// above is for, and is what lets the eye move off to one side without distorting
rlMatrixMode(RL_MODELVIEW);
rlLoadIdentity();
rlMultMatrixf(MatrixToFloat(MatrixLookAt(eye, Vector3Subtract(eye, normal), up)));
rlEnableDepthTest();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -114,6 +114,20 @@ if (NOT BUILD_SHARED_LIBS)
if (NOT glfw3_FOUND)
list(REMOVE_ITEM raylib_install_private_libs glfw)
# Bundled GLFW links its Cocoa backend frameworks privately, so they
# must be propagated to consumers of the static library, whose final
# link line would otherwise miss them. Only the Desktop platform uses
# bundled GLFW; other platforms (e.g. Memory) have no Cocoa/GLFW symbols.
if (APPLE AND PLATFORM STREQUAL "Desktop")
find_library(COCOA_FRAMEWORK Cocoa)
find_library(IOKIT_FRAMEWORK IOKit)
find_library(QUARTZCORE_FRAMEWORK QuartzCore)
list(APPEND raylib_install_private_libs
${COCOA_FRAMEWORK}
${IOKIT_FRAMEWORK}
${QUARTZCORE_FRAMEWORK})
endif ()
endif()
foreach(lib IN LISTS raylib_install_private_libs)