mirror of
https://github.com/thug1src/thug.git
synced 2025-01-22 05:43:47 +00:00
2347 lines
71 KiB
C++
2347 lines
71 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <libgraph.h>
|
|
#include <libdma.h>
|
|
#include <libsn.h>
|
|
#include <devvu1.h>
|
|
#include <math.h>
|
|
#include <sifrpc.h>
|
|
#include <sifdev.h>
|
|
#include <libpad.h>
|
|
#include <core\math.h>
|
|
#include <core\defines.h>
|
|
#include <core\debug.h>
|
|
#include <gfx\nxviewman.h>
|
|
#include <sys\profiler.h>
|
|
#include <sys\timer.h>
|
|
#include "mikemath.h"
|
|
#include "dma.h"
|
|
#include "vif.h"
|
|
#include "vu1.h"
|
|
#include "gif.h"
|
|
#include "gs.h"
|
|
#include "vu1code.h"
|
|
#include "dmacalls.h"
|
|
#include "line.h"
|
|
#include "chars.h"
|
|
#include "texture.h"
|
|
#include "material.h"
|
|
#include "group.h"
|
|
#include "scene.h"
|
|
#include "mesh.h"
|
|
#include "nx_init.h"
|
|
#include "render.h"
|
|
#include "interrupts.h"
|
|
#include "switches.h"
|
|
#include "sprite.h"
|
|
#include "instance.h"
|
|
#include "geomnode.h"
|
|
#include "occlude.h"
|
|
#include "resource.h"
|
|
#include "vu1context.h"
|
|
#include "light.h"
|
|
#include "fx.h"
|
|
#include "asmdma.h"
|
|
#include "vu1\newvu1code.h"
|
|
|
|
#include "gfx\nx.h"
|
|
#include "gfx\ngps\p_nxscene.h"
|
|
#include "gfx\ngps\p_nxtexture.h"
|
|
|
|
|
|
//#undef __USE_PROFILER__
|
|
|
|
|
|
namespace Inp
|
|
{
|
|
extern uint32 gDebugButtons[];
|
|
extern uint32 gDebugMakes[];
|
|
|
|
}
|
|
|
|
namespace NxPs2
|
|
{
|
|
|
|
extern uint32 *p_patch_PRMODE;
|
|
extern uint32 gPassMask1;
|
|
|
|
|
|
extern int geom_stats_total;
|
|
extern int geom_stats_inactive ;
|
|
extern int geom_stats_sky;
|
|
extern int geom_stats_transformed;
|
|
extern int geom_stats_skeletal;
|
|
extern int geom_stats_camera_sphere;
|
|
extern int geom_stats_clipcull;
|
|
extern int geom_stats_culled;
|
|
extern int geom_stats_leaf_culled;
|
|
extern int geom_stats_boxcheck;
|
|
extern int geom_stats_occludecheck;
|
|
extern int geom_stats_occluded;
|
|
extern int geom_stats_occludecheck;
|
|
extern int geom_stats_occluded;
|
|
extern int geom_stats_occludecheck;
|
|
extern int geom_stats_occluded;
|
|
extern int geom_stats_colored;
|
|
extern int geom_stats_leaf;
|
|
extern int geom_stats_wibbleUV;
|
|
extern int geom_stats_wibbleVC;
|
|
extern int geom_stats_sendcontext;
|
|
extern int geom_stats_sorted;
|
|
extern int geom_stats_shadow;
|
|
|
|
// Mick: Letterboxing on the ps2 is a post-processing
|
|
// step one frame ahead in the rendering pipeline (as
|
|
// compared to things like setting the FOV), so
|
|
// it must be delayed by 1 frame to synchronize it
|
|
// with such events.
|
|
bool ActualDoLetterbox = false;
|
|
bool DoLetterbox = false;
|
|
|
|
int DMAOverflowOK = false;
|
|
|
|
bool DoFlipCopy = true;
|
|
sGroup PrologueGroup;
|
|
float SkaterY;
|
|
|
|
int SingleRender = 0;
|
|
|
|
// skinning globals
|
|
Mat SkaterTransform;
|
|
Mat BoneTransforms[64];
|
|
int NumBones=0;
|
|
|
|
|
|
#if STENCIL_SHADOW
|
|
void CastStencilShadow(CInstance *pInstance, sGroup *p_group, Mth::Vector &ShadowVec, Mth::Vector &TweakVec);
|
|
#else
|
|
void CastShadow(CInstance *pInstance, sGroup *p_group);
|
|
#endif
|
|
|
|
void EnableFlipCopy(bool flip)
|
|
{
|
|
DoFlipCopy = flip;
|
|
}
|
|
|
|
bool FlipCopyEnabled()
|
|
{
|
|
return DoFlipCopy;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// void RenderPrologue(void)
|
|
//
|
|
// called at the start of a frame
|
|
// sets up the initial DMA list
|
|
// by setting up a prologue "group"
|
|
// consisting of an empty upload portion, and
|
|
// a render portion that flips the visible buffers,
|
|
// and sets up various vif settings
|
|
// finally we initilize vu1::Loc = vu1::Buffer=0;
|
|
//
|
|
// Note, Field will be 0 or 1, depending on which buffer we are drawing into
|
|
|
|
void RenderPrologue(void)
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
// a nasty patch by Mick to allow us to see the extra passes in flat shaded mode
|
|
if (gPassMask1)
|
|
{
|
|
if (Inp::gDebugMakes[0] & (1<<5)) // check for Circle pressed
|
|
{
|
|
*p_patch_PRMODE ^= 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*p_patch_PRMODE = 1;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
// record the time
|
|
render::sTime = (int)Tmr::GetTime();
|
|
|
|
// set dma pointer to the start of the dma buffer
|
|
dma::pLoc = dma::pList[render::Field];
|
|
dma::pBase = dma::pLoc;
|
|
|
|
PrologueGroup.profile_color = 0x008080; // yellow = prologue group
|
|
|
|
// prologue render begins here
|
|
PrologueGroup.pRender[render::Field] = dma::pLoc;
|
|
PrologueGroup.Used[render::Field] = true; // prologue always used
|
|
|
|
// call the DMA routine to flip frame buffers and clear the draw buffer
|
|
// currently the flip is accomplished by rendering a full screen sprite from the
|
|
// draw buffer to the display buffer. This converts it from 32-bit to 16-bit (dithered)
|
|
// and means there is no actual buffer switching going on
|
|
// the draw buffer is always at the same address.
|
|
// When the game is running at 60fps, this copying takes place during the vblank.
|
|
|
|
//static bool last_flip_copy = true;
|
|
if (DoFlipCopy /*&& last_flip_copy*/) // DoFlipCopy is set/cleared by the loading screen code (p_NxLoadScreen.cpp)
|
|
{
|
|
if (ActualDoLetterbox)
|
|
dma::Gosub(FLIP_N_CLEAR_LETTERBOX,2);
|
|
else
|
|
dma::Gosub(FLIP_N_CLEAR,2);
|
|
ActualDoLetterbox=DoLetterbox;
|
|
}
|
|
else
|
|
{
|
|
dma::Gosub(CLEAR_ZBUFFER,2);
|
|
}
|
|
//last_flip_copy = DoFlipCopy;
|
|
|
|
// set the GS to the default rendering state
|
|
dma::Gosub(SET_RENDERSTATE,2);
|
|
dma::Gosub(SET_FOGCOL,2);
|
|
|
|
// upload VU1 microcode (the code in microcode.dsm)
|
|
// This is done every frame, at the start of the frame
|
|
// it actually only needs doing once, but it does not really take
|
|
// any time, and doing it every frame gives us the flexibility to uploaded new
|
|
// microcode at a later stage in rendering.
|
|
// for now, all the microcode fits in the 16K code area of VU1
|
|
dma::Tag(dma::ref, ((uint8 *)&MPGEnd-(uint8 *)&MPGStart+15)/16, (uint)&MPGStart);
|
|
vif::NOP();
|
|
vif::NOP();
|
|
|
|
// VIF1 and VU1 setup
|
|
dma::BeginTag(dma::end, 0);
|
|
vif::FLUSH();
|
|
vif::STMASK(0);
|
|
vif::STMOD(0);
|
|
vif::STCYCL(1,1);
|
|
vif::BASE(0);
|
|
vif::OFFSET(0);
|
|
vif::MSCAL(VU1_ADDR(Setup));
|
|
dma::EndTag();
|
|
|
|
// prepare all groups for rendering
|
|
for (sGroup *p_group=sGroup::pHead; p_group; p_group=p_group->pNext)
|
|
{
|
|
// initialise the dma list
|
|
dma::BeginList(p_group);
|
|
|
|
// assume textures aren't used until something actually gets rendered
|
|
p_group->Used[render::Field] = false;
|
|
}
|
|
dma::sp_group = NULL;
|
|
|
|
|
|
// while the dma context is null, set up an empty upload for the particles group and fog group
|
|
sGroup::pParticles->pUpload[render::Field] = dma::pLoc;
|
|
sGroup::pFog->pUpload[render::Field] = dma::pLoc;
|
|
dma::Tag(dma::end, 0, 0);
|
|
vif::NOP();
|
|
vif::NOP();
|
|
|
|
// signal that the particle group is used
|
|
sGroup::pParticles->Used[render::Field] = true;
|
|
|
|
// reset current marker number
|
|
render::sMarkerIndex = 0;
|
|
|
|
// count particles
|
|
render::sTotalNewParticles = 0;
|
|
}
|
|
|
|
|
|
|
|
void RenderWorld(Mth::Matrix* camera_orient, Mth::Vector* camera_pos, float view_angle, float screen_aspect)
|
|
{
|
|
Mth::Matrix transform(*camera_orient);
|
|
transform[3] = *camera_pos;
|
|
|
|
// need the transform to be well-formed as a 4x4 matrix
|
|
transform[0][3] = 0.0f;
|
|
transform[1][3] = 0.0f;
|
|
transform[2][3] = 0.0f;
|
|
transform[3][3] = 1.0f;
|
|
|
|
Mth::Rect viewport1(0.0f, 0.0f, 1.0f, 1.0f);
|
|
RenderViewport(0, transform, viewport1, view_angle, screen_aspect, -1.0f, -100000.0f);
|
|
}
|
|
|
|
void RenderInitImmediateMode()
|
|
{
|
|
dma::SetList(sGroup::pEpilogue);
|
|
|
|
// epilogue render (a terrible hack to put this here!)
|
|
//sGroup::pEpilogue->pRender[render::Field] = dma::pLoc;
|
|
sGroup::pEpilogue->Used[render::Field] = true; // epilogue always rendered
|
|
|
|
// VIF1 and VU1 setup
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vif::FLUSH();
|
|
vif::STROW((int)render::RowRegI[0], (int)render::RowRegI[1], (int)render::RowRegI[2], (int)render::RowRegI[3]);
|
|
vif::STMASK(0);
|
|
vif::STMOD(0);
|
|
vif::STCYCL(1,1);
|
|
vif::BASE(0);
|
|
vif::OFFSET(0);
|
|
vif::MSCAL(VU1_ADDR(Setup));
|
|
vu1::Loc = vu1::Buffer=0;
|
|
//vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
|
|
//vu1::StoreVec(*(Vec *)&render::ViewportScale); // VF10
|
|
//vu1::StoreVec(*(Vec *)&render::ViewportOffset); // VF11
|
|
//vu1::StoreMat(*(Mat *)&render::AdjustedWorldToFrustum); // VF12-15
|
|
//vu1::StoreMat(*(Mat *)&render::AdjustedWorldToViewport); // VF16-19
|
|
//vu1::EndPrim(0);
|
|
gs::BeginPrim(ABS,0,0);
|
|
gs::Reg1(gs::XYOFFSET_1, PackXYOFFSET(XOFFSET, YOFFSET));
|
|
gs::Reg1(gs::SCISSOR_1, PackSCISSOR(0,HRES - 1,0,VRES - 1));
|
|
gs::Reg1(gs::TEST_1, PackTEST(0,0,0,0,0,0,1,ZGEQUAL));
|
|
gs::Reg1(gs::CLAMP_1,PackCLAMP(CLAMP,CLAMP,0,0,0,0));
|
|
gs::Reg1(gs::PRMODECONT, PackPRMODECONT(1));
|
|
gs::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
dma::EndTag();
|
|
}
|
|
|
|
void RenderSwitchImmediateMode()
|
|
{
|
|
dma::SetList(sGroup::pEpilogue);
|
|
}
|
|
|
|
|
|
// camera_transform must be a well-formed 4x4 matrix
|
|
void RenderViewport(uint viewport_num, const Mth::Matrix& camera_transform, const Mth::Rect& viewport_rec, float view_angle,
|
|
float viewport_aspect, float near, float far, bool just_setup)
|
|
{
|
|
// set up a load of persistent data for current viewport
|
|
render::SetupVars(viewport_num, camera_transform, viewport_rec, view_angle, viewport_aspect, near, far);
|
|
|
|
// Perform the DMA seperately, as the actual VARS might be re-set by update_render_vars called from plat_transform_to_screen
|
|
render::SetupVarsDMA(viewport_num, near, far);
|
|
|
|
// old and new style renders
|
|
# ifdef __USE_PROFILER__
|
|
Sys::CPUProfiler->PushContext( 255, 0, 0 ); // Red (Under Yellow) = OldStyle Rendering (Skins and stuff)
|
|
# endif // __USE_PROFILER__
|
|
render::OldStyle();
|
|
# ifdef __USE_PROFILER__
|
|
Sys::CPUProfiler->PopContext( );
|
|
# endif // __USE_PROFILER__
|
|
|
|
|
|
# ifdef __USE_PROFILER__
|
|
Sys::CPUProfiler->PushContext( 0, 255, 0 ); // Green (Under Yellow) = NewStyle rendering (Environment)
|
|
# endif // __USE_PROFILER__
|
|
|
|
render::NewStyle(just_setup);
|
|
# ifdef __USE_PROFILER__
|
|
Sys::CPUProfiler->PopContext( );
|
|
# endif // __USE_PROFILER__
|
|
}
|
|
|
|
// Uncomment the next line to use the color-per-mesh feature
|
|
#define USE_COLOR_PER_MESH
|
|
|
|
void render::OldStyle()
|
|
{
|
|
// Don't render anything if it's not going to be displayed
|
|
if (!FlipCopyEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
float x,y,z,R;
|
|
sMesh *pMesh;
|
|
sGroup *p_group;
|
|
int j;
|
|
uint32 vu1_flags;
|
|
uint32 colour;
|
|
float colourComponent;
|
|
|
|
Mth::Matrix LocalToWorld;
|
|
Mth::Matrix LocalToCamera;
|
|
Mth::Matrix LocalToFrustum;
|
|
Mth::Matrix LocalToViewport;
|
|
Mth::Matrix RootToWorld;
|
|
Mth::Matrix RootToCamera;
|
|
Mth::Matrix RootToFrustum;
|
|
Mth::Matrix RootToViewport;
|
|
Mth::Matrix LightMatrix;
|
|
Mth::Matrix ColourMatrix;
|
|
|
|
Mth::Vector ambientColour;
|
|
Mth::Vector diffuseColour0;
|
|
Mth::Vector diffuseColour1;
|
|
Mth::Vector diffuseColour2;
|
|
Mth::Vector colouredAmbientColour;
|
|
Mth::Vector colouredDiffuseColour0;
|
|
Mth::Vector colouredDiffuseColour1;
|
|
Mth::Vector colouredDiffuseColour2;
|
|
Mth::Vector v;
|
|
|
|
// Mick: Initialize these local matricies to known values....
|
|
LocalToWorld.Zero();
|
|
LocalToCamera.Zero();
|
|
LocalToFrustum.Zero();
|
|
LocalToViewport.Zero();
|
|
RootToWorld.Zero();
|
|
RootToCamera.Zero();
|
|
RootToFrustum.Zero();
|
|
RootToViewport.Zero();
|
|
LightMatrix.Zero();
|
|
// And the vectors...
|
|
ambientColour.Set(0.0f,0.0f,0.0f,1.0f);
|
|
diffuseColour0.Set(0.0f,0.0f,0.0f,1.0f);
|
|
diffuseColour1.Set(0.0f,0.0f,0.0f,1.0f);
|
|
diffuseColour2.Set(0.0f,0.0f,0.0f,1.0f);
|
|
colouredAmbientColour.Set(0.0f,0.0f,0.0f,1.0f);
|
|
colouredDiffuseColour0.Set(0.0f,0.0f,0.0f,1.0f);
|
|
colouredDiffuseColour1.Set(0.0f,0.0f,0.0f,1.0f);
|
|
colouredDiffuseColour2.Set(0.0f,0.0f,0.0f,1.0f);
|
|
v.Set(0.0f,0.0f,0.0f,1.0f);
|
|
|
|
|
|
|
|
#if STENCIL_SHADOW
|
|
// prepare stencil shadow dma list for generating the stencil buffer
|
|
dma::SetList(sGroup::pShadow);
|
|
|
|
// send the GS setup
|
|
dma::BeginTag(dma::cnt, 0);
|
|
gs::BeginPrim(ABS,0,0);
|
|
gs::Reg1(gs::XYOFFSET_2, PackXYOFFSET(XOFFSET, YOFFSET));
|
|
gs::Reg1(gs::ZBUF_2, PackZBUF(ZBUFFER_START,PSMZ24,1));
|
|
gs::Reg1(gs::SCISSOR_2, PackSCISSOR(0,HRES - 1,0,VRES - 1));
|
|
gs::Reg1(gs::FRAME_2, PackFRAME(0x1BA,HRES/64,PSMCT16S,0x00000000));
|
|
//gs::Reg1(gs::FRAME_2, PackFRAME(FRAME_START,HRES/64,PSMCT32,0x00000000));
|
|
gs::Reg1(gs::ALPHA_2, PackALPHA(0,2,2,1,128));
|
|
gs::Reg1(gs::FBA_2, PackFBA(0));
|
|
gs::Reg1(gs::PABE, PackPABE(0));
|
|
gs::Reg1(gs::COLCLAMP, PackCOLCLAMP(0));
|
|
gs::Reg1(gs::PRMODECONT, PackPRMODECONT(1));
|
|
gs::Reg1(gs::DTHE, PackDTHE(0));
|
|
gs::Reg1(gs::TEST_2, PackTEST(0,0,0,0,0,0,1,ZALWAYS));
|
|
gs::Reg1(gs::PRIM, PackPRIM(SPRITE,0,0,0,0,0,0,1,0)); // clear the stencil buffer
|
|
gs::Reg1(gs::RGBAQ, PackRGBAQ(0,0,0,0,0));
|
|
gs::Reg1(gs::XYZ2, PackXYZ(XOFFSET,YOFFSET,0));
|
|
gs::Reg1(gs::XYZ2, PackXYZ(0x10000-XOFFSET,0x10000-YOFFSET,0));
|
|
gs::Reg1(gs::TEST_2, PackTEST(0,0,0,0,0,0,1,ZGREATER));
|
|
gs::Reg1(gs::FRAME_2, PackFRAME(0x1BA,HRES/64,PSMCT16S,0xFF000000));
|
|
gs::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
dma::EndTag();
|
|
#else
|
|
// prepare shadow dma list for generating the shadow texture
|
|
dma::SetList(sGroup::pShadow);
|
|
|
|
// send the GS setup
|
|
dma::BeginTag(dma::cnt, 0);
|
|
gs::BeginPrim(ABS,0,0);
|
|
gs::Reg1(gs::XYOFFSET_2, PackXYOFFSET(0x7800,0x7800));
|
|
gs::Reg1(gs::ZBUF_2, PackZBUF(0,PSMZ16,1));
|
|
gs::Reg1(gs::TEST_2, PackTEST(0,0,0,0,0,0,1,ZALWAYS));
|
|
gs::Reg1(gs::SCISSOR_2, PackSCISSOR(0,255,0,255));
|
|
gs::Reg1(gs::FRAME_2, PackFRAME(0x1F0,4,PSMCT16,0x00000000));
|
|
//gs::Reg1(gs::FRAME_2, PackFRAME(0x000,HRES/64,PSMCT32,0x00000000));
|
|
gs::Reg1(gs::FBA_2, PackFBA(0));
|
|
gs::Reg1(gs::PRIM, PackPRIM(SPRITE,0,0,0,0,0,0,1,0)); // clear the texture
|
|
gs::Reg1(gs::RGBAQ, PackRGBAQ(0,0,0,0,0));
|
|
gs::Reg1(gs::XYZ2, PackXYZ(0x7800,0x7800,0));
|
|
gs::Reg1(gs::XYZ2, PackXYZ(0x8800,0x8800,0));
|
|
gs::Reg1(gs::FBA_2, PackFBA(1)); // set A on all pixels we touch
|
|
gs::Reg1(gs::SCISSOR_2, PackSCISSOR(1,254,1,254)); // don't touch edge of texture to avoid streaks
|
|
gs::Reg1(gs::PRMODECONT, PackPRMODECONT(0));
|
|
gs::Reg1(gs::PRMODE, PackPRMODE(0,0,0,0,0,0,1,0));
|
|
gs::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
dma::EndTag();
|
|
#endif
|
|
|
|
|
|
// assume no dma context
|
|
dma::SetList(NULL);
|
|
|
|
// render old-style groups
|
|
for (p_group=sGroup::pHead; p_group!=sGroup::pEpilogue; p_group=p_group->pNext)
|
|
{
|
|
// skip the old render mechanism if the group belongs to a pip-style scene
|
|
if (p_group==sGroup::pShadow || !p_group->pScene || p_group->pScene->Flags & SCENEFLAG_USESPIP)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dma::SetList(p_group);
|
|
|
|
dma::BeginTag(dma::cnt, 0);
|
|
|
|
// VIF1 and VU1 setup
|
|
vif::FLUSH();
|
|
vif::STMASK(0);
|
|
vif::STMOD(0);
|
|
vif::STCYCL(1,1);
|
|
vif::STCOL(0x3F800000,0,0,0);
|
|
vif::BASE(0);
|
|
vif::OFFSET(0);
|
|
vif::MSCAL(VU1_ADDR(Setup));
|
|
vu1::Loc = vu1::Buffer=0;
|
|
|
|
// constant part of vu1 context data
|
|
vu1::BeginPrim(ABS, VU1_ADDR(L_VF09));
|
|
// vu1::StoreVec(*(Vec *)&render::AltFrustum); // VF09
|
|
// vu1::StoreVec(*(Vec *)&render::InverseViewportScale); // VF10
|
|
// vu1::StoreVec(*(Vec *)&render::InverseViewportOffset); // VF11
|
|
vu1::CopyQuads((uint32*)&render::AltFrustum,
|
|
(uint32*)&render::InverseViewportScale,
|
|
(uint32*)&render::InverseViewportOffset
|
|
);
|
|
vu1::EndPrim(0);
|
|
|
|
// send the GS viewport context
|
|
gs::BeginPrim(ABS, 0, 0);
|
|
gs::Reg1(gs::XYOFFSET_1, render::reg_XYOFFSET);
|
|
gs::Reg1(gs::SCISSOR_1, render::reg_SCISSOR);
|
|
gs::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
|
|
dma::EndTag();
|
|
|
|
|
|
Mth::Matrix refl_temp;
|
|
Mth::Matrix ReflVecs;
|
|
refl_temp[0] = Mth::Vector(1.0f, 0.0f, 0.0f, 0.0f);
|
|
refl_temp[1] = Mth::Vector(0.0f, 1.0f, 0.0f, 0.0f);
|
|
refl_temp[2] = Mth::Vector(0.0f, 0.0f, 1.4f, 0.0f);
|
|
refl_temp[3] = Mth::Vector(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
// this part handles the instanced non-skinned models
|
|
if (p_group->pScene && (p_group->pScene->Flags & SCENEFLAG_INSTANCEABLE) &&
|
|
p_group->pMeshes && !(p_group->pMeshes->Flags & MESHFLAG_SKINNED))
|
|
{
|
|
for (CInstance *pInstance=p_group->pScene->pInstances; pInstance; pInstance=pInstance->GetNext())
|
|
{
|
|
// make sure the instance is active
|
|
if (!pInstance->IsActive())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LocalToWorld = *(Mth::Matrix *)pInstance->GetTransform();
|
|
LocalToCamera = LocalToWorld * render::WorldToCamera;
|
|
LocalToFrustum = LocalToWorld * render::WorldToFrustum;
|
|
LocalToViewport = LocalToFrustum * render::FrustumToViewport;
|
|
|
|
// set up reflection map transform
|
|
sceVu0MulMatrix((sceVu0FVECTOR*)&ReflVecs,
|
|
(sceVu0FVECTOR*)&refl_temp,
|
|
(sceVu0FVECTOR*)&LocalToCamera);
|
|
|
|
// send VIF/VU context
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vif::STROW(0,0,0,0);
|
|
vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));
|
|
vu1::StoreMat(*(Mat *)&LocalToViewport); // VF12-15
|
|
vu1::StoreMat(*(Mat *)&ReflVecs); // VF16-19
|
|
vu1::EndPrim(0);
|
|
|
|
//gs::BeginPrim(ABS,0,0);
|
|
//gs::Reg1(gs::FOGCOL, PackFOGCOL(0x60,0x40,0xC0));
|
|
//gs::EndPrim(0);
|
|
|
|
dma::EndTag();
|
|
|
|
// traverse array of meshes in this group
|
|
for (j=0,pMesh=p_group->pMeshes; j<p_group->NumMeshes; j++,pMesh++)
|
|
{
|
|
// skip if invisible
|
|
if (!pMesh->IsActive())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// do nothing if mesh doesn't have a dma subroutine
|
|
if (!pMesh->pSubroutine)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
v = pMesh->Sphere;
|
|
v[3] = 1.0f;
|
|
v *= LocalToCamera;
|
|
x = v[0];
|
|
y = v[1];
|
|
z = v[2];
|
|
R = pMesh->Sphere[3];
|
|
|
|
// if all outside view volume then cull
|
|
if (R<z-render::Near || R<render::Far-z ||
|
|
R<render::sy*z+render::cy*y || R<render::sy*z-render::cy*y ||
|
|
R<render::sx*z-render::cx*x || R<render::sx*z+render::cx*x)
|
|
continue;
|
|
|
|
// else if all inside outer volume then render normally
|
|
else if (R<render::Near-z && R<z-render::Far &&
|
|
R<-render::Sy*z-render::Cy*y && R<-render::Sy*z+render::Cy*y &&
|
|
R<-render::Sx*z+render::Cx*x && R<-render::Sx*z-render::Cx*x)
|
|
{
|
|
dma::Gosub3D(pMesh->pSubroutine, PROJ);
|
|
}
|
|
|
|
// otherwise cull at the vertex level
|
|
else
|
|
{
|
|
dma::Gosub3D(pMesh->pSubroutine, CLIP);
|
|
}
|
|
|
|
p_group->Used[render::Field] = true; // Note, should have culling before here
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// this part handles the instanced skinned models
|
|
if (p_group->pScene && (p_group->pScene->Flags & SCENEFLAG_INSTANCEABLE) &&
|
|
p_group->pMeshes && (p_group->pMeshes->Flags & MESHFLAG_SKINNED))
|
|
{
|
|
for (CInstance *pInstance=p_group->pScene->pInstances; pInstance; pInstance=pInstance->GetNext())
|
|
{
|
|
// make sure the instance is active
|
|
if (!pInstance->IsActive())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// shadow
|
|
if (pInstance->CastsShadow())
|
|
{
|
|
#if STENCIL_SHADOW
|
|
Mth::Vector shadow_vec(20.0f, -100.0f, 0.0f, 0.0f);
|
|
Mth::Vector tweak_vec(0.4f, -2.0f, 0.0f, 0.0f);
|
|
CastStencilShadow(pInstance, p_group, shadow_vec, tweak_vec);
|
|
#else
|
|
CastShadow(pInstance, p_group);
|
|
#endif
|
|
}
|
|
|
|
// mat for bounding sphere test
|
|
RootToWorld = *(Mth::Matrix *)pInstance->GetTransform();
|
|
RootToCamera = RootToWorld * render::WorldToCamera;
|
|
|
|
// get bounding sphere
|
|
v = pInstance->GetBoundingSphere();
|
|
R = v[3];
|
|
v[3] = 1.0f;
|
|
v *= RootToCamera;
|
|
x = v[0];
|
|
y = v[1];
|
|
z = v[2];
|
|
|
|
// if all outside view volume then cull
|
|
if (R<z-render::Near || R<render::Far-z ||
|
|
R<render::sy*z+render::cy*y || R<render::sy*z-render::cy*y ||
|
|
R<render::sx*z-render::cx*x || R<render::sx*z+render::cx*x)
|
|
continue;
|
|
|
|
// else if all inside outer volume then render normally
|
|
else if (R<render::Near-z && R<z-render::Far &&
|
|
R<-render::Sy*z-render::Cy*y && R<-render::Sy*z+render::Cy*y &&
|
|
R<-render::Sx*z+render::Cx*x && R<-render::Sx*z-render::Cx*x)
|
|
{
|
|
vu1_flags = PROJ;
|
|
}
|
|
|
|
// otherwise cull at the vertex level
|
|
else
|
|
{
|
|
vu1_flags = CULL;
|
|
}
|
|
|
|
// wireframe mode
|
|
if (pInstance->IsWireframe())
|
|
{
|
|
vu1_flags |= WIRE;
|
|
}
|
|
|
|
p_group->Used[render::Field] = true; // Note, should have culling before here
|
|
|
|
dma::BeginTag(dma::cnt, 0);
|
|
|
|
|
|
// adjust matrix for 0:8 fixed point weights format (used to be 1:15)
|
|
RootToCamera[0] *= 128.0f;
|
|
RootToCamera[1] *= 128.0f;
|
|
RootToCamera[2] *= 128.0f;
|
|
|
|
// send vu1 context
|
|
RootToFrustum = RootToCamera * render::CameraToFrustum;
|
|
RootToViewport = RootToFrustum * render::FrustumToIntViewport;
|
|
|
|
CLightGroup *pLightGroup = pInstance->GetLightGroup();
|
|
|
|
if (pLightGroup)
|
|
{
|
|
const Mth::Vector &light_dir0 = pLightGroup->GetDirection(0);
|
|
const Mth::Vector &light_dir1 = pLightGroup->GetDirection(1);
|
|
const Mth::Vector &light_dir2 = pLightGroup->GetDirection(2);
|
|
|
|
LightMatrix[0][0] = light_dir0[0];
|
|
LightMatrix[1][0] = light_dir0[1];
|
|
LightMatrix[2][0] = light_dir0[2];
|
|
LightMatrix[0][1] = light_dir1[0];
|
|
LightMatrix[1][1] = light_dir1[1];
|
|
LightMatrix[2][1] = light_dir1[2];
|
|
LightMatrix[0][2] = light_dir2[0];
|
|
LightMatrix[1][2] = light_dir2[1];
|
|
LightMatrix[2][2] = light_dir2[2];
|
|
} else {
|
|
const Mth::Vector &light_dir0 = CLightGroup::sGetDefaultDirection(0);
|
|
const Mth::Vector &light_dir1 = CLightGroup::sGetDefaultDirection(1);
|
|
const Mth::Vector &light_dir2 = CLightGroup::sGetDefaultDirection(2);
|
|
|
|
LightMatrix[0][0] = light_dir0[0];
|
|
LightMatrix[1][0] = light_dir0[1];
|
|
LightMatrix[2][0] = light_dir0[2];
|
|
LightMatrix[0][1] = light_dir1[0];
|
|
LightMatrix[1][1] = light_dir1[1];
|
|
LightMatrix[2][1] = light_dir1[2];
|
|
LightMatrix[0][2] = light_dir2[0];
|
|
LightMatrix[1][2] = light_dir2[1];
|
|
LightMatrix[2][2] = light_dir2[2];
|
|
}
|
|
|
|
Mth::Matrix temp = *(Mth::Matrix *)pInstance->GetTransform();
|
|
temp[0].Normalize(); // do something sensible with a scaled matrix
|
|
temp[1].Normalize();
|
|
temp[2].Normalize();
|
|
LightMatrix = temp * LightMatrix;
|
|
|
|
if (pLightGroup)
|
|
{
|
|
ambientColour = pLightGroup->GetAmbientColor();
|
|
diffuseColour0 = pLightGroup->GetDiffuseColor(0);
|
|
diffuseColour1 = pLightGroup->GetDiffuseColor(1);
|
|
diffuseColour2 = pLightGroup->GetDiffuseColor(2);
|
|
}
|
|
else
|
|
{
|
|
ambientColour = CLightGroup::sGetDefaultAmbientColor();
|
|
diffuseColour0 = CLightGroup::sGetDefaultDiffuseColor(0);
|
|
diffuseColour1 = CLightGroup::sGetDefaultDiffuseColor(1);
|
|
diffuseColour2 = CLightGroup::sGetDefaultDiffuseColor(2);
|
|
}
|
|
|
|
// apply instance colour
|
|
|
|
#ifdef USE_COLOR_PER_MESH
|
|
if (pInstance->HasColorPerMaterial())
|
|
{
|
|
// Get first color for now
|
|
colour = pInstance->GetMaterialColorByIndex(0);
|
|
}
|
|
else
|
|
{
|
|
colour = pInstance->GetColor();
|
|
}
|
|
#else
|
|
colour = pInstance->GetColor();
|
|
#endif
|
|
colourComponent = (float)(colour & 0xFF) * 0.0078125f;
|
|
colouredAmbientColour[0] = ambientColour[0] * colourComponent;
|
|
colouredDiffuseColour0[0] = diffuseColour0[0] * colourComponent;
|
|
colouredDiffuseColour1[0] = diffuseColour1[0] * colourComponent;
|
|
colouredDiffuseColour2[0] = diffuseColour2[0] * colourComponent;
|
|
colourComponent = (float)(colour>>8 & 0xFF) * 0.0078125f;
|
|
colouredAmbientColour[1] = ambientColour[1] * colourComponent;
|
|
colouredDiffuseColour0[1] = diffuseColour0[1] * colourComponent;
|
|
colouredDiffuseColour1[1] = diffuseColour1[1] * colourComponent;
|
|
colouredDiffuseColour2[1] = diffuseColour2[1] * colourComponent;
|
|
colourComponent = (float)(colour>>16 & 0xFF) * 0.0078125f;
|
|
colouredAmbientColour[2] = ambientColour[2] * colourComponent;
|
|
colouredDiffuseColour0[2] = diffuseColour0[2] * colourComponent;
|
|
colouredDiffuseColour1[2] = diffuseColour1[2] * colourComponent;
|
|
colouredDiffuseColour2[2] = diffuseColour2[2] * colourComponent;
|
|
|
|
LightMatrix[3] = colouredAmbientColour * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[0] = colouredDiffuseColour0 * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[1] = colouredDiffuseColour1 * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[2] = colouredDiffuseColour2 * 1.0f/(128.0f*8388608.0f);
|
|
|
|
// vu context data
|
|
vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
|
|
|
|
// vu1::StoreVec(*(Vec *)&render::InverseIntViewportScale); // VF10
|
|
// vu1::StoreVec(*(Vec *)&render::InverseIntViewportOffset); // VF11
|
|
vu1::CopyQuads((uint32*)&render::InverseIntViewportScale,
|
|
(uint32*)&render::InverseIntViewportOffset);
|
|
|
|
vu1::StoreMat(*(Mat *)&RootToFrustum); // VF12-15
|
|
vu1::StoreMat(*(Mat *)&RootToViewport); // VF16-19
|
|
vu1::StoreMat(*(Mat *)&LightMatrix); // VF20-23
|
|
vu1::StoreMat(*(Mat *)&ColourMatrix); // VF24-27
|
|
vu1::EndPrim(0);
|
|
|
|
// gs context data, specifically the fog coefficient
|
|
float w,f;
|
|
w = -RootToCamera[3][2];
|
|
if ((w > FogNear) && EnableFog) // Garrett: We have to check for EnableFog here because the VU1 code isn't
|
|
{
|
|
f = 1.0 + EffectiveFogAlpha * (FogNear/w - 1.0f);
|
|
}
|
|
else
|
|
{
|
|
f = 1.0f;
|
|
}
|
|
gs::BeginPrim(ABS,0,0);
|
|
gs::Reg1(gs::FOG, PackFOG((int)(f*255.99f)));
|
|
//gs::Reg1(gs::FOGCOL, PackFOGCOL(0x60,0x40,0xC0));
|
|
gs::EndPrim(1);
|
|
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
|
|
// get free run of the VUMem
|
|
vif::FLUSHA();
|
|
|
|
dma::EndTag();
|
|
|
|
// send transforms
|
|
vu1::Loc = 0;
|
|
dma::Tag(dma::ref, pInstance->GetNumBones()<<2, (uint)pInstance->GetBoneTransforms());
|
|
vif::NOP();
|
|
vif::UNPACK(0,V4_32,pInstance->GetNumBones()<<2,ABS,SIGNED,0);
|
|
|
|
dma::BeginTag(dma::cnt, 0);
|
|
|
|
vif::ITOP(pInstance->GetNumBones()<<2);
|
|
vif::MSCAL(VU1_ADDR(ReformatXforms));
|
|
|
|
// not using pre-translate
|
|
// don't forget to switch off the offset mode on xyz unpack if using the row reg for something else
|
|
vif::STROW(0,0,0,0);
|
|
|
|
dma::EndTag();
|
|
|
|
// traverse colour-unlocked meshes
|
|
for (j=0,pMesh=p_group->pMeshes; j<p_group->NumMeshes; j++,pMesh++)
|
|
{
|
|
// skip if invisible, no subroutine, or not affected by dynamic colouring
|
|
if (!pMesh->IsActive() || !pMesh->pSubroutine || (pMesh->Flags & MESHFLAG_COLOR_LOCKED))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifdef USE_COLOR_PER_MESH
|
|
uint32 mesh_color;
|
|
// Garrett: Thought I had the material color array in the right order, but the render order
|
|
// is different. Right now I'm just searching out each color, but I'll need to go back
|
|
// and see if it is possible to put the color array in render order (i.e. is the render order
|
|
// static).
|
|
if (pInstance->HasColorPerMaterial() && (colour != (mesh_color = pInstance->GetMaterialColor(pMesh->MaterialName, pMesh->Pass))))
|
|
{
|
|
colour = mesh_color;
|
|
colourComponent = (float)(colour & 0xFF) * 0.0078125f;
|
|
colouredAmbientColour[0] = ambientColour[0] * colourComponent;
|
|
colouredDiffuseColour0[0] = diffuseColour0[0] * colourComponent;
|
|
colouredDiffuseColour1[0] = diffuseColour1[0] * colourComponent;
|
|
colouredDiffuseColour2[0] = diffuseColour2[0] * colourComponent;
|
|
colourComponent = (float)(colour>>8 & 0xFF) * 0.0078125f;
|
|
colouredAmbientColour[1] = ambientColour[1] * colourComponent;
|
|
colouredDiffuseColour0[1] = diffuseColour0[1] * colourComponent;
|
|
colouredDiffuseColour1[1] = diffuseColour1[1] * colourComponent;
|
|
colouredDiffuseColour2[1] = diffuseColour2[1] * colourComponent;
|
|
colourComponent = (float)(colour>>16 & 0xFF) * 0.0078125f;
|
|
colouredAmbientColour[2] = ambientColour[2] * colourComponent;
|
|
colouredDiffuseColour0[2] = diffuseColour0[2] * colourComponent;
|
|
colouredDiffuseColour1[2] = diffuseColour1[2] * colourComponent;
|
|
colouredDiffuseColour2[2] = diffuseColour2[2] * colourComponent;
|
|
|
|
LightMatrix[3] = colouredAmbientColour * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[0] = colouredDiffuseColour0 * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[1] = colouredDiffuseColour1 * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[2] = colouredDiffuseColour2 * 1.0f/(128.0f*8388608.0f);
|
|
|
|
// change lighting context
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vif::BASE(256);
|
|
vif::OFFSET(0);
|
|
vu1::Loc = 256;
|
|
vu1::BeginPrim(ABS, VU1_ADDR(L_VF20));
|
|
vu1::StoreMat(*(Mat *)&LightMatrix); // VF20-23
|
|
vu1::StoreMat(*(Mat *)&ColourMatrix); // VF24-27
|
|
vu1::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(ParseInit));
|
|
vif::FLUSH();
|
|
dma::EndTag();
|
|
}
|
|
#endif // USE_COLOR_PER_MESH
|
|
|
|
// render the mesh
|
|
vu1::Loc = vu1::Buffer = 256;
|
|
dma::BeginTag(dma::call,(uint)pMesh->pSubroutine);
|
|
vif::BASE(256);
|
|
vif::OFFSET(0);
|
|
vif::MSCAL(VU1_ADDR(Setup));
|
|
vif::ITOP(vu1_flags);
|
|
vif::FLUSH();
|
|
dma::EndTag();
|
|
vu1::Loc += ((uint16 *)pMesh->pSubroutine)[1];
|
|
}
|
|
|
|
|
|
// change lighting context
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vif::BASE(256);
|
|
vif::OFFSET(0);
|
|
vu1::Loc = 256;
|
|
LightMatrix[3] = ambientColour * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[0] = diffuseColour0 * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[1] = diffuseColour1 * 1.0f/(128.0f*8388608.0f);
|
|
ColourMatrix[2] = diffuseColour2 * 1.0f/(128.0f*8388608.0f);
|
|
vu1::BeginPrim(ABS, VU1_ADDR(L_VF20));
|
|
vu1::StoreMat(*(Mat *)&LightMatrix); // VF20-23
|
|
vu1::StoreMat(*(Mat *)&ColourMatrix); // VF24-27
|
|
vu1::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(ParseInit));
|
|
vif::FLUSH();
|
|
dma::EndTag();
|
|
|
|
// traverse colour-locked meshes
|
|
for (j=0,pMesh=p_group->pMeshes; j<p_group->NumMeshes; j++,pMesh++)
|
|
{
|
|
// skip if invisible, no subroutine, or affected by dynamic colouring
|
|
if (!pMesh->IsActive() || !pMesh->pSubroutine || !(pMesh->Flags & MESHFLAG_COLOR_LOCKED))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// render the mesh
|
|
vu1::Loc = vu1::Buffer = 256;
|
|
dma::BeginTag(dma::call,(uint)pMesh->pSubroutine);
|
|
vif::BASE(256);
|
|
vif::OFFSET(0);
|
|
vif::MSCAL(VU1_ADDR(Setup));
|
|
vif::ITOP(vu1_flags);
|
|
vif::FLUSH();
|
|
dma::EndTag();
|
|
vu1::Loc += ((uint16 *)pMesh->pSubroutine)[1];
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#if STENCIL_SHADOW
|
|
// postprocess stencil buffer
|
|
dma::SetList(sGroup::pShadow);
|
|
|
|
// send the GS setup
|
|
dma::BeginTag(dma::cnt, 0);
|
|
gs::BeginPrim(ABS,0,0);
|
|
|
|
gs::Reg1(gs::TEXFLUSH, 0);
|
|
|
|
gs::Reg1(gs::COLCLAMP, PackCOLCLAMP(1));
|
|
gs::Reg1(gs::TEST_1, PackTEST(0,0,0,0,0,0,1,ZALWAYS));
|
|
gs::Reg1(gs::TEX0_1, PackTEX0(0x3740,HRES/64,PSMCT16S,10,10,1,DECAL,0,0,0,0,0));
|
|
gs::Reg1(gs::TEX1_1, PackTEX1(1,0,0,0,0,0,0));
|
|
gs::Reg1(gs::ALPHA_1, PackALPHA(2,1,0,1,0));
|
|
gs::Reg1(gs::TEXA, PackTEXA(ShadowDensity, 1, ShadowDensity));
|
|
gs::Reg1(gs::ZBUF_1, PackZBUF(ZBUFFER_START,PSMZ24,1));
|
|
|
|
gs::Reg1(gs::PRIM, PackPRIM(SPRITE,0,1,0,1,0,1,0,0));
|
|
gs::Reg1(gs::UV, PackUV(0x0008,0x0008));
|
|
gs::Reg1(gs::XYZ2, PackXYZ(XOFFSET,YOFFSET,0));
|
|
gs::Reg1(gs::UV, PackUV((HRES<<4)+8, (VRES<<4)+8));
|
|
gs::Reg1(gs::XYZ2, PackXYZ(XOFFSET+(HRES<<4), YOFFSET+(VRES<<4), 0));//0x10000-XOFFSET,0x10000-YOFFSET,0));
|
|
|
|
gs::Reg1(gs::TEXA, PackTEXA(0x00,0,0x80));
|
|
gs::Reg1(gs::ZBUF_1, PackZBUF(ZBUFFER_START,PSMZ24,0));
|
|
|
|
gs::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
dma::EndTag();
|
|
#endif
|
|
|
|
|
|
|
|
// assume no dma context
|
|
dma::SetList(NULL);
|
|
|
|
}
|
|
|
|
void patch_texture_dma()
|
|
{
|
|
Nx::CScene *p_nxscene = Nx::CEngine::sGetMainScene();
|
|
if (p_nxscene)
|
|
{
|
|
Nx::CPs2TexDict *p_tex_dict = (Nx::CPs2TexDict *)p_nxscene->GetTexDict();
|
|
if (p_tex_dict)
|
|
{
|
|
sScene *p_scene = p_tex_dict->GetEngineTextureDictionary();
|
|
|
|
|
|
sTexture *p_textures = p_scene->pTextures;
|
|
int num_textures = p_scene->NumTextures;
|
|
|
|
|
|
// int total_referenced = 0;
|
|
// int num_unique = 0;
|
|
int i;
|
|
for (i=0;i<num_textures;i++)
|
|
{
|
|
|
|
uint32* p_dma = (uint32*)p_textures[i].mp_dma;
|
|
|
|
// gif::Tag2(0, 0, IMAGE, 0, 0, 0, NumTexQWords[j]); // 0..3 words, NumTexQWords is in the lower 15 bits of the first word [0]
|
|
// dma::EndTag(); // Just Aligns upo to QW boundry
|
|
// dma::Tag(dma::ref, NumTexQWords[j], (uint)pTextureSource); // 0..1 (after alignment) NumTexQWords goes in the lower 28 bits of word 0
|
|
|
|
if (p_textures[i].m_render_count)
|
|
{
|
|
// Reset it for next time around
|
|
p_textures[i].m_render_count = 0;
|
|
// fix it, so it gets uploaded
|
|
// This test does speed up the routine by about 20%
|
|
// Generally the visibility does not change
|
|
uint32 current_value = (p_dma[0]);
|
|
if (current_value == 0)
|
|
{
|
|
p_dma[0] = p_textures[i].m_quad_words;;
|
|
uint32* p_dma2 = (uint32*)((((int)(p_dma+4)) + 3)&0xfffffff0);
|
|
p_dma2[0] = (dma::ref << 28) | p_textures[i].m_quad_words;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// patch it, so nothing gets uploaded
|
|
uint32 current_value = (p_dma[0]);
|
|
if (current_value != 0)
|
|
{
|
|
p_dma[0] = 0;
|
|
uint32* p_dma2 = (uint32*)((((int)(p_dma+4)) + 3)&0xfffffff0);
|
|
p_dma2[0] = (dma::ref << 28);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void render::NewStyle(bool just_setup)
|
|
{
|
|
|
|
sGroup *p_group;
|
|
|
|
// add viewport-specific GS registers to each new-style group
|
|
for (p_group=sGroup::pHead; p_group!=sGroup::pEpilogue; p_group=p_group->pNext)
|
|
{
|
|
if ((p_group!=sGroup::pShadow) && p_group->pScene && (p_group->pScene->Flags & SCENEFLAG_USESPIP))
|
|
{
|
|
// send the GS viewport context
|
|
dma::SetList(p_group);
|
|
|
|
// mark our place if it's a (the?) sorted group
|
|
// remember to assert there's no overflow
|
|
if (p_group->flags & GROUPFLAG_SORT)
|
|
{
|
|
//printf("Marking location %08X, p_group==%08X, flags=%08X\n", (int)dma::pLoc, (int)p_group, p_group->flags);
|
|
Dbg_MsgAssert(sMarkerIndex < MAX_SORTED_LIST_MARKERS, ("too many sorted list markers - tell Mike"));
|
|
render::sSortedListMarker[render::sMarkerIndex++] = (int)dma::pLoc;
|
|
}
|
|
|
|
dma::BeginTag(dma::cnt, 0xFE000000);
|
|
vif::BASE(vu1::Loc);
|
|
vif::OFFSET(0);
|
|
uint vu1_loc = vu1::Loc;
|
|
vu1::Loc = 0; // must do this as a relative prim for a sortable list...
|
|
|
|
// constant part of vu1 context data
|
|
vu1::BeginPrim(REL, VU1_ADDR(L_VF09));
|
|
vu1::StoreVec(*(Vec *)&render::AltFrustum); // VF09
|
|
if (p_group->flags & GROUPFLAG_SKY)
|
|
{
|
|
vu1::StoreVec(*(Vec *)&render::SkyInverseViewportScale); // VF10
|
|
vu1::StoreVec(*(Vec *)&render::SkyInverseViewportOffset); // VF11
|
|
}
|
|
else
|
|
{
|
|
vu1::StoreVec(*(Vec *)&render::InverseViewportScale); // VF10
|
|
vu1::StoreVec(*(Vec *)&render::InverseViewportOffset); // VF11
|
|
}
|
|
vu1::EndPrim(0);
|
|
|
|
gs::BeginPrim(REL, 0, 0);
|
|
gs::Reg1(gs::XYOFFSET_1, render::reg_XYOFFSET);
|
|
gs::Reg1(gs::SCISSOR_1, render::reg_SCISSOR);
|
|
gs::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
dma::EndTag();
|
|
((uint16 *)dma::pTag)[1] |= vu1::Loc & 0x3FF; // must write some code for doing this automatically
|
|
vu1::Loc += vu1_loc;
|
|
|
|
if (p_group->flags & GROUPFLAG_SORT)
|
|
{
|
|
dma::Tag(dma::cnt,0,0);
|
|
vif::NOP();
|
|
vif::NOP();
|
|
}
|
|
|
|
// assume no vu1 context for each group
|
|
p_group->pVu1Context = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
// reserve space from dma list for new trans context
|
|
dma::SetList(NULL);
|
|
//CVu1Context *p_ctxt = new(dma::pLoc) CVu1Context;
|
|
CVu1Context *p_ctxt = (CVu1Context *)dma::pLoc;
|
|
//dma::pLoc += sizeof(CVu1Context);
|
|
dma::pLoc += (STD_CTXT_SIZE+7)*16;
|
|
p_ctxt->Init();
|
|
Mth::Matrix I;
|
|
I.Ident();
|
|
p_ctxt->StandardSetup(I);
|
|
|
|
// shadow stuff
|
|
sGroup::pShadow->Used[render::Field] = true;
|
|
|
|
#if !STENCIL_SHADOW
|
|
|
|
// while the dma context is null, set up an empty upload for the shadow group
|
|
sGroup::pShadow->pUpload[render::Field] = dma::pLoc;
|
|
dma::Tag(dma::end, 0, 0);
|
|
vif::NOP();
|
|
vif::NOP();
|
|
|
|
// ...and also a vu1 context for the shadow group
|
|
CVu1Context *p_shdw_ctxt = new(dma::pLoc) CVu1Context(*p_ctxt);
|
|
dma::pLoc += sizeof(CVu1Context);
|
|
p_shdw_ctxt->InitExtended();
|
|
p_shdw_ctxt->SetShadowVecs(SkaterY);
|
|
|
|
// add a z-push
|
|
p_shdw_ctxt->AddZPush(16.0f);
|
|
|
|
|
|
dma::SetList(sGroup::pShadow);
|
|
|
|
// initial cnt tag, for sending row reg and z-sort key
|
|
Mth::Vector *p_trans = p_shdw_ctxt->GetTranslation();
|
|
dma::Tag(dma::cnt, 1, 0);
|
|
vif::NOP();
|
|
vif::STROW((int)(*p_trans)[0], (int)(*p_trans)[1], (int)(*p_trans)[2], 0);
|
|
|
|
// send transform context
|
|
dma::Tag(dma::ref, (EXT_CTXT_SIZE+2)|(EXT_CTXT_SIZE+1)<<16, p_shdw_ctxt->GetDma());
|
|
vif::BASE(vu1::Loc);
|
|
vif::OFFSET(0);
|
|
vu1::Loc += EXT_CTXT_SIZE+1;
|
|
sGroup::pShadow->pVu1Context = p_shdw_ctxt;
|
|
|
|
// send the GS context for the shadow list
|
|
dma::BeginTag(dma::cnt, 0);
|
|
|
|
vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
|
|
vu1::StoreVec(*(Vec *)&render::InverseViewportScale); // VF10
|
|
vu1::StoreVec(*(Vec *)&render::InverseViewportOffset); // VF11
|
|
vu1::EndPrim(0);
|
|
|
|
gs::BeginPrim(ABS,0,0);
|
|
gs::Reg1(gs::TEXFLUSH, PackTEXFLUSH(0));
|
|
gs::Reg1(gs::FBA_2, PackFBA(0));
|
|
gs::Reg1(gs::TEX0_2, PackTEX0(0x3E00,4,PSMCT16,8,8,1,DECAL,0,0,0,0,0));
|
|
gs::Reg1(gs::TEX1_2, PackTEX1(1,0,NEAREST,NEAREST,0,0,0));
|
|
gs::Reg1(gs::CLAMP_2, PackCLAMP(CLAMP,CLAMP,0,0,0,0));
|
|
gs::Reg1(gs::FRAME_2, PackFRAME(FRAME_START,HRES/64,PSMCT32,0xFF000000));
|
|
gs::Reg1(gs::ZBUF_2, PackZBUF(ZBUFFER_START,PSMZ24,1));
|
|
gs::Reg1(gs::XYOFFSET_2, reg_XYOFFSET);
|
|
gs::Reg1(gs::SCISSOR_2, reg_SCISSOR);
|
|
gs::Reg1(gs::ALPHA_2, PackALPHA(2,1,2,1,ShadowDensity));
|
|
gs::Reg1(gs::TEST_2, PackTEST(1,AGEQUAL,0x80,KEEP,0,0,1,ZGEQUAL));
|
|
gs::Reg1(gs::PRMODECONT, PackPRMODECONT(0));
|
|
gs::Reg1(gs::PRMODE, PackPRMODE(0,1,0,1,0,0,1,0));
|
|
gs::EndPrim(0);
|
|
dma::EndTag();
|
|
#endif
|
|
|
|
|
|
geom_stats_total=0;
|
|
geom_stats_inactive =0;
|
|
geom_stats_sky=0;
|
|
geom_stats_transformed=0;
|
|
geom_stats_skeletal=0;
|
|
geom_stats_camera_sphere=0;
|
|
geom_stats_clipcull=0;
|
|
geom_stats_culled=0;
|
|
geom_stats_leaf_culled=0;
|
|
geom_stats_clipcull=0;
|
|
geom_stats_boxcheck=0;
|
|
geom_stats_occludecheck=0;
|
|
geom_stats_occluded=0;
|
|
geom_stats_colored=0;
|
|
geom_stats_leaf=0;
|
|
geom_stats_wibbleUV=0;
|
|
geom_stats_wibbleVC=0;
|
|
geom_stats_sendcontext=0;
|
|
geom_stats_sorted=0;
|
|
geom_stats_shadow=0;
|
|
|
|
// render the world
|
|
if (!just_setup)
|
|
{
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
const int span = 60;
|
|
static int avg[span];
|
|
static int slot = 0;
|
|
Tmr::CPUCycles start = Tmr::GetTimeInCPUCycles();
|
|
#endif
|
|
|
|
#if STENCIL_SHADOW
|
|
CGeomNode::sWorld.RenderWorld(*p_ctxt, RENDERFLAG_CLIP);
|
|
#else
|
|
CGeomNode::sWorld.RenderWorld(*p_ctxt, RENDERFLAG_CLIP | RENDERFLAG_SHADOW);
|
|
#endif
|
|
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
Tmr::CPUCycles len = Tmr::GetTimeInCPUCycles() - start;
|
|
int ticks = len;
|
|
avg[slot++] = ticks;
|
|
if (slot == span) slot = 0;
|
|
int tot = 0;
|
|
for (int a=0;a<span;a++)
|
|
{
|
|
tot += avg[a];
|
|
}
|
|
tot/=span;
|
|
// printf ("%9d %9d\n",tot,ticks); // printf average, and the last frame
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
printf ("\ngeom_stats_total = %d\n", geom_stats_total);
|
|
printf ("geom_stats_inactive = %d\n", geom_stats_inactive );
|
|
printf ("geom_stats_sky = %d\n", geom_stats_sky);
|
|
printf ("geom_stats_transformed = %d\n", geom_stats_transformed);
|
|
printf ("geom_stats_skeletal = %d\n", geom_stats_skeletal);
|
|
printf ("geom_stats_camera_sphere = %d\n", geom_stats_camera_sphere);
|
|
printf ("geom_stats_clipcull = %d\n", geom_stats_clipcull);
|
|
printf ("geom_stats_boxcheck = %d\n", geom_stats_boxcheck);
|
|
printf ("geom_stats_occludecheck = %d\n", geom_stats_occludecheck);
|
|
printf ("geom_stats_occluded = %d\n", geom_stats_occluded);
|
|
printf ("geom_stats_colored = %d\n", geom_stats_colored);
|
|
printf ("geom_stats_leaf = %d\n", geom_stats_leaf);
|
|
printf ("geom_stats_wibbleUV = %d\n", geom_stats_wibbleUV);
|
|
printf ("geom_stats_wibbleVC = %d\n", geom_stats_wibbleVC);
|
|
printf ("geom_stats_sendcontext = %d\n", geom_stats_sendcontext);
|
|
printf ("geom_stats_sorted = %d\n", geom_stats_sorted);
|
|
printf ("geom_stats_shadow = %d\n", geom_stats_shadow);
|
|
#endif
|
|
|
|
|
|
#if !STENCIL_SHADOW
|
|
// undo the havoc caused by the shadow group
|
|
dma::SetList(sGroup::pShadow);
|
|
dma::BeginTag(dma::cnt, 0);
|
|
gs::BeginPrim(ABS,0,0);
|
|
gs::Reg1(gs::PRMODECONT, PackPRMODECONT(1));
|
|
gs::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
dma::EndTag();
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if !STENCIL_SHADOW
|
|
void SetTextureProjectionCamera( Mth::Vector *p_pos, Mth::Vector *p_at )
|
|
{
|
|
// sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->GetItem((uint32)p_texture );
|
|
// if( p_details )
|
|
{
|
|
render::ShadowCameraPosition[0] = p_pos->GetX();
|
|
render::ShadowCameraPosition[1] = p_pos->GetY();
|
|
render::ShadowCameraPosition[2] = p_pos->GetZ();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Added by Mick
|
|
// quick determination of if something is visible or not
|
|
// uses the previously calculated s and c vectors
|
|
// and the WorldToCamera transform
|
|
// (note, no attempt is made to ensure this is the same camera that the object
|
|
// will eventually be rendered with)
|
|
bool IsVisible(Mth::Vector ¢er, float radius)
|
|
{
|
|
|
|
|
|
float x,y,z;
|
|
Mth::Vector v(center);
|
|
|
|
v[3] = 1.0f; // needed?
|
|
v *= render::WorldToCamera;
|
|
x = v[0];
|
|
y = v[1];
|
|
z = v[2];
|
|
|
|
// if all outside view volume then cull
|
|
if (radius<z-render::Near || radius<render::Far-z ||
|
|
radius<render::sy*z+render::cy*y || radius<render::sy*z-render::cy*y ||
|
|
radius<render::sx*z-render::cx*x || radius<render::sx*z+render::cx*x)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// return true;
|
|
|
|
if( TestSphereAgainstOccluders( ¢er, radius ))
|
|
{
|
|
// Occluded.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// The same function, but without occlusion
|
|
bool IsInFrame(Mth::Vector ¢er, float radius)
|
|
{
|
|
|
|
float x,y,z;
|
|
Mth::Vector v(center);
|
|
|
|
v[3] = 1.0f; // needed?
|
|
v *= render::WorldToCamera;
|
|
x = v[0];
|
|
y = v[1];
|
|
z = v[2];
|
|
|
|
// if all outside view volume then cull
|
|
if (radius<z-render::Near || radius<render::Far-z ||
|
|
radius<render::sy*z+render::cy*y || radius<render::sy*z-render::cy*y ||
|
|
radius<render::sx*z-render::cx*x || radius<render::sx*z+render::cx*x)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void RenderMesh(sMesh *pMesh, uint RenderFlags)
|
|
{
|
|
dma::BeginTag(dma::call,(uint)pMesh->pSubroutine);
|
|
vif::BASE(vu1::Loc);
|
|
vif::OFFSET(0);
|
|
vif::ITOP(RenderFlags);
|
|
dma::EndTag();
|
|
vu1::Loc += ((uint16 *)(pMesh->pSubroutine))[1];
|
|
}
|
|
|
|
|
|
void RenderEpilogue(void)
|
|
{
|
|
sGroup *p_group;
|
|
|
|
// ---------------------------------------------
|
|
// tag onto end of particle group
|
|
dma::SetList(sGroup::pParticles);
|
|
|
|
// restore vu1 code (and buffering scheme) at end of particles group
|
|
dma::Tag(dma::ref, ((uint8 *)&MPGEnd-(uint8 *)&MPGStart+15)/16, (uint)&MPGStart);
|
|
vif::NOP();
|
|
vif::NOP();
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vif::FLUSH();
|
|
vif::STMASK(0);
|
|
vif::STMOD(0);
|
|
vif::STCYCL(1,1);
|
|
vif::BASE(0);
|
|
vif::OFFSET(0);
|
|
vif::MSCAL(VU1_ADDR(Setup));
|
|
vu1::Loc = vu1::Buffer = 0;
|
|
dma::EndTag();
|
|
|
|
dma::SetList(NULL);
|
|
// ---------------------------------------------
|
|
|
|
// add a GS SIGNAL primitive to each group
|
|
for (p_group=sGroup::pHead; p_group; p_group=p_group->pNext)
|
|
{
|
|
// end the dma list
|
|
dma::EndList(p_group);
|
|
}
|
|
|
|
dma::SetList(NULL);
|
|
|
|
// epilogue VRAM upload (fonts and sprites)
|
|
|
|
// Upload the fonts, so we can draw 2D text on screen
|
|
sGroup::pEpilogue->pUpload[render::Field] = dma::pLoc;
|
|
uint32 *p = (uint32*)(dma::pLoc);
|
|
for (SFont *pFont=pFontList; pFont; pFont=pFont->pNext)
|
|
{
|
|
// Mick: we build this upload list directly, to cut down on the number of writes
|
|
*p++ = (dma::ref << 28) + ((pFont->VifSize+15)>>4); // dma::Tag(dma::ref, (pFont->VifSize+15)>>4, (uint)pFont->pVifData);
|
|
*p++ = (uint32) pFont->pVifData;
|
|
*p++ = 0; // vif::NOP();
|
|
*p++ = 0; // vif::NOP();
|
|
}
|
|
|
|
// Upload the "Single Textures", the 2D screen elements (sprites)
|
|
for (SSingleTexture *pTexture=SSingleTexture::sp_stexture_list; pTexture; pTexture=pTexture->mp_next)
|
|
{
|
|
if (pTexture->IsInVRAM())
|
|
{
|
|
*p++ = (dma::call << 28) + (0); // dma::Tag(dma::call, 0, (uint)pTexture->mp_VifData);
|
|
*p++ = (uint32) pTexture->mp_VifData;
|
|
*p++ = 0; // vif::NOP();
|
|
*p++ = 0; // vif::NOP();
|
|
}
|
|
}
|
|
dma::pLoc = (uint8*)p;
|
|
|
|
|
|
// (?) writing to TEXFLUSH will stall the gs until all the fonts and sprites
|
|
// have been uploaded.
|
|
// since we are not double buffering anything now, we have to wait until
|
|
// everything is in VRAM before we can start rendering any 2D
|
|
// this is a potential GS hiccup, but removing it would be tricky
|
|
// and possibly not worth the effort
|
|
// Garrett: Now the 2D uses both buffers at once.
|
|
|
|
dma::BeginTag(dma::end, 0);
|
|
vif::NOP();
|
|
vif::NOP();
|
|
gif::Tag2(gs::A_D,1,PACKED,0,0,1,1);
|
|
gs::Reg2(gs::TEXFLUSH, 0);
|
|
dma::EndTag();
|
|
|
|
|
|
// We need the scratchpad in SortGroup()
|
|
bool got_scratch = CSystemResources::sRequestResource(CSystemResources::vSCRATCHPAD_MEMORY);
|
|
Dbg_Assert(got_scratch);
|
|
|
|
// sort the dma lists of transparent groups
|
|
for (p_group=sGroup::pHead; p_group!=sGroup::pEpilogue; p_group=p_group->pNext)
|
|
{
|
|
if ((p_group->flags & GROUPFLAG_SORT) // Mick, most groups are not SORT, so this test first is the fastest
|
|
&& p_group!=sGroup::pShadow
|
|
&& p_group->pScene
|
|
&& (p_group->pScene->Flags & SCENEFLAG_USESPIP)
|
|
)
|
|
{
|
|
p_group->pRender[render::Field] = dma::SortGroup(p_group->pRender[render::Field]);
|
|
}
|
|
}
|
|
|
|
if (got_scratch)
|
|
{
|
|
CSystemResources::sFreeResource(CSystemResources::vSCRATCHPAD_MEMORY);
|
|
}
|
|
|
|
// swap epilogue and particle texture upload pointers (a cheat!)
|
|
uint8 *temp_ptr = sGroup::pParticles->pUpload[render::Field];
|
|
sGroup::pParticles->pUpload[render::Field] = sGroup::pEpilogue->pUpload[render::Field];
|
|
sGroup::pEpilogue->pUpload[render::Field] = temp_ptr;
|
|
|
|
int size = (int)dma::pLoc - (int)dma::pBase;
|
|
static int max = 0;
|
|
static int max2 = 0;
|
|
if (size > max) max = size;
|
|
if (size > max2) max2 = size;
|
|
static int tick = 0;
|
|
tick++;
|
|
tick &= 0x1f;
|
|
if (!tick)
|
|
{
|
|
// printf ("%6d, %6d, %6d (of %6d)\n",size,max2,max, dma::size);
|
|
max2 -= 50000;
|
|
}
|
|
|
|
Dbg_MsgAssert(DMAOverflowOK || size < (dma::size*98/100), ("DMA Buffer Overflow used %d\nShow screenshot to Mick (see .bmp saved, above)",size));
|
|
|
|
if (DMAOverflowOK)
|
|
{
|
|
DMAOverflowOK--;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void SendDisplayList(void)
|
|
{
|
|
|
|
// Mick: Mike added this just before he left, but did not check it in
|
|
// sceGsSyncPath(0, 0);
|
|
|
|
// patch up the DMA list
|
|
// Moved to StuffAfterGSFinished
|
|
// patch_texture_dma();
|
|
|
|
// Writeback data from D-Cache to Memory
|
|
FlushCache(WRITEBACK_DCACHE);
|
|
|
|
# ifdef __USE_PROFILER__
|
|
// Initial render group does not cause an interrupt
|
|
// so don't push context
|
|
// Sys::VUProfiler->PushContext( 80,80, 228 );
|
|
# endif // __USE_PROFILER__
|
|
// kick prologue render
|
|
*D1_QWC = 0; // must zero QWC because the first action will be to use current MADR & QWC
|
|
*D1_TADR = (uint)PrologueGroup.pRender[!render::Field]; // address of 1st tag
|
|
*D1_CHCR = 0x145; // start transfer, tte=1, chain mode, from memory
|
|
sceGsSyncPath(0, 0);
|
|
|
|
#if USE_INTERRUPTS
|
|
|
|
// use interrupts
|
|
sGroup::pUploadGroup = sGroup::pHead;
|
|
sGroup::pRenderGroup = sGroup::pUploadGroup;
|
|
|
|
|
|
// SingleRender = 0;
|
|
|
|
#if 0 // debugging code to force all gorups to be executed, even if empty
|
|
sGroup * p_group = (sGroup*)sGroup::pUploadGroup; // Skip prologue and sky
|
|
while (p_group->pNext) // all except the last one
|
|
{
|
|
p_group->Used[!render::Field] = 1;
|
|
p_group = p_group->pNext;
|
|
}
|
|
#endif
|
|
|
|
|
|
// Some debugging code to only render/upload a single selectable group
|
|
// so we can see what is taking all the time
|
|
if (SingleRender)
|
|
{
|
|
sGroup * p_group = (sGroup*)sGroup::pUploadGroup; // Skip prologue and sky
|
|
int g = 0;
|
|
while (p_group->pNext) // all except the last one
|
|
{
|
|
g++;
|
|
if (g > 2 && g != SingleRender)
|
|
{
|
|
p_group->Used[!render::Field] = 0;
|
|
}
|
|
p_group = p_group->pNext;
|
|
}
|
|
}
|
|
|
|
UploadStalled = 0;
|
|
RenderStalled = 1;
|
|
FlushCache(WRITEBACK_DCACHE);
|
|
|
|
# ifdef __USE_PROFILER__
|
|
Sys::DMAProfiler->PushContext( sGroup::pUploadGroup->profile_color );
|
|
# endif // __USE_PROFILER__
|
|
|
|
|
|
// kick off first upload, should trigger all the rest by interrupt
|
|
*D2_QWC = 0; // must zero QWC because the first action will be to use current MADR & QWC
|
|
*D2_TADR = (uint)sGroup::pUploadGroup->pUpload[!render::Field]; // address of 1st tag
|
|
*D2_CHCR = 0x105; // start transfer, tte=0, chain mode, from memory
|
|
|
|
|
|
|
|
#else
|
|
|
|
// iterate over groups, uploading textures then rendering meshes in each
|
|
// last group will be epilogue group
|
|
for (sGroup *p_group=sGroup::pHead; p_group!=sGroup::pEpilogue; p_group=p_group->pNext)
|
|
{
|
|
if (p_group->Used[!render::Field])
|
|
{
|
|
// kick dma to upload textures from this group
|
|
*D2_QWC = 0; // must zero QWC because the first action will be to use current MADR & QWC
|
|
*D2_TADR = (uint)p_group->pUpload[!render::Field]; // address of 1st tag
|
|
*D2_CHCR = 0x105; // start transfer, tte=0, chain mode, from memory
|
|
sceGsSyncPath(0, 0);
|
|
|
|
// kick dma to render scene
|
|
*D1_QWC = 0; // must zero QWC because the first action will be to use current MADR & QWC
|
|
*D1_TADR = (uint)p_group->pRender[!render::Field]; // address of 1st tag
|
|
*D1_CHCR = 0x145; // start transfer, tte=1, chain mode, from memory
|
|
sceGsSyncPath(0, 0);
|
|
}
|
|
}
|
|
|
|
// kick dma to upload textures from this group
|
|
*D2_QWC = 0; // must zero QWC because the first action will be to use current MADR & QWC
|
|
*D2_TADR = (uint)sGroup::pEpilogue->pUpload[!render::Field]; // address of 1st tag
|
|
*D2_CHCR = 0x105; // start transfer, tte=0, chain mode, from memory
|
|
sceGsSyncPath(0, 0);
|
|
|
|
// kick dma to render scene
|
|
*D1_QWC = 0; // must zero QWC because the first action will be to use current MADR & QWC
|
|
*D1_TADR = (uint)sGroup::pEpilogue->pRender[!render::Field]; // address of 1st tag
|
|
*D1_CHCR = 0x145; // start transfer, tte=1, chain mode, from memory
|
|
sceGsSyncPath(0, 0);
|
|
#endif
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
static bool sUpdate2DDma = false;
|
|
static bool sVRAMNeedsUpdating = false;
|
|
|
|
|
|
void Clear2DVRAM()
|
|
{
|
|
// Reset VRAM pointer
|
|
FontVramBase = FontVramStart;
|
|
|
|
// And set flag
|
|
sUpdate2DDma = true;
|
|
}
|
|
|
|
void VRAMNeedsUpdating()
|
|
{
|
|
sVRAMNeedsUpdating = true;
|
|
}
|
|
|
|
void Reallocate2DVRAM()
|
|
{
|
|
if (!sVRAMNeedsUpdating)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Reset VRAM pointer
|
|
Clear2DVRAM();
|
|
|
|
for (SFont *pFont=pFontList; pFont; pFont=pFont->pNext)
|
|
{
|
|
pFont->ReallocateVRAM();
|
|
}
|
|
|
|
for (SSingleTexture *pTexture=SSingleTexture::sp_stexture_list; pTexture; pTexture=pTexture->mp_next)
|
|
{
|
|
pTexture->ReallocateVRAM();
|
|
}
|
|
|
|
// And set flags
|
|
sUpdate2DDma = true;
|
|
sVRAMNeedsUpdating = false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
void Update2DDMA()
|
|
{
|
|
// Only go through the lists if it was requested
|
|
if (!sUpdate2DDma)
|
|
return;
|
|
|
|
// Go through each item
|
|
for (SFont *pFont=pFontList; pFont; pFont=pFont->pNext)
|
|
{
|
|
pFont->UpdateDMA();
|
|
}
|
|
|
|
for (SSingleTexture *pTexture=SSingleTexture::sp_stexture_list; pTexture; pTexture=pTexture->mp_next)
|
|
{
|
|
pTexture->UpdateDMA();
|
|
}
|
|
|
|
// Clear flag
|
|
sUpdate2DDma = false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if STENCIL_SHADOW
|
|
|
|
void CastStencilShadow(CInstance *pInstance, sGroup *p_group, Mth::Vector &ShadowVec, Mth::Vector &TweakVec)
|
|
{
|
|
sShadowVolumeHeader * p_shadow = pInstance->GetScene()->mp_shadow_volume_header;
|
|
if (!p_shadow)
|
|
return;
|
|
|
|
Mth::Matrix RootToFrustum,RootToViewport, WorldToRoot;
|
|
|
|
RootToFrustum = *(Mth::Matrix *)pInstance->GetTransform() * render::WorldToFrustum;
|
|
RootToViewport = RootToFrustum * render::FrustumToIntViewport;
|
|
|
|
WorldToRoot = *(Mth::Matrix *)pInstance->GetTransform();
|
|
WorldToRoot.Transpose();
|
|
ShadowVec *= WorldToRoot;
|
|
TweakVec *= WorldToRoot;
|
|
|
|
dma::SetList(sGroup::pShadow);
|
|
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vu1::BeginPrim(ABS, VU1_ADDR(L_VF14));
|
|
vu1::StoreVec(*(Vec *)&ShadowVec); // VF14, shadow vec
|
|
vu1::StoreVec(*(Vec *)&TweakVec); // VF15, tweak vec
|
|
vu1::StoreMat(*(Mat *)&RootToViewport); // VF16-19
|
|
vu1::EndPrim(1);
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
vif::FLUSH();
|
|
dma::EndTag();
|
|
|
|
// send transforms
|
|
vu1::Loc = 0;
|
|
dma::Tag(dma::ref, pInstance->GetNumBones()<<2, (uint)pInstance->GetBoneTransforms());
|
|
vif::STCYCL(1,1);
|
|
vif::UNPACK(0,V4_32,pInstance->GetNumBones()<<2,ABS,SIGNED,0);
|
|
|
|
// send data
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vif::STCYCL(7,21);
|
|
|
|
sShadowVertex * p_vertex = p_shadow->p_vertex;
|
|
sShadowConnect * p_connect = p_shadow->p_connect;
|
|
sShadowNeighbor * p_neighbor = p_shadow->p_neighbor;
|
|
|
|
int vert, num_faces;
|
|
for (uint32 i=0; i<p_shadow->num_faces; i++)
|
|
{
|
|
if (i%36==0)
|
|
{
|
|
vu1::Loc = 256;
|
|
vif::BASE(256);
|
|
vif::OFFSET(0);
|
|
vif::MSCAL(VU1_ADDR(Setup));
|
|
}
|
|
|
|
if (i%12==0)
|
|
{
|
|
num_faces = p_shadow->num_faces-i;
|
|
if (num_faces>12)
|
|
num_faces=12;
|
|
|
|
|
|
vif::UNPACK(0, V4_32, 7*num_faces, ABS, UNSIGNED, 0);
|
|
}
|
|
|
|
if ((i%12==11) || (i==p_shadow->num_faces-1))
|
|
{
|
|
vif::StoreV4_32(0x3480800A, 0x21224000, 0x00000051, 0x8000);
|
|
}
|
|
else
|
|
{
|
|
vif::StoreV4_32(0x3480800A, 0x21224000, 0x00000051, 0x0000);
|
|
}
|
|
|
|
uint32 * p32 = (uint32 *)dma::pLoc;
|
|
|
|
vert = p_connect[i].corner[0];
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].x));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].y));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].z));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].idx));
|
|
|
|
vert = p_connect[i].corner[1];
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].x));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].y));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].z));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].idx));
|
|
|
|
vert = p_connect[i].corner[2];
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].x));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].y));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].z));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].idx));
|
|
|
|
vert = p_neighbor[i].edge[0];
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].x));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].y));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].z));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].idx));
|
|
|
|
vert = p_neighbor[i].edge[1];
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].x));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].y));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].z));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].idx));
|
|
|
|
vert = p_neighbor[i].edge[2];
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].x));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].y));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].z));
|
|
*p32++ = *((uint32 *)(&p_vertex[vert].idx));
|
|
|
|
dma::pLoc = (uint8 *)p32;
|
|
|
|
vu1::Loc += 21;
|
|
|
|
if ((i%12 == 11) || (i==p_shadow->num_faces-1))
|
|
{
|
|
vif::MSCAL(VU1_ADDR(ShadowVolumeSkin));
|
|
}
|
|
}
|
|
|
|
dma::EndTag();
|
|
|
|
dma::SetList(p_group);
|
|
}
|
|
|
|
#else
|
|
|
|
void CastShadow(CInstance *pInstance, sGroup *p_group)
|
|
{
|
|
int j;
|
|
sMesh *pMesh;
|
|
Mth::Matrix ProjMat, temp;
|
|
|
|
// set up projection matrix
|
|
ProjMat = *pInstance->GetTransform();
|
|
SkaterY = ProjMat[3][1];
|
|
ProjMat[3].Set(-36.0f/**SUB_INCH_PRECISION*/*ProjMat[1][0],
|
|
-36.0f/**SUB_INCH_PRECISION*/*ProjMat[1][1],
|
|
-36.0f/**SUB_INCH_PRECISION*/*ProjMat[1][2], 1.0f);
|
|
temp[0].Set(32.0f/**RECIPROCAL_SUB_INCH_PRECISION*/, 0.0f, 0.0f, 0.0f);
|
|
temp[1].Set(0.0f, 0.0f, 0.0f, 0.0f);
|
|
temp[2].Set(0.0f, 32.0f/**RECIPROCAL_SUB_INCH_PRECISION*/, 0.0f, 0.0f);
|
|
temp[3].Set(8421376.0f, 8421376.0f, 0.0f, 1.0f);
|
|
ProjMat *= temp;
|
|
|
|
dma::SetList(sGroup::pShadow);
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));
|
|
vu1::StoreMat(*(Mat *)&ProjMat); // VF16-19
|
|
vu1::EndPrim(1);
|
|
|
|
vif::MSCAL(VU1_ADDR(Parser));
|
|
|
|
// not using pre-translate
|
|
// don't forget to switch off the offset mode on xyz unpack if using the row reg for somthing else
|
|
vif::STROW(0,0,0,0);
|
|
|
|
// get free run of the VUMem
|
|
vif::FLUSH();
|
|
|
|
dma::EndTag();
|
|
|
|
// send transforms
|
|
vu1::Loc = 0;
|
|
dma::Tag(dma::ref, pInstance->GetNumBones()<<2, (uint)pInstance->GetBoneTransforms());
|
|
vif::NOP();
|
|
vif::UNPACK(0,V4_32,pInstance->GetNumBones()<<2,ABS,SIGNED,0);
|
|
|
|
dma::Tag(dma::cnt, 0, 0);
|
|
vif::ITOP(pInstance->GetNumBones()<<2);
|
|
vif::MSCAL(VU1_ADDR(ReformatXforms));
|
|
|
|
// traverse array of meshes in the model
|
|
for (j=0,pMesh=p_group->pMeshes; j<p_group->NumMeshes; j++,pMesh++)
|
|
{
|
|
// skip if invisible
|
|
if (!pMesh->IsActive())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// do nothing if mesh doesn't have a dma subroutine
|
|
if (!pMesh->pSubroutine)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// render the mesh
|
|
vu1::Loc = vu1::Buffer = 256;
|
|
dma::BeginTag(dma::call,(uint)pMesh->pSubroutine);
|
|
vif::BASE(256);
|
|
vif::OFFSET(0);
|
|
vif::MSCAL(VU1_ADDR(Setup));
|
|
vif::ITOP(SHDW);
|
|
vif::FLUSH();
|
|
dma::EndTag();
|
|
vu1::Loc += ((uint16 *)pMesh->pSubroutine)[1];
|
|
}
|
|
|
|
dma::Tag(dma::cnt, 0, 0);
|
|
vif::FLUSH();
|
|
vif::NOP();
|
|
|
|
dma::SetList(p_group);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace render
|
|
{
|
|
Mth::Matrix WorldToCamera;
|
|
Mth::Matrix WorldToCameraRotation; // shouldn't need this
|
|
Mth::Matrix WorldToFrustum;
|
|
Mth::Matrix WorldToViewport;
|
|
Mth::Matrix WorldToIntViewport;
|
|
Mth::Matrix CameraToWorld;
|
|
Mth::Matrix CameraToWorldRotation;
|
|
Mth::Matrix CameraToFrustum;
|
|
Mth::Matrix CameraToViewport;
|
|
Mth::Matrix FrustumToViewport;
|
|
Mth::Matrix FrustumToIntViewport;
|
|
Mth::Matrix AdjustedWorldToCamera;
|
|
Mth::Matrix AdjustedWorldToFrustum;
|
|
Mth::Matrix AdjustedWorldToViewport;
|
|
Mth::Matrix AdjustedWorldToIntViewport;
|
|
Mth::Matrix SkyWorldToCamera;
|
|
Mth::Matrix SkyWorldToFrustum;
|
|
Mth::Matrix SkyWorldToViewport;
|
|
Mth::Matrix SkyFrustumToViewport;
|
|
|
|
Mth::Vector ViewportScale;
|
|
Mth::Vector ViewportOffset;
|
|
Mth::Vector IntViewportScale;
|
|
Mth::Vector IntViewportOffset;
|
|
Mth::Vector InverseViewportScale;
|
|
Mth::Vector InverseViewportOffset;
|
|
Mth::Vector InverseIntViewportScale;
|
|
Mth::Vector InverseIntViewportOffset;
|
|
Mth::Vector SkyViewportScale;
|
|
Mth::Vector SkyViewportOffset;
|
|
Mth::Vector SkyInverseViewportScale;
|
|
Mth::Vector SkyInverseViewportOffset;
|
|
Mth::Vector RowReg;
|
|
Mth::Vector RowRegI;
|
|
Mth::Vector RowRegF;
|
|
#if !STENCIL_SHADOW
|
|
Mth::Vector ShadowCameraPosition;
|
|
#endif
|
|
Mth::Vector AltFrustum;
|
|
Mth::Vector CameraPosition;
|
|
|
|
uint64 reg_XYOFFSET;
|
|
uint64 reg_SCISSOR;
|
|
|
|
float sx,sy,cx,cy,tx,ty,Sx,Sy,Cx,Cy,Tx,Ty,Near,Far;
|
|
//float sMultipassMaxDist = 1000000000.0f;
|
|
float sMultipassMaxDist = 4000.0f;
|
|
|
|
uint Frame, Field;
|
|
uint ViewportMask;
|
|
|
|
uint RenderVarFrame = 0xFFFFFFFF;
|
|
uint RenderVarViewport = 0xFFFFFFFF;
|
|
|
|
bool FrustumFlagsPlus[3], FrustumFlagsMinus[3];
|
|
|
|
int sSortedListMarker[MAX_SORTED_LIST_MARKERS];
|
|
int sMarkerIndex;
|
|
int sTime;
|
|
int sTotalNewParticles;
|
|
|
|
float FogNear=3000.0f, FogAlpha=0.80f;
|
|
uint32 FogCol=0xFF4070;
|
|
bool EnableFog=false;
|
|
float EffectiveFogAlpha;
|
|
|
|
uint32 *p_patch_FOGCOL;
|
|
|
|
uint8 ShadowDensity = 48;
|
|
}
|
|
|
|
|
|
|
|
void render::SetupVars(uint viewport_num, const Mth::Matrix& camera_transform, const Mth::Rect& viewport_rec, float view_angle,
|
|
float viewport_aspect, float near, float far)
|
|
{
|
|
|
|
// fog variables
|
|
EffectiveFogAlpha = FogAlpha;
|
|
if (FogAlpha < 0.003f)
|
|
{
|
|
EffectiveFogAlpha = 0.003f;
|
|
}
|
|
if (FogAlpha > 0.997f)
|
|
{
|
|
EffectiveFogAlpha = 0.997f;
|
|
}
|
|
if (!EnableFog) // TT5158: ZPush is affected by FogAlpha, so having a very low number causes z-fighting. Garrett
|
|
{
|
|
EffectiveFogAlpha = 0.75f;
|
|
}
|
|
*p_patch_FOGCOL = FogCol;
|
|
|
|
|
|
// Check if we already set up the current variables
|
|
if (VarsUpToDate(viewport_num))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// camera transform
|
|
CameraToWorld = camera_transform;
|
|
|
|
Dbg_MsgAssert(CameraToWorld[0][3] == 0.0f, ("CameraToWorld[0][3] is %f, not 0.0", CameraToWorld[0][3]));
|
|
Dbg_MsgAssert(CameraToWorld[1][3] == 0.0f, ("CameraToWorld[1][3] is %f, not 0.0", CameraToWorld[1][3]));
|
|
Dbg_MsgAssert(CameraToWorld[2][3] == 0.0f, ("CameraToWorld[2][3] is %f, not 0.0", CameraToWorld[2][3]));
|
|
Dbg_MsgAssert(CameraToWorld[3][3] == 1.0f, ("CameraToWorld[3][3] is %f, not 1.0", CameraToWorld[3][3]));
|
|
|
|
// need the transform to be well-formed as a 4x4 matrix
|
|
//CameraToWorld[0][3] = 0.0f;
|
|
//CameraToWorld[1][3] = 0.0f;
|
|
//CameraToWorld[2][3] = 0.0f;
|
|
//CameraToWorld[3][3] = 1.0f;
|
|
|
|
CameraToWorldRotation = CameraToWorld;
|
|
CameraToWorldRotation[3].Set(0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
// world view transform
|
|
WorldToCameraRotation.Transpose(CameraToWorldRotation);
|
|
WorldToCamera = CameraToWorld;
|
|
WorldToCamera.InvertUniform();
|
|
|
|
// massage to get ROW register value
|
|
RowReg = SUB_INCH_PRECISION * WorldToCamera[3] * CameraToWorldRotation;
|
|
RowRegI[0] = (float)(int)RowReg[0];
|
|
RowRegI[1] = (float)(int)RowReg[1];
|
|
RowRegI[2] = (float)(int)RowReg[2];
|
|
RowRegI[3] = 0.0f;
|
|
RowRegF = RowReg - RowRegI;
|
|
RowRegF[3] = 0.0f;
|
|
|
|
// massage to get modified matrix
|
|
AdjustedWorldToCamera = WorldToCamera;
|
|
AdjustedWorldToCamera[3] = RowRegF * WorldToCameraRotation;
|
|
AdjustedWorldToCamera[3] *= RECIPROCAL_SUB_INCH_PRECISION;
|
|
AdjustedWorldToCamera[3][3] = 1.0f;
|
|
|
|
// view frustum
|
|
tx = tanf(view_angle*8.72664626e-03f); // tan of half (angle in radians)
|
|
ty = -(tx / viewport_aspect);
|
|
sx = 1.0f/sqrtf(1.0f+1.0f/(tx*tx));
|
|
sy = 1.0f/sqrtf(1.0f+1.0f/(ty*ty));
|
|
cx = 1.0f/sqrtf(1.0f+tx*tx);
|
|
cy = 1.0f/sqrtf(1.0f+ty*ty);
|
|
Near = near;
|
|
Far = far;
|
|
|
|
// outer frustum
|
|
Tx = tx * (620.0f/(((float) HRES) * 0.5f * viewport_rec.GetWidth()));
|
|
Ty = ty * (524.0f/(((float) VRES) * 0.5f * viewport_rec.GetHeight()));
|
|
Sx = 1.0f/sqrtf(1.0f+1.0f/(Tx*Tx));
|
|
Sy = 1.0f/sqrtf(1.0f+1.0f/(Ty*Ty));
|
|
Cx = 1.0f/sqrtf(1.0f+Tx*Tx);
|
|
Cy = 1.0f/sqrtf(1.0f+Ty*Ty);
|
|
|
|
// frustum transform
|
|
CameraToFrustum.Zero();
|
|
CameraToFrustum[0][0] = 1.0f/Tx;
|
|
CameraToFrustum[1][1] = 1.0f/Ty;
|
|
CameraToFrustum[2][2] = (near+far) / (far-near);
|
|
CameraToFrustum[3][2] = 2.0f * near * far / (near-far);
|
|
CameraToFrustum[2][3] = -1.0f;
|
|
AdjustedWorldToFrustum = AdjustedWorldToCamera * CameraToFrustum;
|
|
WorldToFrustum = WorldToCamera * CameraToFrustum;
|
|
|
|
// viewport (scale and offset from outer frustum)
|
|
ViewportScale.Set( 620.0f, 524.0f, 0.999f*powf(2,22), EffectiveFogAlpha);
|
|
ViewportOffset.Set(2048.0f, 2048.0f, 1.5f*powf(2,23), 0);
|
|
IntViewportScale.Set(620.0f, 524.0f, 0.999f*powf(2,-95), powf(2,32));
|
|
IntViewportOffset.Set(2048.0f+powf(2,19), 2048.0f+powf(2,19), 1.5f*powf(2,-94), powf(2,-32));
|
|
|
|
// viewport transform
|
|
FrustumToViewport.Zero();
|
|
FrustumToViewport[0][0] = ViewportScale[0];
|
|
FrustumToViewport[1][1] = ViewportScale[1];
|
|
FrustumToViewport[2][2] = ViewportScale[2];
|
|
FrustumToViewport[3][3] = ViewportScale[3];
|
|
FrustumToViewport[3][0] = ViewportOffset[0];
|
|
FrustumToViewport[3][1] = ViewportOffset[1];
|
|
FrustumToViewport[3][2] = ViewportOffset[2];
|
|
AdjustedWorldToViewport = AdjustedWorldToFrustum * FrustumToViewport;
|
|
WorldToViewport = WorldToFrustum * FrustumToViewport;
|
|
CameraToViewport = CameraToFrustum * FrustumToViewport;
|
|
|
|
FrustumToIntViewport.Zero();
|
|
FrustumToIntViewport[0][0] = IntViewportScale[0];
|
|
FrustumToIntViewport[1][1] = IntViewportScale[1];
|
|
FrustumToIntViewport[2][2] = IntViewportScale[2];
|
|
FrustumToIntViewport[3][3] = IntViewportScale[3];
|
|
FrustumToIntViewport[3][0] = IntViewportOffset[0];
|
|
FrustumToIntViewport[3][1] = IntViewportOffset[1];
|
|
FrustumToIntViewport[3][2] = IntViewportOffset[2];
|
|
AdjustedWorldToIntViewport = AdjustedWorldToFrustum * FrustumToIntViewport;
|
|
WorldToIntViewport = WorldToFrustum * FrustumToIntViewport;
|
|
|
|
|
|
// sky transforms
|
|
//SkyViewportScale.Set(1900.0f, 1900.0f, powf(2,-102), powf(2,32)); // convert these to constants
|
|
//SkyViewportOffset.Set(2048.0f+powf(2,19), 2048.0f+powf(2,19), 1.5f*powf(2,-101), powf(2,-32)); // convert these to constants
|
|
SkyViewportScale.Set( 620.0f, 524.0f, 0.01f, EffectiveFogAlpha);
|
|
SkyViewportOffset.Set(2048.0f, 2048.0f, 1.0f, 0);
|
|
|
|
SkyWorldToCamera = AdjustedWorldToCamera;
|
|
SkyWorldToCamera[3][0] = 0;
|
|
SkyWorldToCamera[3][1] = 0;
|
|
SkyWorldToCamera[3][2] = 0;
|
|
SkyWorldToFrustum = SkyWorldToCamera * CameraToFrustum;
|
|
SkyFrustumToViewport = FrustumToViewport;
|
|
SkyFrustumToViewport[2][2] = SkyViewportScale[2];
|
|
SkyFrustumToViewport[3][2] = SkyViewportOffset[2];
|
|
SkyWorldToViewport = SkyWorldToFrustum * SkyFrustumToViewport;
|
|
|
|
// used in 3D clipping
|
|
AltFrustum[0] = Near;
|
|
AltFrustum[1] = Far;
|
|
AltFrustum[2] = 620.0f/(((float) HRES) * 0.5f * viewport_rec.GetWidth());
|
|
AltFrustum[3] = 524.0f/(((float) VRES) * 0.5f * viewport_rec.GetHeight());
|
|
|
|
// invert the viewport scale and offset
|
|
InverseViewportScale[0] = 1.0f / ViewportScale[0];
|
|
InverseViewportScale[1] = 1.0f / ViewportScale[1];
|
|
InverseViewportScale[2] = 1.0f / ViewportScale[2];
|
|
InverseViewportScale[3] = 1.0f / ViewportScale[3];
|
|
|
|
InverseViewportOffset[0] = -InverseViewportScale[0] * ViewportOffset[0] * InverseViewportScale[3];
|
|
InverseViewportOffset[1] = -InverseViewportScale[1] * ViewportOffset[1] * InverseViewportScale[3];
|
|
InverseViewportOffset[2] = -InverseViewportScale[2] * ViewportOffset[2] * InverseViewportScale[3];
|
|
InverseViewportOffset[3] = -InverseViewportScale[3] * ViewportOffset[3] * InverseViewportScale[3];
|
|
InverseViewportOffset[3] = FogNear;
|
|
|
|
InverseIntViewportScale[0] = 1.0f / IntViewportScale[0];
|
|
InverseIntViewportScale[1] = 1.0f / IntViewportScale[1];
|
|
InverseIntViewportScale[2] = 1.0f / IntViewportScale[2];
|
|
InverseIntViewportScale[3] = 1.0f / IntViewportScale[3];
|
|
|
|
InverseIntViewportOffset[0] = -InverseIntViewportScale[0] * IntViewportOffset[0] * InverseIntViewportScale[3];
|
|
InverseIntViewportOffset[1] = -InverseIntViewportScale[1] * IntViewportOffset[1] * InverseIntViewportScale[3];
|
|
InverseIntViewportOffset[2] = -InverseIntViewportScale[2] * IntViewportOffset[2] * InverseIntViewportScale[3];
|
|
InverseIntViewportOffset[3] = 0.0f;
|
|
|
|
SkyInverseViewportScale[0] = 1.0f / SkyViewportScale[0];
|
|
SkyInverseViewportScale[1] = 1.0f / SkyViewportScale[1];
|
|
SkyInverseViewportScale[2] = 1.0f / SkyViewportScale[2];
|
|
SkyInverseViewportScale[3] = 1.0f / SkyViewportScale[3];
|
|
|
|
SkyInverseViewportOffset[0] = -SkyInverseViewportScale[0] * SkyViewportOffset[0] * SkyInverseViewportScale[3];
|
|
SkyInverseViewportOffset[1] = -SkyInverseViewportScale[1] * SkyViewportOffset[1] * SkyInverseViewportScale[3];
|
|
SkyInverseViewportOffset[2] = -SkyInverseViewportScale[2] * SkyViewportOffset[2] * SkyInverseViewportScale[3];
|
|
SkyInverseViewportOffset[3] = 0.0f;
|
|
|
|
|
|
|
|
// generate the GS register values we'll need for the viewport
|
|
reg_XYOFFSET = PackXYOFFSET((int)(16.0f * (2048.0f - ((float) HRES) * (viewport_rec.GetOriginX() + 0.5f * viewport_rec.GetWidth()))),
|
|
(int)(16.0f * (2048.0f - ((float) VRES) * (viewport_rec.GetOriginY() + 0.5f * viewport_rec.GetHeight()))));
|
|
reg_SCISSOR = PackSCISSOR( (int)(((float) HRES) * viewport_rec.GetOriginX()),
|
|
(int)(((float) HRES) * (viewport_rec.GetOriginX() + viewport_rec.GetWidth())) - 1,
|
|
(int)(((float) VRES) * viewport_rec.GetOriginY()),
|
|
(int)(((float) VRES) * (viewport_rec.GetOriginY() + viewport_rec.GetHeight())) - 1);
|
|
|
|
|
|
// inverse frustum transform
|
|
//ViewFrustumToCamera.Zero();
|
|
//ViewFrustumToCamera[0][0] = tx;
|
|
//ViewFrustumToCamera[1][1] = ty;
|
|
//ViewFrustumToCamera[2][3] = (near - far) / (2.0f * near * far);
|
|
//ViewFrustumToCamera[3][2] = -1.0f;
|
|
//ViewFrustumToCamera[3][3] = -(near + far) / (2.0f * near * far);
|
|
|
|
// get world coords of the far frustum vertices
|
|
Mth::Vector vFTL, vFTR, vFBL, vFBR;
|
|
vFTL.Set( tx*far, ty*far, far, 1.0f);
|
|
vFTL *= CameraToWorld;
|
|
vFTR.Set(-tx*far, ty*far, far, 1.0f);
|
|
vFTR *= CameraToWorld;
|
|
vFBL.Set( tx*far,-ty*far, far, 1.0f);
|
|
vFBL *= CameraToWorld;
|
|
vFBR.Set(-tx*far,-ty*far, far, 1.0f);
|
|
vFBR *= CameraToWorld;
|
|
|
|
Mth::Vector pos(WorldToCamera[3]);
|
|
for (int i=0; i<3; i++)
|
|
{
|
|
FrustumFlagsPlus[i] = (vFTL[i]>=pos[i] && vFTR[i]>=pos[i] && vFBL[i]>=pos[i] && vFBR[i]>=pos[i]);
|
|
FrustumFlagsMinus[i] = (vFTL[i]<=pos[i] && vFTR[i]<=pos[i] && vFBL[i]<=pos[i] && vFBR[i]<=pos[i]);
|
|
}
|
|
|
|
|
|
// Set ViewportMask. Will only draw CGeomNodes that have a non-zero result with (ViewportMask & Visibility)
|
|
ViewportMask = (1 << (VISIBILITY_FLAG_BIT + viewport_num));
|
|
|
|
// Mark current viewport and frame
|
|
RenderVarFrame = Frame;
|
|
RenderVarViewport = viewport_num;
|
|
|
|
}
|
|
|
|
void render::SetupVarsDMA(uint viewport_num, float near, float far)
|
|
{
|
|
|
|
|
|
// initialisation for new bounding volume tests
|
|
Mth::Matrix Normals;
|
|
|
|
asm ("vmaxw vf31,vf00,vf00w": :);
|
|
|
|
// view frustum R, L, B, T
|
|
Normals[0].Set(-cx, cx, 0, 0);
|
|
Normals[1].Set( 0, 0, cy,-cy);
|
|
Normals[2].Set(-sx,-sx,-sy,-sy);
|
|
Normals[3].Set( 0, 0, 0, 0);
|
|
|
|
Normals = WorldToCamera * Normals;
|
|
|
|
asm __volatile__("
|
|
|
|
lqc2 vf10,(%0)
|
|
lqc2 vf11,(%1)
|
|
lqc2 vf12,(%2)
|
|
lqc2 vf16,(%3)
|
|
vabs vf13,vf10
|
|
vabs vf14,vf11
|
|
vabs vf15,vf12
|
|
|
|
": : "r" (&Normals[0]), "r" (&Normals[1]), "r" (&Normals[2]), "r" (&Normals[3]));
|
|
|
|
// both frusta ?, ? N, F,
|
|
Normals[0].Set( 0, 0, 0, 0);
|
|
Normals[1].Set( 0, 0, 0, 0);
|
|
Normals[2].Set( 0, 0, -1, 1);
|
|
Normals[3].Set( 0, 0,near,-far);
|
|
|
|
Normals = WorldToCamera * Normals;
|
|
|
|
asm __volatile__("
|
|
|
|
lqc2 vf17,(%0)
|
|
lqc2 vf18,(%1)
|
|
lqc2 vf19,(%2)
|
|
lqc2 vf23,(%3)
|
|
vabs vf20,vf17
|
|
vabs vf21,vf18
|
|
vabs vf22,vf19
|
|
|
|
": : "r" (&Normals[0]), "r" (&Normals[1]), "r" (&Normals[2]), "r" (&Normals[3]));
|
|
|
|
|
|
// outer frustum R, L, B, T
|
|
Normals[0].Set(-Cx, Cx, 0, 0);
|
|
Normals[1].Set( 0, 0, Cy,-Cy);
|
|
Normals[2].Set(-Sx,-Sx,-Sy,-Sy);
|
|
Normals[3].Set( 0, 0, 0, 0);
|
|
|
|
Normals = WorldToCamera * Normals;
|
|
|
|
asm __volatile__("
|
|
|
|
lqc2 vf24,(%0)
|
|
lqc2 vf25,(%1)
|
|
lqc2 vf26,(%2)
|
|
lqc2 vf30,(%3)
|
|
vabs vf27,vf24
|
|
vabs vf28,vf25
|
|
vabs vf29,vf26
|
|
|
|
": : "r" (&Normals[0]), "r" (&Normals[1]), "r" (&Normals[2]), "r" (&Normals[3]));
|
|
|
|
|
|
|
|
|
|
#if OLD_FOG
|
|
// link in the dma list that does fogging postprocessing
|
|
dma::SetList(sGroup::pFog);
|
|
dma::Tag(dma::call, 0, (uint)&FogDma);
|
|
vif::NOP();
|
|
vif::NOP();
|
|
sGroup::pFog->Used[render::Field] = true;
|
|
|
|
dma::Gosub(SET_RENDERSTATE,2);
|
|
dma::Gosub(SET_FOGCOL,2);
|
|
#endif
|
|
|
|
|
|
// upload new vu1 code and set up double-buffering
|
|
dma::SetList(sGroup::pParticles);
|
|
dma::Tag(dma::ref, ((uint8 *)&NewMPGEnd-(uint8 *)&NewMPGStart+15)/16, (uint)&NewMPGStart);
|
|
vif::BASE(0);
|
|
vif::OFFSET(32);
|
|
|
|
// send vu1 data for particle setup
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vif::FLUSH();
|
|
vif::BeginDIRECT();
|
|
gif::Tag2(gs::A_D,1,PACKED,0,0,1,3);
|
|
gs::Reg2(gs::SCISSOR_1, render::reg_SCISSOR);
|
|
gs::Reg2(gs::XYOFFSET_1, render::reg_XYOFFSET);
|
|
gs::Reg2(gs::ZBUF_1, PackZBUF(ZBUFFER_START,PSMZ24,1));
|
|
vif::EndDIRECT();
|
|
vif::UNPACK(0, V4_32, 7, REL, SIGNED, 0);
|
|
vu1::StoreMat(*(Mat *)&render::WorldToIntViewport); // view transform
|
|
vif::StoreV4_32F(320.0f, 320.0f, 0.0f, 0.0f); // kvec
|
|
vif::StoreV4_32(0x39005000, 0x39006000, 0, 0x3F800000); // NTL cull test vec - w-component limits tan of apparent radius
|
|
vif::StoreV4_32(0x3900B000, 0x3900A000, 0, 0); // BR cull test vec
|
|
vif::MSCAL(8); // init sprites
|
|
dma::EndTag();
|
|
dma::SetList(NULL);
|
|
}
|
|
|
|
|
|
|
|
// Checks to see if the render variables are current
|
|
bool render::VarsUpToDate(uint viewport_num)
|
|
{
|
|
return (RenderVarFrame == Frame) && (RenderVarViewport == viewport_num);
|
|
}
|
|
|
|
// Invalidates the render variables
|
|
void render::InvalidateVars()
|
|
{
|
|
RenderVarFrame = 0xFFFFFFFF;
|
|
RenderVarViewport = 0xFFFFFFFF;
|
|
}
|
|
|
|
// a quick hack for Dave...
|
|
void DrawRectangle(int x0, int y0, int x1, int y1, uint32 rgba)
|
|
{
|
|
dma::SetList(sGroup::pEpilogue);
|
|
dma::BeginTag(dma::cnt, 0);
|
|
vif::FLUSH();
|
|
vif::BeginDIRECT();
|
|
gif::BeginTag2(gs::A_D, 1, PACKED, SPRITE|ABE, 1);
|
|
gs::Reg2(gs::ALPHA_1, PackALPHA(0,1,0,1,0));
|
|
gs::Reg2(gs::RGBAQ, (uint64)rgba);
|
|
gs::Reg2(gs::XYZ2, PackXYZ(x0,y0,0xFFFFFF));
|
|
gs::Reg2(gs::XYZ2, PackXYZ(x1,y1,0xFFFFFF));
|
|
gif::EndTag2(1);
|
|
vif::EndDIRECT();
|
|
dma::EndTag();
|
|
}
|
|
|
|
|
|
|
|
} // namespace NxPs2
|
|
|