group2 0.1.0
CSE 125 Group 2
Loading...
Searching...
No Matches
SkinnedRenderer Class Reference

Instanced GPU-skinning subsystem. One per renderer. More...

#include <SkinnedRenderer.hpp>

Collaboration diagram for SkinnedRenderer:
[legend]

Classes

struct  SkinnedMesh
 One mesh of the installed skinned rig. More...
struct  SsboInfo

Public Member Functions

 SkinnedRenderer ()=default
 ~SkinnedRenderer ()=default
 SkinnedRenderer (const SkinnedRenderer &)=delete
SkinnedRendereroperator= (const SkinnedRenderer &)=delete
 SkinnedRenderer (SkinnedRenderer &&)=delete
SkinnedRendereroperator= (SkinnedRenderer &&)=delete
void init (SDL_GPUDevice *device, SDL_GPUTextureFormat &colorTarget, const SDL_GPUShaderFormat &shaderFormat)
 Bind the SDL GPU device.
void setFrustumCullEnabled (bool enabled)
 Enable/disable per-instance frustum culling in setFrame.
bool setRig (const std::vector< RigMeshSource > &meshes, int numJoints)
 Install the shared character rig.
void setFrame (const std::vector< glm::mat4 > &palette, const std::vector< SkinnedInstance > &instances, const FrustumPlanes &frustum)
 Push this frame's per-character bone palette + per-instance data and frustum-cull it down to the on-screen subset.
void uploadFrame (SDL_GPUCommandBuffer *cmd, SDL_GPUCopyPass *copyPass)
 Upload this frame's palette + instance buffers to the GPU.
void draw (SDL_GPURenderPass *renderPass, SDL_GPUCommandBuffer *cmd)
 Issue the instanced draws for every visible skinned character.
void drawDepth (SDL_GPURenderPass *renderPass, SDL_GPUCommandBuffer *cmd)
 Issue the instanced draws for every visible skinned character into a depth-only shadow pass.
void drawChams (SDL_GPURenderPass *renderPass, SDL_GPUCommandBuffer *cmd)
 Draw wallhack chams for flagged instances.
void drawKillcamHighlight (SDL_GPURenderPass *renderPass, SDL_GPUCommandBuffer *cmd)
 Draw the killcam killer full-body red highlight.
void shutdown ()
 Release all GPU resources owned by this subsystem.
int numJoints () const
 Number of joints in the installed rig (0 if not installed).
bool rigInstalled () const
 True after a successful setRig.
size_t pendingInstanceCount () const
 Number of instances pending render this frame (0 if no frame submitted).

Private Member Functions

bool ensureSsbos (Uint32 paletteBytes, Uint32 instanceBytes)
 Grow palette/instance SSBOs (and their transfer buffers) to at least these byte sizes.
bool createSkinningPipeline (SDL_GPUTextureFormat &colorTarget, const SDL_GPUShaderFormat &shaderFormat)
bool createSkinnedDepthPipeline (const SDL_GPUShaderFormat &shaderFormat)
bool createChamsPipeline ()
 Create the wallhack chams pipeline.
bool createKillcamHighlightPipeline ()
 Create the killcam full-body highlight pipeline.

Static Private Member Functions

static bool sphereInFrustum (const glm::vec3 &center, float radius, const FrustumPlanes &planes)
 Test whether a world-space bounding sphere intersects (or lies inside) the view frustum.

Private Attributes

SDL_GPUDevice * device_ = nullptr
SDL_GPUGraphicsPipeline * pipeline_ = nullptr
 The skinned graphics pipeline.
SDL_GPUGraphicsPipeline * depthPipeline_ = nullptr
 Depth-only variant of the skinned pipeline, used to rasterise the rig into the shadow map so the player casts a shadow.
SDL_GPUGraphicsPipeline * chamsPipeline_ = nullptr
 Wallhack chams pipeline: flat-red, GREATER depth test, no depth write — draws flagged players where they're occluded by world geometry.
SDL_GPUGraphicsPipeline * killcamHighlightPipeline_ = nullptr
 Killcam full-body red highlight pipeline.
SDL_GPUTextureFormat colorFormat_ {}
 Cached formats (from init) needed to build the chams pipeline.
SDL_GPUShaderFormat shaderFormat_ {}
std::vector< Uint32 > chamsIndices_
 Indices into frameInstances_ of wallhack-revealed enemies.
std::vector< Uint32 > killcamHighlightIndices_
 Indices into frameInstances_ of killcam-highlighted killers.
bool rigInstalled_ = false
int numJoints_ = 0
std::vector< SkinnedMeshskinnedMeshes_
bool frustumCullEnabled_ = false
 Optional per-instance frustum cull in setFrame.
glm::vec3 rigBoundingCenter_ {0.0f}
float rigBoundingRadius_ = 0.0f
SsboInfo palettesSsboInfo_
SsboInfo instancesSsboInfo_
SDL_GPUTransferBuffer * paletteXfer_ = nullptr
SDL_GPUTransferBuffer * instanceXfer_ = nullptr
Uint32 paletteXferCapacityBytes_ = 0
Uint32 instanceXferCapacityBytes_ = 0
std::vector< glm::mat4 > framePalette_
std::vector< SkinnedInstanceframeInstances_
Uint32 visibleInstanceCount_ = 0
bool frameDirty_ = false

Detailed Description

Instanced GPU-skinning subsystem. One per renderer.

Constructor & Destructor Documentation

◆ SkinnedRenderer() [1/3]

SkinnedRenderer::SkinnedRenderer ( )
default
Here is the caller graph for this function:

◆ ~SkinnedRenderer()

SkinnedRenderer::~SkinnedRenderer ( )
default

◆ SkinnedRenderer() [2/3]

SkinnedRenderer::SkinnedRenderer ( const SkinnedRenderer & )
delete
Here is the call graph for this function:

◆ SkinnedRenderer() [3/3]

SkinnedRenderer::SkinnedRenderer ( SkinnedRenderer && )
delete
Here is the call graph for this function:

Member Function Documentation

◆ createChamsPipeline()

bool SkinnedRenderer::createChamsPipeline ( )
private

Create the wallhack chams pipeline.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ createKillcamHighlightPipeline()

bool SkinnedRenderer::createKillcamHighlightPipeline ( )
private

Create the killcam full-body highlight pipeline.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ createSkinnedDepthPipeline()

bool SkinnedRenderer::createSkinnedDepthPipeline ( const SDL_GPUShaderFormat & shaderFormat)
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ createSkinningPipeline()

bool SkinnedRenderer::createSkinningPipeline ( SDL_GPUTextureFormat & colorTarget,
const SDL_GPUShaderFormat & shaderFormat )
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ draw()

void SkinnedRenderer::draw ( SDL_GPURenderPass * renderPass,
SDL_GPUCommandBuffer * cmd )

Issue the instanced draws for every visible skinned character.

Parameters
renderPassThe active main HDR (or swapchain) render pass.
cmdThe frame command buffer.

Called from NewRenderer::drawFrame INSIDE the geometry pass. Currently a no-op placeholder — see cpp for the algorithm sketch.

◆ drawChams()

void SkinnedRenderer::drawChams ( SDL_GPURenderPass * renderPass,
SDL_GPUCommandBuffer * cmd )

Draw wallhack chams for flagged instances.

Material id 2 renders flat-red with a GREATER depth test, so only the parts occluded by world geometry show through. Must be called BEFORE draw() so the depth buffer still holds world-only geometry.

◆ drawDepth()

void SkinnedRenderer::drawDepth ( SDL_GPURenderPass * renderPass,
SDL_GPUCommandBuffer * cmd )

Issue the instanced draws for every visible skinned character into a depth-only shadow pass.

Parameters
renderPassThe active shadow depth render pass.
cmdThe frame command buffer.

Mirrors draw() but binds the depth-only pipeline so the player rig is rasterised into the shadow map (and thus casts shadows). Relies on the caller having already pushed the shadow view-projection at vertex UBO slot 0 (NewRenderer::drawGeometryDepthPass does this).

◆ drawKillcamHighlight()

void SkinnedRenderer::drawKillcamHighlight ( SDL_GPURenderPass * renderPass,
SDL_GPUCommandBuffer * cmd )

Draw the killcam killer full-body red highlight.

Material id 1 renders after the normal skinned pass so the killer body is visibly red in the killcam, matching the weapon highlight.

◆ ensureSsbos()

bool SkinnedRenderer::ensureSsbos ( Uint32 paletteBytes,
Uint32 instanceBytes )
private

Grow palette/instance SSBOs (and their transfer buffers) to at least these byte sizes.

Here is the caller graph for this function:

◆ init()

void SkinnedRenderer::init ( SDL_GPUDevice * device,
SDL_GPUTextureFormat & colorTarget,
const SDL_GPUShaderFormat & shaderFormat )

Bind the SDL GPU device.

Call from NewRenderer::init once the device exists. Does NOT allocate any GPU resources yet; those are created lazily on the first setRig / setFrame call.

Parameters
deviceBorrowed; the device must outlive this SkinnedRenderer.
Here is the call graph for this function:

◆ numJoints()

int SkinnedRenderer::numJoints ( ) const
inlinenodiscard

Number of joints in the installed rig (0 if not installed).

Here is the caller graph for this function:

◆ operator=() [1/2]

SkinnedRenderer & SkinnedRenderer::operator= ( const SkinnedRenderer & )
delete
Here is the call graph for this function:

◆ operator=() [2/2]

SkinnedRenderer & SkinnedRenderer::operator= ( SkinnedRenderer && )
delete
Here is the call graph for this function:

◆ pendingInstanceCount()

size_t SkinnedRenderer::pendingInstanceCount ( ) const
inlinenodiscard

Number of instances pending render this frame (0 if no frame submitted).

◆ rigInstalled()

bool SkinnedRenderer::rigInstalled ( ) const
inlinenodiscard

True after a successful setRig.

◆ setFrame()

void SkinnedRenderer::setFrame ( const std::vector< glm::mat4 > & palette,
const std::vector< SkinnedInstance > & instances,
const FrustumPlanes & frustum )

Push this frame's per-character bone palette + per-instance data and frustum-cull it down to the on-screen subset.

Parameters
paletteFlat array, sized numInstances * numJoints (mat4 each).
instancesOne entry per character (ALL of them — visible or not). paletteBase of entry i MUST equal i * numJoints so the palette slice for that instance can be located.
frustumCamera view-projection frustum planes (Gribb-Hartmann). Each instance is bounding-sphere tested against these and dropped if fully outside the view.

IMPLEMENTATION (real, wired):

  • Sphere-culls instances against frustum: the bounding sphere is the rig's bind-pose bounds (computed in setRig) transformed by the instance worldTransform, so the test tracks the actual rendered mesh (which is vertically offset from the sim position) rather than a bare point — this is what fixes characters popping out at the screen edge.
  • Copies the surviving instances (and their palette slices, compacted so no off-screen palettes are uploaded) into CPU-side staging (framePalette_, frameInstances_).
  • The actual GPU upload happens later via uploadFrame() (called by NewRenderer::drawFrame before the render pass starts).

DATA SOURCE: Game.cpp's per-frame animation block walks the ECS view <AnimatedCharacter, Position, Velocity, PlayerVisState, InputSnapshot>:

  1. For each character compute worldTransform = T(pos) * R(yaw) * S(scale).
  2. palette[slot * numJoints .. (slot+1) * numJoints] = animator.skinMatrices().
  3. instances[slot] = {worldTransform, paletteBase=slot*numJoints, tint}.

CONSUMER: the skinned vertex shader reads: instances[gl_InstanceIndex] → worldTransform + paletteBase palette[paletteBase + boneIndices.k] → bone matrix k (k = 0..3)

Here is the call graph for this function:

◆ setFrustumCullEnabled()

void SkinnedRenderer::setFrustumCullEnabled ( bool enabled)
inline

Enable/disable per-instance frustum culling in setFrame.

Defaults to true.

◆ setRig()

bool SkinnedRenderer::setRig ( const std::vector< RigMeshSource > & meshes,
int numJoints )

Install the shared character rig.

Call ONCE after init.

Parameters
meshesOne source-mesh entry per skinned mesh in the rig (typically 1-3 for humanoid rigs).
numJointsNumber of skeleton joints. Determines per-instance palette stride in the shader.
Returns
True on success. False if already installed, or upload failed.

IMPLEMENTATION (real, wired):

  • Iterates meshes, creating three GPU buffers per mesh: slot 0: bind-pose vertices (ModelVertex, 48 bytes/vert) — VERTEX usage slot 1: bone influences (BoneInfluence, 32 bytes/vert) — VERTEX usage slot 2: indices (uint32_t, 4 bytes each) — INDEX usage Saves them in skinnedMeshes_.
  • Stores numJoints in numJoints_; sets rigInstalled_.
  • Does NOT allocate palette/instance SSBOs — those grow lazily on the first setFrame call.

DATA SOURCE: Game.cpp builds vector<RigMeshSource> from CharacterRig::meshes() (animation/CharacterRig.hpp) once after the rig FBX loads. See RigMeshSource in RendererTypes.hpp for the layout.

Here is the call graph for this function:

◆ shutdown()

void SkinnedRenderer::shutdown ( )

Release all GPU resources owned by this subsystem.

Called from NewRenderer::quit (BEFORE the device is destroyed).

◆ sphereInFrustum()

bool SkinnedRenderer::sphereInFrustum ( const glm::vec3 & center,
float radius,
const FrustumPlanes & planes )
staticprivate

Test whether a world-space bounding sphere intersects (or lies inside) the view frustum.

Uses the Gribb-Hartmann plane convention of FrustumPlanes (a point is inside a plane when dot(plane.xyz, p) + plane.w >= 0). Returns false only when the sphere is fully outside at least one plane.

Here is the caller graph for this function:

◆ uploadFrame()

void SkinnedRenderer::uploadFrame ( SDL_GPUCommandBuffer * cmd,
SDL_GPUCopyPass * copyPass )

Upload this frame's palette + instance buffers to the GPU.

Parameters
cmdFrame command buffer.
copyPassAn open copy pass on cmd.

Called from NewRenderer::drawFrame BEFORE the main render pass begins so the copy is sequenced ahead of the draws. Uses cycle=true on the transfer-buffer map so we never stall waiting on last frame's GPU read of the SSBO.

Here is the call graph for this function:

Member Data Documentation

◆ chamsIndices_

std::vector<Uint32> SkinnedRenderer::chamsIndices_
private

Indices into frameInstances_ of wallhack-revealed enemies.

◆ chamsPipeline_

SDL_GPUGraphicsPipeline* SkinnedRenderer::chamsPipeline_ = nullptr
private

Wallhack chams pipeline: flat-red, GREATER depth test, no depth write — draws flagged players where they're occluded by world geometry.

◆ colorFormat_

SDL_GPUTextureFormat SkinnedRenderer::colorFormat_ {}
private

Cached formats (from init) needed to build the chams pipeline.

◆ depthPipeline_

SDL_GPUGraphicsPipeline* SkinnedRenderer::depthPipeline_ = nullptr
private

Depth-only variant of the skinned pipeline, used to rasterise the rig into the shadow map so the player casts a shadow.

◆ device_

SDL_GPUDevice* SkinnedRenderer::device_ = nullptr
private

◆ frameDirty_

bool SkinnedRenderer::frameDirty_ = false
private

◆ frameInstances_

std::vector<SkinnedInstance> SkinnedRenderer::frameInstances_
private

◆ framePalette_

std::vector<glm::mat4> SkinnedRenderer::framePalette_
private

◆ frustumCullEnabled_

bool SkinnedRenderer::frustumCullEnabled_ = false
private

Optional per-instance frustum cull in setFrame.

◆ instancesSsboInfo_

SsboInfo SkinnedRenderer::instancesSsboInfo_
private

◆ instanceXfer_

SDL_GPUTransferBuffer* SkinnedRenderer::instanceXfer_ = nullptr
private

◆ instanceXferCapacityBytes_

Uint32 SkinnedRenderer::instanceXferCapacityBytes_ = 0
private

◆ killcamHighlightIndices_

std::vector<Uint32> SkinnedRenderer::killcamHighlightIndices_
private

Indices into frameInstances_ of killcam-highlighted killers.

◆ killcamHighlightPipeline_

SDL_GPUGraphicsPipeline* SkinnedRenderer::killcamHighlightPipeline_ = nullptr
private

Killcam full-body red highlight pipeline.

◆ numJoints_

int SkinnedRenderer::numJoints_ = 0
private

◆ palettesSsboInfo_

SsboInfo SkinnedRenderer::palettesSsboInfo_
private

◆ paletteXfer_

SDL_GPUTransferBuffer* SkinnedRenderer::paletteXfer_ = nullptr
private

◆ paletteXferCapacityBytes_

Uint32 SkinnedRenderer::paletteXferCapacityBytes_ = 0
private

◆ pipeline_

SDL_GPUGraphicsPipeline* SkinnedRenderer::pipeline_ = nullptr
private

The skinned graphics pipeline.

TODO(graphics): create this in init (or lazily on first frame) with two vertex buffers (ModelVertex, BoneInfluence), two vertex storage buffers (palette, instances), one vertex UBO (view-projection), depth test on, cull mode NONE.

◆ rigBoundingCenter_

glm::vec3 SkinnedRenderer::rigBoundingCenter_ {0.0f}
private

◆ rigBoundingRadius_

float SkinnedRenderer::rigBoundingRadius_ = 0.0f
private

◆ rigInstalled_

bool SkinnedRenderer::rigInstalled_ = false
private

◆ shaderFormat_

SDL_GPUShaderFormat SkinnedRenderer::shaderFormat_ {}
private

◆ skinnedMeshes_

std::vector<SkinnedMesh> SkinnedRenderer::skinnedMeshes_
private

◆ visibleInstanceCount_

Uint32 SkinnedRenderer::visibleInstanceCount_ = 0
private

The documentation for this class was generated from the following files: