raylib
raylib copied to clipboard
[rcamera] extend Camera API with some basic Camera Frustum related functions ?
Hi @raysan5 ,
I read here #3136 that frustum culling is up to users because Raylib does not contain scene-graph.
But what about some basic Camera-Frustum functions ? (see code below)
Would it be welcome as a PR into rcamera.h ? (edit : or maybe a rfrustum.h ?)
i think a example would be better.
i think a example would be better.
The problem is that the addition to the API will be relatively large. If published as an example, this would make the example extremely large.
Here is the WIP current version of the API :
// BoundingBoxCornersFlag
// NOTE: bitwise flags to select corners
typedef enum
{
BOX_NO_CORNER = 0,
BOX_FRONT_BOTTOM_LEFT = 1,
BOX_FRONT_BOTTOM_RIGHT = 2,
BOX_FRONT_TOP_LEFT = 4,
BOX_FRONT_TOP_RIGHT = 8,
BOX_BACK_BOTTOM_LEFT = 16,
BOX_BACK_BOTTOM_RIGHT = 32,
BOX_BACK_TOP_LEFT = 64,
BOX_BACK_TOP_RIGHT = 128,
BOX_ALL_CORNERS = 255
} BoundingBoxCornersFlag;
typedef struct Frustum {
Vector4 up;
Vector4 down;
Vector4 left;
Vector4 right;
Vector4 near;
Vector4 far;
} Frustum;
float PlaneDistanceToPoint( Vector4 plane , Vector3 point );
bool CheckCollisionPlanePoint( Vector4 plane , Vector3 point );
bool CheckCollisionPlaneSphere( Vector4 plane , Vector3 center , float radius );
bool CheckCollisionPlaneBox( Vector4 plane , BoundingBox box );
int CheckCollisionPlaneBoxEx( Vector4 plane , BoundingBox box ); // Return a BoundingBoxCornersFlag bitfield
Frustum CameraGetFrustum( Camera *camera , float aspect );
bool FrustumContainsPoint( Frustum frustum , Vector3 point );
bool FrustumContainsSphere( Frustum frustum , Vector3 center , float radius );
bool FrustumContainsBox( Frustum frustum , BoundingBox box );
// Return the frustum of the camera.
// NOTE : The returned frustum is in World Space coordinates.
Frustum CameraGetFrustum( Camera *camera , float aspect )
{
Frustum frustum ;
Matrix view = GetCameraViewMatrix( camera );
Matrix proj = GetCameraProjectionMatrix( camera , aspect );
Matrix clip = MatrixMultiply( view , proj ); // The frustum is calculated in World Space
if (1) // TODO : Perspective mode condition
{
frustum.left = Vector4Normalize( (Vector4){ clip.m3 + clip.m0 , clip.m7 + clip.m4 , clip.m11 + clip.m8 , clip.m15 + clip.m12 } );
frustum.right = Vector4Normalize( (Vector4){ clip.m3 - clip.m0 , clip.m7 - clip.m4 , clip.m11 - clip.m8 , clip.m15 - clip.m12 } );
frustum.down = Vector4Normalize( (Vector4){ clip.m3 + clip.m1 , clip.m7 + clip.m5 , clip.m11 + clip.m9 , clip.m15 + clip.m13 } );
frustum.up = Vector4Normalize( (Vector4){ clip.m3 - clip.m1 , clip.m7 - clip.m5 , clip.m11 - clip.m9 , clip.m15 - clip.m13 } );
frustum.near = Vector4Normalize( (Vector4){ clip.m3 + clip.m2 , clip.m7 + clip.m6 , clip.m11 + clip.m10 , clip.m15 + clip.m14 } );
frustum.far = Vector4Normalize( (Vector4){ clip.m3 - clip.m2 , clip.m7 - clip.m6 , clip.m11 - clip.m10 , clip.m15 - clip.m14 } );
}
else
{
// TODO : Orthogonal mode
}
return frustum;
}
// Return the closest (orthogonal) signed distance between a point and a plane.
// NOTE : A negative distance means the point is under the plane.
float PlaneDistanceToPoint( Vector4 plane , Vector3 point )
{
float d = point.x * plane.x + point.y * plane.y + point.z * plane.z + plane.w ;
float e = sqrt( plane.x * plane.x + plane.y * plane.y + plane.z * plane.z );
float distance = d/e ;
return distance ;
}
// Check if the point is touching or is under the plane.
bool CheckCollisionPlanePoint( Vector4 plane , Vector3 point )
{
return PlaneDistanceToPoint( plane , point ) <= 0.0f ;
}
// Check if the sphere is touching or is under the plane.
bool CheckCollisionPlaneSphere( Vector4 plane , Vector3 center , float radius )
{
return PlaneDistanceToPoint( plane , center ) <= radius ;
}
// Check if the box is touching or is under the plane.
bool CheckCollisionPlaneBox( Vector4 plane , BoundingBox box )
{
/*
F---------G
/| /|
B---------C |
| | | |
| E-------|-H
|/ |/
A---------D
*/
// A and G corners :
if ( CheckCollisionPlanePoint( plane , box.min ) ) return true;
if ( CheckCollisionPlanePoint( plane , box.max ) ) return true;
// B corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.min.x , box.max.y , box.min.z } ) ) return true;
// C corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.max.x , box.max.y , box.min.z } ) ) return true;
// D corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.max.x , box.min.y , box.min.z } ) ) return true;
// E corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.min.x , box.min.y , box.max.z } ) ) return true;
// F corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.min.x , box.max.y , box.max.z } ) ) return true;
// H corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.max.x , box.min.y , box.max.z } ) ) return true;
return false;
}
// Check which box corners are touching or are under the plane.
int CheckCollisionPlaneBoxEx( Vector4 plane , BoundingBox box )
{
/*
F---------G
/| /|
B---------C |
| | | |
| E-------|-H
|/ |/
A---------D
*/
int corners = BOX_NO_CORNER;
// A and G corners :
if ( CheckCollisionPlanePoint( plane , box.min ) ) corners |= BOX_FRONT_BOTTOM_LEFT;
if ( CheckCollisionPlanePoint( plane , box.max ) ) corners |= BOX_BACK_TOP_RIGHT;
// B corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.min.x , box.max.y , box.min.z } ) ) corners |= BOX_FRONT_TOP_LEFT;
// C corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.max.x , box.max.y , box.min.z } ) ) corners |= BOX_FRONT_TOP_RIGHT;
// D corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.max.x , box.min.y , box.min.z } ) ) corners |= BOX_FRONT_BOTTOM_RIGHT;
// E corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.min.x , box.min.y , box.max.z } ) ) corners |= BOX_BACK_BOTTOM_LEFT;
// F corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.min.x , box.max.y , box.max.z } ) ) corners |= BOX_BACK_TOP_LEFT;
// H corner :
if ( CheckCollisionPlanePoint( plane , (Vector3){ box.max.x , box.min.y , box.max.z } ) ) corners |= BOX_BACK_BOTTOM_RIGHT;
return corners;
}
bool FrustumContainsSphere( Frustum frustum , Vector3 center , float radius )
{
if ( PlaneDistanceToPoint( frustum.left , center ) <= -radius ) return false;
if ( PlaneDistanceToPoint( frustum.right, center ) <= -radius ) return false;
if ( PlaneDistanceToPoint( frustum.up , center ) <= -radius ) return false;
if ( PlaneDistanceToPoint( frustum.down , center ) <= -radius ) return false;
if ( PlaneDistanceToPoint( frustum.far , center ) <= -radius ) return false;
if ( PlaneDistanceToPoint( frustum.near , center ) <= -radius ) return false;
return true;
}
bool FrustumContainsPoint( Frustum frustum , Vector3 point )
{
return FrustumContainsSphere(frustum , point , 0.0f);
}
bool FrustumContainsBox( Frustum frustum , BoundingBox box )
{
// A box is outside the frustum if all its corners are outside a single plane
if ( CheckCollisionPlaneBoxEx( frustum.up , box ) == BOX_ALL_CORNERS ) return false ;
if ( CheckCollisionPlaneBoxEx( frustum.down , box ) == BOX_ALL_CORNERS ) return false ;
if ( CheckCollisionPlaneBoxEx( frustum.left , box ) == BOX_ALL_CORNERS ) return false ;
if ( CheckCollisionPlaneBoxEx( frustum.right, box ) == BOX_ALL_CORNERS ) return false ;
if ( CheckCollisionPlaneBoxEx( frustum.near , box ) == BOX_ALL_CORNERS ) return false ;
if ( CheckCollisionPlaneBoxEx( frustum.far , box ) == BOX_ALL_CORNERS ) return false ;
return true;
}
oh and you forgot about rotated boxes in your example.
Idk, i would like a example more.
@SuperUserNameMan This is indeed an interesting addition but not sure where should it belong, it seems a bit out-of-scope for rcamera module and raylib... I think @JeffM2501 already implemented similar functionality in an example and he can share his opinion.
I think this should be a drop in lib, not part of raylib itself to help with maintenance. It should go in the raylib-extras repo IMHO.
I'm currently trying to implement everything into a single header without changing anything into Raylib's source code. I'll see how far I can go and keep you updated.
I think @JeffM2501 already implemented similar functionality
Here (i think) : https://github.com/JeffM2501/raylibExtras/blob/index/rlExtrasC/Frustum.h
I'm currently adding a Node3D structure in mine to embed Model, and a FrustumDrawNode() function that draws the model only if it is inside the frustum. It will be used to manage LOD too.
Do you have that already ?
Implemented as separate library...