///////////////////////////////////////////////////////////////////////////// // p_nx.cpp - PS2 platform specific interface to the engine // // This is PS2 SPECIFIC!!!!!! So might get a bit messy // // If there is anything in here that is not PS2 specific, then it needs to // be in nx.cpp #include "gfx\nx.h" #include "gfx\NxViewMan.h" #include "gfx\NxMiscFX.h" #include "gfx\NxParticleMgr.h" #include "gfx\NxQuickAnim.h" #include "gfx\NGPS\p_NxGeom.h" #include "gfx\NGPS\p_NxLightMan.h" #include "gfx\NGPS\p_NxMesh.h" #include "gfx\NGPS\p_NxModel.h" #include "gfx\NGPS\p_NxSector.h" #include "gfx\NGPS\p_NxScene.h" #include "gfx\NGPS\p_NxSprite.h" #include "gfx\NGPS\p_NxTexture.h" #include "gfx\NGPS\p_NxTextured3dPoly.h" #include "gfx\NGPS\p_NxNewParticleMgr.h" #include "core\math.h" #include "gel\collision\collision.h" #include "gfx\ngps\nx\nx_init.h" #include "gfx\ngps\nx\material.h" #include "gfx\ngps\nx\mesh.h" #include "gfx\ngps\nx\occlude.h" #include "gfx\ngps\nx\scene.h" #include "gfx\ngps\nx\chars.h" #include "gfx\ngps\nx\render.h" #include "gfx\ngps\nx\geomnode.h" #include "gfx\ngps\nx\immediate.h" #include "gfx\ngps\p_nxweather.h" #include "gfx\ngps\nx\switches.h" #include #include #include #include #include #include namespace NxPs2 { extern void test_render_start_frame(); extern void test_render(Mth::Matrix* camera_orient, Mth::Vector* camera_pos, float view_angle, float screen_aspect); extern void test_render_end_frame(); extern void test_init(); extern bool IsVisible(Mth::Vector ¢er, float radius); extern void WaitForRendering(); extern uint32 *p_patch_ALPHA; extern uint32 *p_patch_ALPHA2; } // namespace NxPs2 namespace Nx { ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// // Functions /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_start_engine() { NxPs2::InitialiseEngine(); mp_particle_manager = new CPs2NewParticleManager; Nx::CPs2Texture::sInitTables(); mp_weather = new CPs2Weather; } void CEngine::s_plat_pre_render() { // Moved to StuffAfterGSFinished // CPs2LightManager::sUpdateEngine(); // // // particle systems effectively have their transform double-buffered for dma purposes // // so now is the time to do the buffer flip by updating the transforms // mp_particle_manager->UpdateParticles(); NxPs2::test_render_start_frame(); } void CEngine::s_plat_post_render() { // bit of a hack, see below // Gfx::Camera *cur_camera = CViewportManager::sGetCamera( 0 ); // if (cur_camera) { NxPs2::test_render_end_frame(); } } //void shadow_test( float * p_sn, uint16 * p_index_list, int count, float * dir ) //{ // // Test... // // asm __volatile__( // " // .set noreorder // // lqc2 vf09, 0x0(%1) # light dir // // lqc2 vf10, 0x0(%0) # surface normal // addi %2, %2, -1 // //dot_loop: // vmul.xyz vf13, vf09, vf10 # result = plane * center + plane_w [start] // addiu %0, %0, 12 // lqc2 vf10, 0x4(%0) # surface normal // vaddy.x vf13, vf13, vf13y // vaddz.x vf13, vf13, vf13z // // qmfc2 $8, vf13 // sw $8, 0x0(%0) // addiu %0, %0, 4 // // bne %2, $0, dot_loop // addi %2, %2, -1 //done: // // .set reorder // ": : "r" (p_sn), "r" (dir), "r" (count) : "$8" ); //// //// ": "=r" (p_dot) : "r" (p_sn) : "$8", "$9", "$10"); // // ////# lhu $8, 0(%2) ////# lhu $9, 2(%2) ////# lhu $10, 4(%2) ////# addiu %2, %2, 6 ////# ////# add $8, $8, %1 ////# add $9, $9, %1 ////# add $10, $10, %1 ////# lqc2 vf10, 0x0($8) # p0 ////# lqc2 vf11, 0x0($8) # p1 ////# lqc2 vf12, 0x0($8) # p2 // //} /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_render_world() { NxPs2::RenderPrologue(); // should this be in test_render_start_frame? (Garrett: Answer is no, it won't work) NxPs2::RenderInitImmediateMode(); //#define NUM_TRIS 1851 //// float xyz[(NUM_TRIS+2)*3]; // float sn[NUM_TRIS*4]; // uint16 list[NUM_TRIS*3]; // float vec[3] = { 1.0f, 1.5f, 2.0f }; // // for ( int lp = 0; lp < NUM_TRIS; lp++ ) // { // sn[(lp*4)+0] = 6.0f; // sn[(lp*4)+1] = 7.0f; // sn[(lp*4)+2] = 8.0f; // //// xyz[(lp*3)+0] = ((float)(lp/2)); //// xyz[(lp*3)+1] = ((float)(lp&1)); //// xyz[(lp*3)+2] = 0.0f; // // list[(lp*3)+0] = ( lp + 0 ) * 12; // list[(lp*3)+1] = ( lp + 1 ) * 12; // list[(lp*3)+2] = ( lp + 2 ) * 12; // } // // Tmr::CPUCycles cycles = Tmr::GetTimeInCPUCycles(); // shadow_test( sn, list, NUM_TRIS, vec ); // cycles = Tmr::GetTimeInCPUCycles() - cycles; // printf( "CPU Cycles for shadow test: %d\n", cycles ); #if 0 // (Mike) see if there are any viewports to render, and return if there aren't // (Mike) ...perhaps make this trivial by having a CViewportManager::sGetNumActiveCameras() ? bool no_viewports = true; for (int view_idx = 0; view_idx < CViewportManager::sGetNumActiveViewports(); view_idx++) { if (CViewportManager::sGetActiveViewport(view_idx)->GetCamera()) { no_viewports = false; break; } } if (no_viewports) { return; } // (Mike) now we know there's something to render... #endif /* #if 1 // normal 4:3 TV float view_angle = 72.0f; // The angle prepended by the width of the screen float screen_aspect = 4.0f/3.0f; // physical ration of width to height on the screen (4:3 or 16:9) #else // anamorphic widescreen float view_angle = 80.0f; // The angle prepended by the width of the screen float screen_aspect = 16.0f/9.0f; // physical ratio of width to height on the screen (4:3 or 16:9) #endif */ // CViewportManager::sSetScreenMode(vSPLIT_V); bool got_vu0 = NxPs2::OccludeUseVU0(); Dbg_Assert(got_vu0); // Note: this method of setting up the camera must change // so that the p_nx module does not reference things higher up the hierarchy for (int view_idx = 0; view_idx < CViewportManager::sGetNumActiveViewports(); view_idx++) { Gfx::Camera *cur_camera; CViewport *p_cur_viewport = CViewportManager::sGetActiveViewport(view_idx); Dbg_MsgAssert(p_cur_viewport,("Unable to get viewport %d",view_idx)); cur_camera = p_cur_viewport->GetCamera(); if (cur_camera) { // Build the occluders first now that we know where the camera is for this frame. NxPs2::BuildOccluders( &( cur_camera->GetPos())); // Make camera matrix Mth::Matrix cam_matrix(cur_camera->GetMatrix()); cam_matrix[W] = cur_camera->GetPos(); // Garrett: For some reason, only these clipping values work NxPs2::RenderViewport(view_idx, cam_matrix, p_cur_viewport->GetRect(), cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio(), /*-cur_camera->GetNearClipPlane(), -cur_camera->GetFarClipPlane()*/ -1.0f, -100000.0f, s_render_mode > 1); # ifdef __USE_PROFILER__ Sys::CPUProfiler->PushContext( 0, 0, 255 ); // Blue (Under Yellow) = Immediate mode rnedering # endif // __USE_PROFILER__ // Draw the immediate mode stuff NxPs2::RenderSwitchImmediateMode(); NxPs2::CImmediateMode::sViewportInit(); // Mick: Moved this here from test_render_end_frame NxPs2::Reallocate2DVRAM(); if (NxPs2::FlipCopyEnabled()) // TT 13470, 13471, 11061 Particle systems displayed when screen is blank { Nx::render_particles(); mp_particle_manager->RenderParticles(); // Render Weather effects. mp_weather->Process( Tmr::FrameLength() ); mp_weather->Render(); //Nx::CTextured3dPoly::sRenderAll(); TextureSplatRender(); ShatterRender(); // Make sure this is always the last immediate draw since it switches DMA lists } NxPs2::RenderSwitchImmediateMode(); # ifdef __USE_PROFILER__ Sys::CPUProfiler->PopContext( ); # endif // __USE_PROFILER__ if (s_render_mode) { #ifdef __NOPT_ASSERT__ for (int i = 0; i < MAX_LOADED_SCENES; i++) { if (sp_loaded_scenes[i]) { sp_loaded_scenes[i]->DebugRenderCollision(s_debug_ignore_1, s_debug_ignore_0); } } #endif const Lst::Head& composite_obj_list = Nx::CEngine::sGetMovableObjects(); Lst::Node* p_composite_node = composite_obj_list.GetNext(); while( p_composite_node ) { Obj::CCompositeObject* p_composite_object = static_cast(p_composite_node->GetData()); Dbg_MsgAssert(p_composite_object, ("Node in CObject list wasn't a CCompositeObject")); Obj::CCollisionComponent* p_collision_component = GetCollisionComponentFromObject( p_composite_object ); if ( p_collision_component ) { CCollObj* p_coll_obj = p_collision_component->GetCollision(); if (p_coll_obj) { p_coll_obj->DebugRender( s_debug_ignore_1, s_debug_ignore_0 ); } } p_composite_node = p_composite_node->GetNext(); } } } else { //printf ("RENDERING FRAME WITH NO CAMERA (Nothing rendered)\n"); } // Screen flash rendering. Nx::ScreenFlashRender( view_idx, 0 ); } //NxPs2::RenderEpilogueStart(); if (got_vu0) { NxPs2::OccludeDisableVU0(); } } /******************************************************************/ /* */ /* */ /******************************************************************/ CScene * CEngine::s_plat_create_scene(const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors) { // Create scene class instance CPs2Scene *p_ps2_scene = new CPs2Scene; NxPs2::CGeomNode *p_geomnode = new NxPs2::CGeomNode; p_geomnode->AddToTree(LOADFLAG_RENDERNOW); p_geomnode->SetActive(true); p_ps2_scene->SetEngineCloneScene(p_geomnode); CScene *new_scene = p_ps2_scene; new_scene->SetInSuperSectors(add_super_sectors); new_scene->SetIsSky(false); return new_scene; } /******************************************************************/ /* */ /* */ /******************************************************************/ CScene *CEngine::s_plat_load_scene_from_memory( void *p_mem, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary ) { // GJ: This is a stub function that should never be called by the PS2. // Currently, it's only needed by the X-box and the NGC // because all models are treated as "scenes" on those platforms. // On the PS2, models are treated as CPS2Mesh-es... Dbg_MsgAssert( 0, ( "This function is not supported on the PS2 right now" ) ); return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ CScene * CEngine::s_plat_load_scene(const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary) { // Create scene class instance CPs2Scene *p_ps2_scene = new CPs2Scene; CScene *new_scene = p_ps2_scene; new_scene->SetInSuperSectors(add_super_sectors); new_scene->SetIsSky(is_sky); #ifdef __NOPT_ASSERT__ CPs2TexDict *p_ps2_tex_dict = static_cast(p_tex_dict); Dbg_MsgAssert(p_ps2_tex_dict,("Invalid texture dictionsary for %s",p_name)); #endif // load scene // Pip::Load the new file uint8 *p_pipData = (uint8*)Pip::Load(p_name); // 1st section is shadow volume data. // p_ps2_scene->mp_shadow_volume_header = (Nx::sShadowVolumeHeader*)p_pipData; // p_ps2_scene->mp_shadow_volume_header->p_vertex = (Nx::sShadowVertex *)&p_ps2_scene->mp_shadow_volume_header[1]; // p_ps2_scene->mp_shadow_volume_header->p_connect = (Nx::sShadowConnect *)&p_ps2_scene->mp_shadow_volume_header->p_vertex[p_ps2_scene->mp_shadow_volume_header->num_verts]; // p_ps2_scene->mp_shadow_volume_header->p_neighbor = (Nx::sShadowNeighbor *)&p_ps2_scene->mp_shadow_volume_header->p_connect[p_ps2_scene->mp_shadow_volume_header->num_faces]; // // uint8 * p_geom_data = (uint8*)p_ps2_scene->mp_shadow_volume_header->p_vertex; // p_geom_data = &p_geom_data[p_ps2_scene->mp_shadow_volume_header->byte_size]; // PJR // Process my data here. // set up load flags uint32 load_flags = 0; if (!is_dictionary) { // render it unconditionally as opposed to holding in a database to be instanced later) load_flags |= LOADFLAG_RENDERNOW; } else { Dbg_MsgAssert(!add_super_sectors, ("Why would you need collision SuperSectors for a scene dictionary?")); } if (is_sky) { load_flags |= LOADFLAG_SKY; } // process data in place NxPs2::CGeomNode *p_geomnode = NxPs2::CGeomNode::sProcessInPlace(p_pipData, load_flags); // NxPs2::CGeomNode *p_geomnode = NxPs2::CGeomNode::sProcessInPlace(p_geom_data, load_flags); Dbg_MsgAssert(((int)(p_geomnode) & 0xf) == 0,("p_geomnode (0x%x) not multiple of 16 after sProcessInPlace\n" ,(int)p_geomnode)); p_ps2_scene->SetEngineScene(p_geomnode); return new_scene; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CEngine::s_plat_add_scene(CScene *p_scene, const char *p_filename) { Dbg_MsgAssert(/*!p_scene->is_dictionary &&*/ !p_scene->IsSky(), ("Can't add to sky or dictionary scene.")); // load scene // Pip::Load the new file uint8 *p_pipData = (uint8*)Pip::Load(p_filename); Dbg_MsgAssert(p_pipData, ("s_plat_add_scene(): Can't open file %s", p_filename)); // process data in place NxPs2::CGeomNode *p_geomnode = NxPs2::CGeomNode::sProcessInPlace(p_pipData, LOADFLAG_RENDERNOW); CPs2Scene *p_ps2_scene = static_cast(p_scene); p_ps2_scene->SetEngineAddScene(p_geomnode); return p_geomnode != NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CEngine::s_plat_unload_scene(CScene *p_scene) { Dbg_MsgAssert(p_scene,("Trying to delete a NULL scene")); CPs2Scene * p_ps2_scene = (CPs2Scene*)p_scene; NxPs2::CGeomNode *p_geomnode = p_ps2_scene->GetEngineScene(); if (p_geomnode) { // clean up the node we created p_geomnode->Cleanup(); // Pip::Unload the file Pip::Unload(p_scene->GetSceneFilename()); } // Clone scene node #if 0 // Moved this into ~CPs2Scene so that this CGeomNode isn't destroyed before the cloned sectors NxPs2::CGeomNode *p_clone_geomnode = p_ps2_scene->GetEngineCloneScene(); if (p_clone_geomnode) { p_clone_geomnode->Cleanup(); delete p_clone_geomnode; } #endif return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ CModel* CEngine::s_plat_init_model(void) { CPs2Model* pModel = new CPs2Model; return pModel; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CEngine::s_plat_uninit_model(CModel* pModel) { Dbg_Assert( pModel ); delete pModel; return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ CGeom* CEngine::s_plat_init_geom(void) { CPs2Geom* pGeom = new CPs2Geom; return pGeom; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CEngine::s_plat_uninit_geom(CGeom* pGeom) { Dbg_Assert( pGeom ); delete pGeom; return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ CQuickAnim* CEngine::s_plat_init_quick_anim() { CQuickAnim* pQuickAnim = new CQuickAnim; return pQuickAnim; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_uninit_quick_anim(CQuickAnim* pQuickAnim) { Dbg_Assert( pQuickAnim ); delete pQuickAnim; return; } /******************************************************************/ /* */ /* */ /******************************************************************/ CMesh* CEngine::s_plat_load_mesh(uint32 id, uint32* pModelData, int modelDataSize, uint8* pCASData, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume) { CPs2Mesh* pMesh = new CPs2Mesh(pModelData, modelDataSize, pCASData, pTexDict, texDictOffset, isSkin, doShadowVolume); return pMesh; } /******************************************************************/ /* */ /* */ /******************************************************************/ CMesh* CEngine::s_plat_load_mesh(const char* pMeshFileName, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume) { CPs2Mesh* pMesh = new CPs2Mesh(pMeshFileName, pTexDict, texDictOffset, isSkin, doShadowVolume); return pMesh; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CEngine::s_plat_unload_mesh(CMesh* pMesh) { Dbg_Assert( pMesh ); delete pMesh; return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_set_mesh_scaling_parameters( SMeshScalingParameters* pParams ) { NxPs2::SetMeshScalingParameters( pParams ); } /******************************************************************/ /* */ /* */ /******************************************************************/ CSprite * CEngine::s_plat_create_sprite(CWindow2D *p_window) { return new CPs2Sprite(p_window); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CEngine::s_plat_destroy_sprite(CSprite *p_sprite) { delete p_sprite; return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ CTextured3dPoly * CEngine::s_plat_create_textured_3d_poly() { return new NxPs2::CPs2Textured3dPoly; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CEngine::s_plat_destroy_textured_3d_poly(CTextured3dPoly *p_poly) { delete p_poly; return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ Nx::CTexture *CEngine::s_plat_create_render_target_texture( int width, int height, int depth, int z_depth ) { return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_project_texture_into_scene( Nx::CTexture *p_texture, Nx::CModel *p_model, Nx::CScene *p_scene ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_set_projection_texture_camera( Nx::CTexture *p_texture, Gfx::Camera *p_camera ) { #if !STENCIL_SHADOW // Currently assumes a top down view. So all we need to store is the position of the camera. // The width and height of the view frustum (this is an orthographic camera) are constant. Mth::Vector pos = p_camera->GetPos(); NxPs2::SetTextureProjectionCamera( &pos, &pos ); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_stop_projection_texture( Nx::CTexture *p_texture ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_add_occlusion_poly( uint32 num_verts, Mth::Vector *p_vert_array, uint32 checksum ) { if( num_verts == 4 ) { NxPs2::AddOcclusionPoly( p_vert_array[0], p_vert_array[1], p_vert_array[2], p_vert_array[3], checksum ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_enable_occlusion_poly( uint32 checksum, bool enable ) { NxPs2::EnableOcclusionPoly( checksum, enable ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_remove_all_occlusion_polys( void ) { NxPs2::RemoveAllOcclusionPolys(); } /******************************************************************/ /* */ /* */ /******************************************************************/ // returns true if the sphere at "center", with the "radius" // is visible to the current camera // (note, currently this is the last frame's camera on PS2) bool CEngine::s_plat_is_visible( Mth::Vector& center, float radius ) { return NxPs2::IsVisible(center,radius); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_set_max_multipass_distance(float dist) { NxPs2::render::sMultipassMaxDist = dist; } /******************************************************************/ /* */ /* */ /******************************************************************/ const char * CEngine::s_plat_get_platform_extension() { return "PS2"; // String literals are statically allocated so can be returned safely, (Bjarne, p90) } /******************************************************************/ // Wait for any pending asyncronous rendering to finish, so rendering // data can be unloaded /******************************************************************/ void CEngine::s_plat_finish_rendering() { NxPs2::WaitForRendering(); // PS2 Specific, wait for prior frame's DMA to finish } /******************************************************************/ // Set the amount that the previous frame is blended with this frame // 0 = none (just see current frame) // 128 = 50/50 // 255 = 100% (so you only see the previous frame) /******************************************************************/ void CEngine::s_plat_set_screen_blur(uint32 amount ) { amount = (255-amount)/2; uint32 alpha = *NxPs2::p_patch_ALPHA; alpha &= 0xffffff00; alpha |= amount; *NxPs2::p_patch_ALPHA = alpha; *NxPs2::p_patch_ALPHA2 = alpha; } /******************************************************************/ /* */ /* */ /******************************************************************/ int CEngine::s_plat_get_num_soundtracks() { return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ const char* CEngine::s_plat_get_soundtrack_name( int soundtrack_number ) { return NULL; } // Add some PS2 specific debug info to the metrics script structure void CEngine::s_plat_get_metrics(Script::CStruct * p_info) { // get the scene, and get the root Script::CStruct *p_ps2 = new Script::CStruct(); NxPs2::CGeomMetrics * p_metrics = new NxPs2::CGeomMetrics; CPs2Scene * p_ps2_scene = (CPs2Scene *) sGetMainScene(); if (p_ps2_scene->GetEngineScene()) { p_ps2_scene->GetEngineScene()->CountMetrics(p_metrics); p_ps2->AddInteger(CRCD(0xd55cd658,"NULLEngineScene"),0); } else { // K: Added this so that the script refresh_poly_count in lighttool.q // can display "N/A" for the values. p_ps2->AddInteger(CRCD(0xd55cd658,"NULLEngineScene"),1); } p_ps2->AddInteger(CRCD(0x3dd01ea1,"total"), p_metrics->m_total); p_ps2->AddInteger(CRCD(0x3960ff18,"leaf"), p_metrics->m_leaf); p_ps2->AddInteger(CRCD(0x4de5330c,"objects"), p_metrics->m_object); p_ps2->AddInteger(CRCD(0x94927839,"vert"), p_metrics->m_verts); p_ps2->AddInteger(CRCD(0x169ee151,"poly"), p_metrics->m_polys); p_ps2->AddFloat(CRCD(0x58785e10,"verts_per_poly"), (float)p_metrics->m_verts/(float)p_metrics->m_polys); p_ps2->AddFloat(CRCD(0x3b75d7cc,"polys_per_object"), (float)p_metrics->m_polys/(float)p_metrics->m_object); p_ps2->AddFloat(CRCD(0xbc56249d,"polys_per_mesh"), (float)p_metrics->m_polys/(float)p_metrics->m_leaf); p_info->AddStructurePointer(CRCD(0x26861025,"scene"),p_ps2); delete p_metrics; } void CEngine::s_plat_set_letterbox( bool letterbox ) { NxPs2::DoLetterbox = letterbox; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CEngine::s_plat_set_color_buffer_clear( bool clear ) { } }