From e8525babd113a163f3e6a7f59d3f610bc344a678 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Sun, 10 Mar 2024 17:41:38 +0500 Subject: [PATCH] feat(ConVar): Portal exit velocitys are no longer hard coded. fix(Physgun): Rotation works now. I think i took it from SourceBox or whatever it was called. feat(Portal 2): I don't remember what i did but i did something. A lot of that something was taken from: https://github.com/RocketLauncher21/Portal2ASW chore(ImGui): Updated ImGui to latest. chore(Polyhedron): Changed a lot i don't remember where i took it from. --- game/client/c_baseanimating.cpp | 20 +- game/client/client_portal.vpc | 3 + game/client/imgui/imgui_impl_source.cpp | 1 + game/client/imgui/imgui_system.cpp | 21 +- game/client/imgui/test_window.cpp | 25 + game/client/in_touch.cpp | 4 +- .../portal/remake/c_env_portal_laser.cpp | 14 +- .../portal/remake/c_prop_waightedcube.cpp | 143 + game/client/viewrender.cpp | 6 + game/server/portal/portal_client.cpp | 5 +- game/server/portal/prop_portal.cpp | 27 +- .../server/portal/remake/env_portal_laser.cpp | 38 +- .../portal/remake/prop_laser_catcher.cpp | 34 +- .../server/portal/remake/prop_laser_catcher.h | 1 - .../portal/remake/prop_weightedcube.cpp | 115 +- game/server/portal/remake/prop_weightedcube.h | 11 + game/server/portal2/indicator_panel.cpp | 186 + game/server/portal2/npc_personality_core.cpp | 7 + game/server/portal2/npc_wheatley_boss.cpp | 60 + game/server/server_portal.vpc | 2 + mathlib/polyhedron.cpp | 3508 ++++++++++++----- 21 files changed, 3208 insertions(+), 1023 deletions(-) create mode 100644 game/client/imgui/test_window.cpp create mode 100644 game/client/portal/remake/c_prop_waightedcube.cpp create mode 100644 game/server/portal2/indicator_panel.cpp create mode 100644 game/server/portal2/npc_wheatley_boss.cpp diff --git a/game/client/c_baseanimating.cpp b/game/client/c_baseanimating.cpp index f12e8bc4..b334ca08 100644 --- a/game/client/c_baseanimating.cpp +++ b/game/client/c_baseanimating.cpp @@ -3591,6 +3591,10 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) if ( bIsInvisible && !clienttools->IsInRecordingMode() ) return; +#if !defined( CSTRIKE15 ) + // We already handle muzzle flash events in CSTRIKE15. + // Also this code has a bug in that it always uses attachment 1 instead of by name. + // add in muzzleflash effect if ( ShouldMuzzleFlash() ) { @@ -3599,6 +3603,8 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) ProcessMuzzleFlashEvent(); } +#endif + // If we're invisible, don't process animation events. if ( bIsInvisible ) return; @@ -3616,7 +3622,7 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) if ( nSeqNum >= nStudioNumSeq ) { // This can happen e.g. while reloading Heavy's shotgun, switch to the minigun. - Warning( "%s[%d]: Playing sequence %d but there's only %d in total?\n", GetDebugName(), entindex(), nSeqNum, nStudioNumSeq ); + //Warning( "%s[%d]: Playing sequence %d but there's only %d in total?\n", GetDebugName(), entindex(), nSeqNum, nStudioNumSeq ); return; } @@ -3717,8 +3723,8 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) if (ScriptHookFireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ) == false) continue; #endif - - + + FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ); } @@ -3740,14 +3746,16 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) { if ( watch ) { - Msg( "%i (seq: %d) FE %i Normal cycle %f, prev %f ev %f (time %.3f)\n", + Msg( "%i (seq: %d/%s) FE %i Normal cycle %f, prev %f ev %f (time %.3f) (options %s)\n", gpGlobals->tickcount, GetSequence(), + GetSequenceActivityName( GetSequence() ), pevent[i].event, pevent[i].cycle, m_flPrevEventCycle, flEventCycle, - gpGlobals->curtime ); + gpGlobals->curtime, + pevent[ i ].pszOptions() ); } #ifdef MAPBASE_VSCRIPT @@ -3759,7 +3767,7 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) } } - m_flPrevEventCycle = flEventCycle; + m_flPrevEventCycle = GetCycle(); } #ifdef MAPBASE_VSCRIPT diff --git a/game/client/client_portal.vpc b/game/client/client_portal.vpc index ee00e4cf..ade75f3d 100644 --- a/game/client/client_portal.vpc +++ b/game/client/client_portal.vpc @@ -27,6 +27,8 @@ $Project "Client (Portal)" $File "c_team_objectiveresource.cpp" $File "c_team_objectiveresource.h" + $File "imgui\test_window.cpp" + $Folder "HL2 DLL" { $File "episodic\flesh_internal_material_proxy.cpp" @@ -196,6 +198,7 @@ $Project "Client (Portal)" $Folder "Portal re2" { $File "portal\remake\c_env_portal_laser.cpp" + $File "portal\remake\c_prop_waightedcube.cpp" $File "portal2\c_prop_floor_button.cpp" $File "vgui_movie_display.cpp" diff --git a/game/client/imgui/imgui_impl_source.cpp b/game/client/imgui/imgui_impl_source.cpp index f8dcd6eb..261ebf94 100644 --- a/game/client/imgui/imgui_impl_source.cpp +++ b/game/client/imgui/imgui_impl_source.cpp @@ -205,6 +205,7 @@ static bool ImGui_ImplSource_CreateFontsTexture() // Create a material for the texture ITexture *fonttex = g_pMaterialSystem->CreateProceduralTexture( "imgui_font", TEXTURE_GROUP_OTHER, width, height, IMAGE_FORMAT_RGBA8888, TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_POINTSAMPLE | TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_NOLOD ); fonttex->SetTextureRegenerator( new CDearImGuiFontTextureRegenerator ); + fonttex->Download(); KeyValues *vmt = new KeyValues( "UnlitGeneric" ); vmt->SetString( "$basetexture", "imgui_font" ); diff --git a/game/client/imgui/imgui_system.cpp b/game/client/imgui/imgui_system.cpp index 12f96bc7..20b1ec9c 100644 --- a/game/client/imgui/imgui_system.cpp +++ b/game/client/imgui/imgui_system.cpp @@ -140,7 +140,7 @@ public: CDummyOverlayPanel() { SetVisible( false ); - SetParent( enginevgui->GetPanel( PANEL_GAMEUIDLL ) ); + SetParent( enginevgui->GetPanel( PANEL_CLIENTDLL ) ); SetPaintEnabled( true ); SetCursor( vgui::dc_arrow ); @@ -188,18 +188,17 @@ public: io.AddInputCharacter( code ); } + // always pass keycodes to imgui, WantCaptureKeyboard should NOT affect whether or not we do this void OnKeyCodePressed( vgui::KeyCode code ) override { auto& io = ImGui::GetIO(); - if ( io.WantCaptureKeyboard ) - io.AddKeyEvent( IMGUI_KEY_TABLE[code], true ); + io.AddKeyEvent( IMGUI_KEY_TABLE[code], true ); } void OnKeyCodeReleased( vgui::KeyCode code ) override { auto& io = ImGui::GetIO(); - if ( io.WantCaptureKeyboard ) - io.AddKeyEvent( IMGUI_KEY_TABLE[code], false ); + io.AddKeyEvent( IMGUI_KEY_TABLE[code], false ); } void Paint() override @@ -231,6 +230,11 @@ bool CDearImGuiSystem::Init() ImGui::SetAllocatorFunctions( ImGui_MemAlloc, ImGui_MemFree, nullptr ); ImFontAtlas *atlas = new ImFontAtlas(); ImGui::CreateContext( atlas ); + + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + ImGui_ImplSource_Init(); SetStyle(); @@ -379,7 +383,8 @@ void CDearImGuiSystem::UnregisterWindowFactories( IImguiWindow **ppWindows, int //---------------------------------------------------------------------------------------// IImguiWindow *CDearImGuiSystem::FindWindow( const char *szName ) { - if ( auto it = m_ImGuiWindows.Find( szName ); it != m_ImGuiWindows.InvalidIndex() ) + int it = m_ImGuiWindows.Find( szName ); + if ( it != m_ImGuiWindows.InvalidIndex() ) return m_ImGuiWindows[it]; return nullptr; } @@ -606,7 +611,7 @@ static ConCommand imgui_input_end( CON_COMMAND_F( imgui_toggle_menu, "Toggles the imgui menu bar", FCVAR_CLIENTDLL ) { g_ImguiSystem.ToggleMenuBar(); - if ( g_ImguiSystem.IsDrawingMenuBar() ) + if ( g_ImguiSystem.IsDrawingMenuBar() ) g_ImguiSystem.PushInputContext(); else g_ImguiSystem.PopInputContext(); @@ -616,7 +621,7 @@ template void CC_ToggleMenu( const CCommand &args ) { g_ImguiSystem.SetDrawMenuBar( ON ); - if constexpr ( ON ) + if ( ON ) g_ImguiSystem.PushInputContext(); else g_ImguiSystem.PopInputContext(); diff --git a/game/client/imgui/test_window.cpp b/game/client/imgui/test_window.cpp new file mode 100644 index 00000000..c440b886 --- /dev/null +++ b/game/client/imgui/test_window.cpp @@ -0,0 +1,25 @@ +/** + * @file test_window.cpp + * @brief A demo window for reference. + */ + +#include "cdll_client_int.h" +#include "convar.h" +#include "imgui_window.h" + +DECLARE_IMGUI_WINDOW(test_window, "Test window") +{ + ImGui::Text("Demo text"); + + if (ImGui::Button("Print")) + { + Msg("Hello ImGui!\n"); + } + + if (ImGui::Button("Impulse 101")) + { + engine->ClientCmd("impulse 101"); + } + + return false; +} diff --git a/game/client/in_touch.cpp b/game/client/in_touch.cpp index e2bbeaae..5dfaa64a 100644 --- a/game/client/in_touch.cpp +++ b/game/client/in_touch.cpp @@ -80,8 +80,8 @@ void CInput::ApplyTouch( QAngle &viewangles, CUserCmd *cmd, float dx, float dy ) { viewangles[YAW] -= dx; viewangles[PITCH] += dy; - cmd->mousedx = dx; - cmd->mousedy = dy; + cmd->mousedx += dx; + cmd->mousedy += dy; } void CInput::TouchMove( CUserCmd *cmd ) diff --git a/game/client/portal/remake/c_env_portal_laser.cpp b/game/client/portal/remake/c_env_portal_laser.cpp index c3b2fb3a..46c8d8cb 100644 --- a/game/client/portal/remake/c_env_portal_laser.cpp +++ b/game/client/portal/remake/c_env_portal_laser.cpp @@ -111,10 +111,6 @@ void C_PortalLaser::LaserOn() return; } - Vector vecMuzzle; - QAngle angMuzzleDir; - GetAttachment( m_iAttachmentId, vecMuzzle, angMuzzleDir ); - QAngle angAimDir = GetAbsAngles(); Vector vecAimDir; AngleVectors ( angAimDir, &vecAimDir ); @@ -136,7 +132,7 @@ void C_PortalLaser::LaserOn() m_pBeam->SetHaloScale( 5.5f ); m_pBeam->SetBeamFlag( FBEAM_REVERSED ); m_pBeam->SetCollisionGroup( COLLISION_GROUP_NONE ); - m_pBeam->PointsInit( vecMuzzle + vecAimDir, vecMuzzle ); + m_pBeam->PointsInit( m_vStartPoint + vecAimDir, m_vEndPoint ); m_pBeam->SetStartEntity( this ); } else @@ -148,12 +144,12 @@ void C_PortalLaser::LaserOn() Vector vEndPoint; float fEndFraction; Ray_t rayPath; - rayPath.Init( vecMuzzle, vecMuzzle + vecAimDir * 8192 ); + rayPath.Init( m_vStartPoint, m_vStartPoint + vecAimDir * 8192 ); if ( UTIL_Portal_TraceRay_Beam( rayPath, MASK_SHOT, &m_filterBeams, &fEndFraction ) ) - vEndPoint = vecMuzzle + vecAimDir * 8192; // Trace went through portal and endpoint is unknown + vEndPoint = m_vStartPoint + vecAimDir * 8192; // Trace went through portal and endpoint is unknown else - vEndPoint = vecMuzzle + vecAimDir * 8192 * fEndFraction; // Trace hit a wall + vEndPoint = m_vStartPoint + vecAimDir * 8192 * fEndFraction; // Trace hit a wall - m_pBeam->PointsInit( vEndPoint, vecMuzzle ); + m_pBeam->PointsInit( vEndPoint, m_vStartPoint ); } diff --git a/game/client/portal/remake/c_prop_waightedcube.cpp b/game/client/portal/remake/c_prop_waightedcube.cpp new file mode 100644 index 00000000..27d0db12 --- /dev/null +++ b/game/client/portal/remake/c_prop_waightedcube.cpp @@ -0,0 +1,143 @@ +/** + * @file c_env_portal_laser.cpp + * @brief Adds lasers from Portal 2 + * + * Mostly just copypasted from the rocket turret + * + * @bug Laser can be blocked by a player when laser goes through a portal. + */ + +#include "cbase.h" +#include "c_physicsprop.h" +#include "beam_shared.h" +#include "mathlib/vector.h" +#include "portal_util_shared.h" + +class C_PropWeightedCube : public C_PhysicsProp +{ +public: + DECLARE_CLASS( C_PropWeightedCube, C_PhysicsProp ); + DECLARE_CLIENTCLASS(); + + C_PropWeightedCube( void ); + ~C_PropWeightedCube( void ); + + void Spawn( void ); + void ClientThink( void ); + + void LaserOff( void ); + void LaserOn( void ); + float LaserEndPointSize( void ); + +private: + CBeam *m_pBeam; + CTraceFilterSkipTwoEntities m_filterBeams; + + int m_nSiteHalo; + int m_iAttachmentId; + + //////////// + //CNewParticleEffect m_pSparkEffect; + bool m_bLaserOn; + bool m_bIsLethal; + //////////// +}; + +LINK_ENTITY_TO_CLASS( prop_weighted_cube, C_PropWeightedCube ); +IMPLEMENT_CLIENTCLASS_DT( C_PropWeightedCube, DT_PropWeightedCube, CPropWeightedCube ) + RecvPropBool( RECVINFO( m_bLaserOn ) ), + RecvPropBool( RECVINFO( m_bIsLethal ) ), +END_RECV_TABLE() + + +C_PropWeightedCube::C_PropWeightedCube( void ) + : m_filterBeams( this, UTIL_PlayerByIndex( 1 ), COLLISION_GROUP_DEBRIS ) + //m_pSparkEffect() +{ +} + +C_PropWeightedCube::~C_PropWeightedCube( void ) +{ + LaserOff(); + if( m_pBeam ) + m_pBeam->Remove(); +} + +void C_PropWeightedCube::Spawn() +{ + SetThink( &C_PropWeightedCube::ClientThink ); + SetNextClientThink( CLIENT_THINK_ALWAYS ); + + m_pBeam = NULL; + + BaseClass::Spawn(); +} + +void C_PropWeightedCube::ClientThink() +{ + if( m_bLaserOn ) + LaserOn(); + else + LaserOff(); +} + +void C_PropWeightedCube::LaserOff() +{ + if( m_pBeam ) + m_pBeam->AddEffects( EF_NODRAW ); +} + +void C_PropWeightedCube::LaserOn() +{ + if ( !IsBoneAccessAllowed() ) + { + LaserOff(); + return; + } + + Vector vecMuzzle; + QAngle angMuzzleDir; + GetAttachment( m_iAttachmentId, vecMuzzle, angMuzzleDir ); + + QAngle angAimDir = GetAbsAngles(); + Vector vecAimDir; + AngleVectors ( angAimDir, &vecAimDir ); + + if(!m_pBeam) + { + if(m_bIsLethal) + m_pBeam = CBeam::BeamCreate("sprites/laserbeam.vmt", 2); + else + m_pBeam = CBeam::BeamCreate("sprites/purplelaser1.vmt", 32);; + m_pBeam->SetHaloTexture( m_nSiteHalo ); + m_pBeam->SetColor( 255, 255, 255 ); + m_pBeam->SetBrightness( 255 ); + m_pBeam->SetNoise( 0 ); + m_pBeam->SetWidth( 2 ); + m_pBeam->SetEndWidth( 0 ); + m_pBeam->SetScrollRate( 0 ); + m_pBeam->SetFadeLength( 0 ); + m_pBeam->SetHaloScale( 5.5f ); + m_pBeam->SetBeamFlag( FBEAM_REVERSED ); + m_pBeam->SetCollisionGroup( COLLISION_GROUP_NONE ); + m_pBeam->PointsInit( vecMuzzle + vecAimDir, vecMuzzle ); + m_pBeam->SetStartEntity( this ); + } + else + { + m_pBeam->RemoveEffects( EF_NODRAW ); + } + + // Trace to find an endpoint (so the beam draws through portals) + Vector vEndPoint; + float fEndFraction; + Ray_t rayPath; + rayPath.Init( vecMuzzle, vecMuzzle + vecAimDir * 8192 ); + + if ( UTIL_Portal_TraceRay_Beam( rayPath, MASK_SHOT, &m_filterBeams, &fEndFraction ) ) + vEndPoint = vecMuzzle + vecAimDir * 8192; // Trace went through portal and endpoint is unknown + else + vEndPoint = vecMuzzle + vecAimDir * 8192 * fEndFraction; // Trace hit a wall + + m_pBeam->PointsInit( vEndPoint, vecMuzzle ); +} diff --git a/game/client/viewrender.cpp b/game/client/viewrender.cpp index 193423c1..e1de23bd 100644 --- a/game/client/viewrender.cpp +++ b/game/client/viewrender.cpp @@ -1913,6 +1913,9 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT m_CurrentView = view; + if ( building_cubemaps.GetBool() ) + m_CurrentView.fov = RAD2DEG( 2.0f * atanf( 64.0f / ( 64 - 0.5f ) ) ); + C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true ); VPROF( "CViewRender::RenderView" ); tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); @@ -4380,6 +4383,9 @@ void CRendering3dView::DrawTranslucentRenderables( bool bInSkybox, bool bShadowD return; } + if( !m_pWorldListInfo ) + return; + VPROF_BUDGET( "CViewRender::DrawTranslucentRenderables", "DrawTranslucentRenderables" ); int iPrevLeaf = info.m_LeafCount - 1; int nDetailLeafCount = 0; diff --git a/game/server/portal/portal_client.cpp b/game/server/portal/portal_client.cpp index 03a2ed82..54cc194d 100644 --- a/game/server/portal/portal_client.cpp +++ b/game/server/portal/portal_client.cpp @@ -14,6 +14,7 @@ */ #include "cbase.h" +#include "convar.h" #include "portal_player.h" #include "portal_gamerules.h" #include "gamerules.h" @@ -121,11 +122,11 @@ void ClientGamePrecache( void ) CBaseEntity::PrecacheModel( "models/portals/portal2.mdl" ); } - +ConVar sv_respawn_in_sp( "sv_respawn_in_sp", "0" ); // called by ClientKill and DeadThink void respawn( CBaseEntity *pEdict, bool fCopyCorpse ) { - if (gpGlobals->coop || gpGlobals->deathmatch) + if (sv_respawn_in_sp.GetBool() || gpGlobals->coop || gpGlobals->deathmatch) { if ( fCopyCorpse ) { diff --git a/game/server/portal/prop_portal.cpp b/game/server/portal/prop_portal.cpp index 61a2aac8..3fad04ea 100644 --- a/game/server/portal/prop_portal.cpp +++ b/game/server/portal/prop_portal.cpp @@ -35,15 +35,14 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" - -#define MINIMUM_FLOOR_PORTAL_EXIT_VELOCITY 50.0f -#define MINIMUM_FLOOR_TO_FLOOR_PORTAL_EXIT_VELOCITY 225.0f -#define MINIMUM_FLOOR_PORTAL_EXIT_VELOCITY_PLAYER 300.0f -#define MAXIMUM_PORTAL_EXIT_VELOCITY 1000.0f - CCallQueue *GetPortalCallQueue(); +ConVar sv_portal_minimum_floor_exit_velocity("sv_portal_minimum_floor_exit_velocity", "50.0", FCVAR_REPLICATED ); +ConVar sv_portal_minimum_floor_to_floor_exit_velocity("sv_portal_minimum_floor_to_floor_exit_velocity", "225.0", FCVAR_REPLICATED ); +ConVar sv_portal_minimum_floor_exit_velocity_player("sv_portal_minimum_floor_exit_velocity_player", "300.0", FCVAR_REPLICATED ); +ConVar sv_portal_maximum_exit_velocity("sv_portal_maximum_exit_velocity", "1000.0", FCVAR_REPLICATED ); + ConVar sv_portal_debug_touch("sv_portal_debug_touch", "0", FCVAR_REPLICATED ); ConVar sv_portal_placement_never_fail("sv_portal_placement_never_fail", "0", FCVAR_REPLICATED | FCVAR_CHEAT ); ConVar sv_portal_new_velocity_check("sv_portal_new_velocity_check", "1", FCVAR_CHEAT ); @@ -1133,27 +1132,27 @@ void CProp_Portal::TeleportTouchingEntity( CBaseEntity *pOther ) { if ( bPlayer ) { - if( vNewVelocity.z < MINIMUM_FLOOR_PORTAL_EXIT_VELOCITY_PLAYER ) - vNewVelocity.z = MINIMUM_FLOOR_PORTAL_EXIT_VELOCITY_PLAYER; + if( vNewVelocity.z < sv_portal_minimum_floor_exit_velocity_player.GetFloat() ) + vNewVelocity.z = sv_portal_minimum_floor_exit_velocity_player.GetFloat(); } else { if( LocalPortalDataAccess.Placement.vForward.z > 0.7071f ) { - if( vNewVelocity.z < MINIMUM_FLOOR_TO_FLOOR_PORTAL_EXIT_VELOCITY ) - vNewVelocity.z = MINIMUM_FLOOR_TO_FLOOR_PORTAL_EXIT_VELOCITY; + if( vNewVelocity.z < sv_portal_minimum_floor_to_floor_exit_velocity.GetFloat() ) + vNewVelocity.z = sv_portal_minimum_floor_to_floor_exit_velocity.GetFloat(); } else { - if( vNewVelocity.z < MINIMUM_FLOOR_PORTAL_EXIT_VELOCITY ) - vNewVelocity.z = MINIMUM_FLOOR_PORTAL_EXIT_VELOCITY; + if( vNewVelocity.z < sv_portal_minimum_floor_exit_velocity.GetFloat() ) + vNewVelocity.z = sv_portal_minimum_floor_exit_velocity.GetFloat(); } } } - if ( vNewVelocity.LengthSqr() > (MAXIMUM_PORTAL_EXIT_VELOCITY * MAXIMUM_PORTAL_EXIT_VELOCITY) ) - vNewVelocity *= (MAXIMUM_PORTAL_EXIT_VELOCITY / vNewVelocity.Length()); + if ( vNewVelocity.LengthSqr() > (sv_portal_maximum_exit_velocity.GetFloat() * sv_portal_maximum_exit_velocity.GetFloat()) ) + vNewVelocity *= (sv_portal_maximum_exit_velocity.GetFloat() / vNewVelocity.Length()); } //untouch the portal(s), will force a touch on destination after the teleport diff --git a/game/server/portal/remake/env_portal_laser.cpp b/game/server/portal/remake/env_portal_laser.cpp index d1f5000e..46b14f64 100644 --- a/game/server/portal/remake/env_portal_laser.cpp +++ b/game/server/portal/remake/env_portal_laser.cpp @@ -66,6 +66,9 @@ private: CNetworkVar( bool, m_bUseParentDir ); CNetworkVector( m_angParentAngles ); QAngle m_angPortalExitAngles; + + // Custom fields + CBaseEntity* m_LastEntity; }; // Start of our data description for the class @@ -229,9 +232,14 @@ void CPortalLaser::StrikeThink( void ) { Ray_t rayDmg; Vector vForward; - AngleVectors( GetAbsAngles(), &vForward, NULL, NULL ); - Vector vEndPoint = EyePosition() + vForward*8192; - rayDmg.Init( EyePosition(), vEndPoint ); + Vector vStartPoint; + GetAttachment(m_iLaserAttachment, vStartPoint, &vForward); + + // Source. + m_vStartPoint = vStartPoint; + + m_vEndPoint = m_vStartPoint + vForward*8192; + rayDmg.Init( m_vStartPoint, m_vEndPoint ); rayDmg.m_IsRay = true; trace_t traceDmg; @@ -242,6 +250,27 @@ void CPortalLaser::StrikeThink( void ) CProp_Portal* pFirstPortal = UTIL_Portal_FirstAlongRay( rayDmg, flRequiredParameter ); UTIL_Portal_TraceRay_Bullets( pFirstPortal, rayDmg, MASK_VISIBLE_AND_NPCS, &filter, &traceDmg, false ); + if ( m_LastEntity && traceDmg.m_pEnt != m_LastEntity ) + { + if( FClassnameIs( m_LastEntity, "prop_weighted_cube" ) && UTIL_IsReflectiveCube( m_LastEntity ) ) + { + //Set the cube to activate + CPropWeightedCube* pCube = assert_cast( m_LastEntity ); + if( pCube ) + { + pCube->SetActivated( false ); + } + } + else if( FClassnameIs( m_LastEntity, "prop_laser_catcher" )) + { + CPropLaserCatcher* pCatcher = assert_cast( m_LastEntity ); + if( pCatcher ) + { + pCatcher->SetActivated( false ); + } + } + } + if ( traceDmg.m_pEnt ) { if ( traceDmg.m_pEnt->IsPlayer() ) @@ -254,11 +283,11 @@ void CPortalLaser::StrikeThink( void ) else if( FClassnameIs( traceDmg.m_pEnt, "prop_weighted_cube" ) && UTIL_IsReflectiveCube( traceDmg.m_pEnt ) ) { //Set the cube to activate - // TODO: Make lasers come from cubes CPropWeightedCube* pCube = assert_cast( traceDmg.m_pEnt ); if( pCube ) { pCube->SetActivated( true ); + m_LastEntity = traceDmg.m_pEnt; } } else if( FClassnameIs( traceDmg.m_pEnt, "prop_laser_catcher" )) @@ -267,6 +296,7 @@ void CPortalLaser::StrikeThink( void ) if( pCatcher ) { pCatcher->SetActivated( true ); + m_LastEntity = traceDmg.m_pEnt; } } } diff --git a/game/server/portal/remake/prop_laser_catcher.cpp b/game/server/portal/remake/prop_laser_catcher.cpp index 317adfe2..1b44df08 100644 --- a/game/server/portal/remake/prop_laser_catcher.cpp +++ b/game/server/portal/remake/prop_laser_catcher.cpp @@ -24,7 +24,6 @@ BEGIN_DATADESC( CPropLaserCatcher ) END_DATADESC() CPropLaserCatcher::CPropLaserCatcher() - : m_bAlreadyActivated( false ) { } @@ -91,17 +90,12 @@ void CPropLaserCatcher::Spawn( void ) void CPropLaserCatcher::SetActivated( bool bActivate ) { - m_bActivated = bActivate; -} - -/** - * @brief Updates the laser catcher - */ -void CPropLaserCatcher::CatcherThink( void ) -{ - if( m_bActivated ) + if( m_bActivated != bActivate ) { - if( !m_bAlreadyActivated ) + m_bActivated = bActivate; + SetCatcherSkin(); + + if( bActivate ) { m_OnPowered.FireOutput( this, this ); @@ -111,17 +105,11 @@ void CPropLaserCatcher::CatcherThink( void ) EmitSound("prop_laser_catcher.powerloop"); StopSound("prop_laser_catcher.poweroff"); - SetCatcherSkin(); SetSequence( m_PowerOnSequence ); SetPlaybackRate( 1.0f ); UseClientSideAnimation(); } - m_bAlreadyActivated = true; - m_bActivated = false; // This is a dumb way of making the catcher power off. - } - else - { - if( m_bAlreadyActivated ) + else { m_OnUnpowered.FireOutput( this, this ); @@ -130,13 +118,17 @@ void CPropLaserCatcher::CatcherThink( void ) EmitSound("prop_laser_catcher.poweroff"); StopSound("prop_laser_catcher.powerloop"); - SetCatcherSkin(); SetSequence( m_IdleSequence ); SetPlaybackRate( 1.0f ); UseClientSideAnimation(); } - - m_bAlreadyActivated = false; } +} + +/** + * @brief Updates the laser catcher + */ +void CPropLaserCatcher::CatcherThink( void ) +{ SetNextThink( gpGlobals->curtime + 0.2f ); } \ No newline at end of file diff --git a/game/server/portal/remake/prop_laser_catcher.h b/game/server/portal/remake/prop_laser_catcher.h index 07cadf00..27114898 100644 --- a/game/server/portal/remake/prop_laser_catcher.h +++ b/game/server/portal/remake/prop_laser_catcher.h @@ -31,6 +31,5 @@ private: int m_iTargetAttachment; bool m_bActivated; - bool m_bAlreadyActivated; bool m_bRusted; }; diff --git a/game/server/portal/remake/prop_weightedcube.cpp b/game/server/portal/remake/prop_weightedcube.cpp index 11d32175..f189e59d 100644 --- a/game/server/portal/remake/prop_weightedcube.cpp +++ b/game/server/portal/remake/prop_weightedcube.cpp @@ -11,9 +11,11 @@ #include "datacache/imdlcache.h" #include "portal_util_shared.h" -#include +// Laser implementation. +#include "prop_laser_catcher.h" +#include "physicsshadowclone.h" -LINK_ENTITY_TO_CLASS( prop_weighted_cube, CPropWeightedCube ); +#include BEGIN_DATADESC( CPropWeightedCube ) @@ -28,9 +30,17 @@ BEGIN_DATADESC( CPropWeightedCube ) DEFINE_INPUTFUNC( FIELD_VOID, "Dissolve", InputDissolve ), DEFINE_INPUTFUNC( FIELD_VOID, "SilentDissolve", InputDissolve ), // InputSilentDissolve + DEFINE_THINKFUNC( StrikeThink ), END_DATADESC() +IMPLEMENT_SERVERCLASS_ST(CPropWeightedCube, DT_PropWeightedCube) + SendPropBool( SENDINFO( m_bLaserOn ) ), + SendPropBool( SENDINFO( m_bIsLethal ) ), +END_SEND_TABLE() + +LINK_ENTITY_TO_CLASS( prop_weighted_cube, CPropWeightedCube ); + const char *CUBE_MODEL = "models/props/metal_box.mdl"; const char *CUBE_REFLECT_MODEL = "models/props/reflection_cube.mdl"; const char *CUBE_SPHERE_MODEL = "models/props_gameplay/mp_ball.mdl"; @@ -231,8 +241,23 @@ void CPropWeightedCube::SetCubeType( void ) //----------------------------------------------------------------------------- void CPropWeightedCube::SetActivated( bool bActivate ) { + if(m_bActivated == bActivate) + return; + m_bActivated = bActivate; + if(bActivate && m_nCubeType == CUBE_REFLECTIVE) + { + m_bLaserOn = true; + SetThink(&CPropWeightedCube::StrikeThink); + SetNextThink( gpGlobals->curtime ); + } + else + { + m_bLaserOn = false; + SetThink(nullptr); + } + SetCubeSkin(); } @@ -548,6 +573,92 @@ void CPropWeightedCube::Spawn( void ) SetFadeDistance( -1.0f, 0.0f ); } +extern ConVar sv_portal_laser_normal_update; +extern ConVar sv_portal_laser_high_precision_update; +//----------------------------------------------------------------------------- +// Purpose: Toggle the laser on and off +// TODO: Not how it should be implemented! +//----------------------------------------------------------------------------- +void CPropWeightedCube::StrikeThink( void ) +{ + Ray_t rayDmg; + Vector vForward; + AngleVectors( GetAbsAngles(), &vForward, NULL, NULL ); + Vector vEndPoint = EyePosition() + vForward*8192; + rayDmg.Init( EyePosition(), vEndPoint ); + rayDmg.m_IsRay = true; + trace_t traceDmg; + + // This version reorients through portals + CTraceFilterSimple subfilter( this, COLLISION_GROUP_NONE ); + CTraceFilterTranslateClones filter ( &subfilter ); + float flRequiredParameter = 2.0f; + CProp_Portal* pFirstPortal = UTIL_Portal_FirstAlongRay( rayDmg, flRequiredParameter ); + UTIL_Portal_TraceRay_Bullets( pFirstPortal, rayDmg, MASK_VISIBLE_AND_NPCS, &filter, &traceDmg, false ); + + if ( m_LastEntity && traceDmg.m_pEnt != m_LastEntity ) + { + if( FClassnameIs( m_LastEntity, "prop_weighted_cube" ) && UTIL_IsReflectiveCube( m_LastEntity ) ) + { + //Set the cube to activate + CPropWeightedCube* pCube = assert_cast( m_LastEntity ); + if( pCube ) + { + pCube->SetActivated( false ); + } + } + else if( FClassnameIs( m_LastEntity, "prop_laser_catcher" )) + { + CPropLaserCatcher* pCatcher = assert_cast( m_LastEntity ); + if( pCatcher ) + { + pCatcher->SetActivated( false ); + } + } + } + + if ( traceDmg.m_pEnt ) + { + if ( traceDmg.m_pEnt->IsPlayer() ) + { + CTakeDamageInfo dmgInfo; + dmgInfo.SetDamage( 1 ); + dmgInfo.SetDamageType( DMG_ENERGYBEAM ); + traceDmg.m_pEnt->TakeDamage( dmgInfo ); + } + else if( FClassnameIs( traceDmg.m_pEnt, "prop_weighted_cube" ) && UTIL_IsReflectiveCube( traceDmg.m_pEnt ) ) + { + //Set the cube to activate + CPropWeightedCube* pCube = assert_cast( traceDmg.m_pEnt ); + if( pCube ) + { + pCube->SetActivated( true ); + m_LastEntity = traceDmg.m_pEnt; + } + } + else if( FClassnameIs( traceDmg.m_pEnt, "prop_laser_catcher" )) + { + CPropLaserCatcher* pCatcher = assert_cast( traceDmg.m_pEnt ); + if( pCatcher ) + { + pCatcher->SetActivated( true ); + m_LastEntity = traceDmg.m_pEnt; + } + } + } + + // FIXME: + bool m_bFromReflectedCube = false; + + float fTime; + if(m_bFromReflectedCube) + fTime = sv_portal_laser_normal_update.GetFloat(); + else + fTime = sv_portal_laser_high_precision_update.GetFloat(); + + SetNextThink(gpGlobals->curtime + fTime); +} + //----------------------------------------------------------------------------- // Creates a weighted cube of a specific type //----------------------------------------------------------------------------- diff --git a/game/server/portal/remake/prop_weightedcube.h b/game/server/portal/remake/prop_weightedcube.h index cc2c6003..9617cd98 100644 --- a/game/server/portal/remake/prop_weightedcube.h +++ b/game/server/portal/remake/prop_weightedcube.h @@ -86,6 +86,7 @@ class CPropWeightedCube : public CPhysicsProp { public: DECLARE_CLASS( CPropWeightedCube, CPhysicsProp ); + DECLARE_SERVERCLASS(); DECLARE_DATADESC(); CPropWeightedCube(); @@ -116,6 +117,8 @@ public: return m_hLaser.Get() != NULL; } + void StrikeThink( void ); + static void CreatePortalWeightedCube( WeightedCubeType_e objectType, bool bAtCursorPosition = true, const Vector &position = vec3_origin ); private: @@ -137,6 +140,14 @@ private: bool m_bNewSkins; EHANDLE m_hLaser; + + // TODO: Laser vars don't belong here, remove. + CNetworkVar( bool, m_bLaserOn ); + CNetworkVar( bool, m_bIsLethal ); + + CBaseEntity* m_LastEntity; + + // }; bool UTIL_IsReflectiveCube( CBaseEntity *pEntity ); diff --git a/game/server/portal2/indicator_panel.cpp b/game/server/portal2/indicator_panel.cpp new file mode 100644 index 00000000..c6b1001d --- /dev/null +++ b/game/server/portal2/indicator_panel.cpp @@ -0,0 +1,186 @@ +/** + * @file indicator_panel.cpp + * @brief + * @date 2023-10-22 + */ + +#include "cbase.h" +#include "entitylist.h" +#include "util.h" + +// Name of our entity's model +#define ENTITY_MODEL "models/gibs/airboat_broken_engine.mdl" + +class CPropIndicatorPanel : public CBaseAnimating +{ +public: + DECLARE_CLASS( CPropIndicatorPanel, CBaseAnimating ); + DECLARE_DATADESC(); + + CPropIndicatorPanel() + { + } + + void Spawn( void ); + + void Precache( void ); + + void CreateIndicatorPanel( void ); + void SetTimerDuration( float fDuration); + + // Input function + void InputCheck( inputdata_t &inputData ); + void InputUncheck( inputdata_t &inputData ); + void InputStart( inputdata_t &inputData ); + void InputStop( inputdata_t &inputData ); + void InputReset( inputdata_t &inputData ); + + void ToggleThink( void ); + + void StartTimer( void ); + void ResetTimer( void ); + + void ToggleIndicatorLights( bool bLightsOn ); + +private: + bool m_bEnabled; + bool m_bStopped; + bool m_bCountingDown; + bool m_bIsCountdownTimer; + bool m_bIsChecked; + + string_t m_strIndicatorLights; + float m_flTimerDuration; + float m_flTimerStart; + + // CHandle m_hIndicatorPanel; +}; + +LINK_ENTITY_TO_CLASS( prop_indicator_panel, CPropIndicatorPanel ); + +// Start of our data description for the class +BEGIN_DATADESC( CPropIndicatorPanel ) + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bStopped, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bCountingDown, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bIsCountdownTimer, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bIsChecked, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_flTimerDuration, FIELD_TIME ), + DEFINE_FIELD( m_flTimerStart, FIELD_TIME ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Check", InputCheck ), + DEFINE_INPUTFUNC( FIELD_VOID, "Uncheck", InputUncheck ), + DEFINE_INPUTFUNC( FIELD_VOID, "Start", InputStart ), + DEFINE_INPUTFUNC( FIELD_VOID, "Stop", InputStop ), + DEFINE_INPUTFUNC( FIELD_VOID, "Reset", InputReset ), + + // Declare our think function + DEFINE_THINKFUNC( ToggleThink ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Precache assets used by the entity +//----------------------------------------------------------------------------- +void CPropIndicatorPanel::Precache( void ) +{ + PrecacheModel( ENTITY_MODEL ); + + BaseClass::Precache(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets up the entity's initial state +//----------------------------------------------------------------------------- +void CPropIndicatorPanel::Spawn( void ) +{ + Precache(); + + SetModel( ENTITY_MODEL ); + SetSolid( SOLID_VPHYSICS ); + SetMoveType(MOVETYPE_NONE); + CreateIndicatorPanel(); +} + +void CPropIndicatorPanel::CreateIndicatorPanel( void ) +{ + // TODO +} + +void CPropIndicatorPanel::InputCheck( inputdata_t &inputData ) +{ + // TODO: Implement vgui indecator. + { + + } + + SetThink( NULL ); + ToggleIndicatorLights( true ); +} + +void CPropIndicatorPanel::InputUncheck( inputdata_t &inputData ) +{ + // TODO: Implement vgui indecator. + { + + } + + SetThink( NULL ); + ToggleIndicatorLights( false ); +} + +void CPropIndicatorPanel::InputStart( inputdata_t &inputData ) +{ + StartTimer(); +} +void CPropIndicatorPanel::InputReset( inputdata_t &inputData ) +{ + ResetTimer(); +} + +// Does nothing. +void CPropIndicatorPanel::InputStop( inputdata_t &inputData ) +{ + return; +} + +void CPropIndicatorPanel::StartTimer( void ) +{ + return; +} + +void CPropIndicatorPanel::ResetTimer( void ) +{ + return; +} + + +void CPropIndicatorPanel::ToggleThink( void ) +{ + return; +} + + +void CPropIndicatorPanel::ToggleIndicatorLights( bool bLightsOn ) +{ + CBaseEntity* pStartEntity = NULL; + const char* indicator_lights = m_strIndicatorLights.ToCStr(); + while( true ) + { + if(indicator_lights == NULL || *indicator_lights == '\0') + indicator_lights = ""; + + pStartEntity = gEntList.FindEntityByName( pStartEntity, indicator_lights ); + + if (pStartEntity == NULL) + break; + } + return; +} + +void CPropIndicatorPanel::SetTimerDuration( float fDuration ) +{ + m_flTimerDuration = fDuration; +} diff --git a/game/server/portal2/npc_personality_core.cpp b/game/server/portal2/npc_personality_core.cpp index 3719caa8..be000fd1 100644 --- a/game/server/portal2/npc_personality_core.cpp +++ b/game/server/portal2/npc_personality_core.cpp @@ -1,3 +1,10 @@ +/** + * @file npc_personality_core.cpp + * @brief Placeholder code for personality cores. + * + * @author RocketLauncher21 https://github.com/RocketLauncher21/Portal2ASW + */ + #include "cbase.h" #include "ai_baseactor.h" #include "ai_basenpc.h" diff --git a/game/server/portal2/npc_wheatley_boss.cpp b/game/server/portal2/npc_wheatley_boss.cpp new file mode 100644 index 00000000..84f52fca --- /dev/null +++ b/game/server/portal2/npc_wheatley_boss.cpp @@ -0,0 +1,60 @@ +/** + * @file npc_wheatly_boss.cpp + * @brief Placeholder code for wheatley boss. + * + * @author RocketLauncher21 https://github.com/RocketLauncher21/Portal2ASW + */ + +#include "cbase.h" +#include "ai_baseactor.h" +#include "ai_basenpc.h" +#include "props.h" + +#define BOSS_MODEL "models/npcs/glados/glados_wheatley_boss.mdl" + +class CNPCWheatleyBoss : public CAI_BaseActor +{ +public: + DECLARE_CLASS(CNPCWheatleyBoss, CAI_BaseActor); + DECLARE_DATADESC(); + + CNPCWheatleyBoss() + { + } + + bool CreateVPhysics(void) + { + VPhysicsInitNormal(SOLID_VPHYSICS, 0, false); + return true; + } + + void Spawn(void); + void Precache(void); +private: + CHandle m_hPhysicsAttacker; + COutputEvent m_OnPlayerPickup; + COutputEvent m_OnPhysGunDrop; +}; + +LINK_ENTITY_TO_CLASS(npc_wheatley_boss, CNPCWheatleyBoss); + +BEGIN_DATADESC(CNPCWheatleyBoss) +END_DATADESC() + +void CNPCWheatleyBoss::Precache(void) +{ + PrecacheModel(BOSS_MODEL); + + BaseClass::Precache(); +} + +void CNPCWheatleyBoss::Spawn(void) +{ + Precache(); + SetModel(BOSS_MODEL); + + SetSolid(SOLID_VPHYSICS); + CreateVPhysics(); + + BaseClass::Spawn(); +} \ No newline at end of file diff --git a/game/server/server_portal.vpc b/game/server/server_portal.vpc index 071287ed..6f290dff 100644 --- a/game/server/server_portal.vpc +++ b/game/server/server_portal.vpc @@ -366,6 +366,8 @@ $Project "Server (Portal)" $File "portal2\prop_button.cpp" $File "portal2\point_changelevel.cpp" $File "portal2\npc_personality_core.cpp" + $File "portal2\npc_wheatley_boss.cpp" + $File "portal2\indicator_panel.cpp" $File "movie_display.cpp" diff --git a/mathlib/polyhedron.cpp b/mathlib/polyhedron.cpp index f4b9e44e..9cfa03f2 100644 --- a/mathlib/polyhedron.cpp +++ b/mathlib/polyhedron.cpp @@ -4,17 +4,20 @@ // // $NoKeywords: $ // -//=============================================================================// +// Notes: For some semblance of clarity. All spatial orientation notation assumes you're +// looking at the polyhedron from an outside point located spherically above +// the primitive in question +// +//======================================================================================// #include "mathlib/polyhedron.h" #include "mathlib/vmatrix.h" #include #include #include "tier1/utlvector.h" -#include "tier1/memhelpers.h" -#ifdef COMPILER_MSVC -#include -#endif +#include "mathlib/ssemath.h" + + struct GeneratePolyhedronFromPlanes_Point; struct GeneratePolyhedronFromPlanes_PointLL; @@ -29,14 +32,15 @@ struct GeneratePolyhedronFromPlanes_UnorderedPolygonLL; Vector FindPointInPlanes( const float *pPlanes, int planeCount ); bool FindConvexShapeLooseAABB( const float *pInwardFacingPlanes, int iPlaneCount, Vector *pAABBMins, Vector *pAABBMaxs ); -CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, const float *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory ); -CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, bool bUseTemporaryMemory ); +CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, int iPointCount, const fltx4 *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory, fltx4 vShiftResultPositions ); +CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, bool bUseTemporaryMemory, fltx4 vShiftResultPositions ); //#define ENABLE_DEBUG_POLYHEDRON_DUMPS //Dumps debug information to disk for use with glview. Requires that tier2 also be in all projects using debug mathlib //#define DEBUG_DUMP_POLYHEDRONS_TO_NUMBERED_GLVIEWS //dumps successfully generated polyhedrons +#define USE_WORLD_CENTERED_POSITIONS //shift all our incoming math towards world origin before performing operations on it, shift it back in the result. -#ifdef _DEBUG -void DumpPolyhedronToGLView( const CPolyhedron *pPolyhedron, const char *pFilename, const VMatrix *pTransform ); +#ifdef DBGFLAG_ASSERT +void DumpPolyhedronToGLView( const CPolyhedron *pPolyhedron, const char *pFilename, const VMatrix *pTransform, const char *szfileOpenOptions = "ab" ); void DumpPlaneToGlView( const float *pPlane, float fGrayScale, const char *pszFileName, const VMatrix *pTransform ); void DumpLineToGLView( const Vector &vPoint1, const Vector &vColor1, const Vector &vPoint2, const Vector &vColor2, float fThickness, FILE *pFile ); void DumpAABBToGLView( const Vector &vCenter, const Vector &vExtents, const Vector &vColor, FILE *pFile ); @@ -49,16 +53,30 @@ static VMatrix s_matIdentity( 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ); + +#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS +void DumpWorkingStatePolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDeadPolygons, + GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines, GeneratePolyhedronFromPlanes_UnorderedLineLL *pDeadLines, + GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPoints, + const char *pFilename, const VMatrix *pTransform ); #endif #if defined( DEBUG_DUMP_POLYHEDRONS_TO_NUMBERED_GLVIEWS ) static int g_iPolyhedronDumpCounter = 0; #endif +#define DBG_ONLY(x) x +#else +#define DBG_ONLY(x) +#endif + + + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#if defined( _DEBUG ) && defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) +#if defined( DBGFLAG_ASSERT ) && defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + void CreateDumpDirectory( const char *szDirectoryName ) { #if defined( WIN32 ) @@ -67,6 +85,11 @@ void CreateDumpDirectory( const char *szDirectoryName ) Assert( false ); //TODO: create directories in linux #endif } + +#define DEBUG_POLYHEDRON_CONVERSION 1 +typedef bool (*PFN_PolyhedronCarvingDebugStepCallback)( CPolyhedron *pPolyhedron ); //function that receives a polyhedron conversion after each cut. For the slowest, surest debugging possible. Returns true if the polyhedron passes mustard, false to dump the current work state +PFN_PolyhedronCarvingDebugStepCallback g_pPolyhedronCarvingDebugStepCallback = NULL; + #endif @@ -128,8 +151,8 @@ static CPolyhedron_TempMemory s_TempMemoryPolyhedron; CPolyhedron *GetTempPolyhedron( unsigned short iVertices, unsigned short iLines, unsigned short iIndices, unsigned short iPolygons ) //grab the temporary polyhedron. Avoids new/delete for quick work. Can only be in use by one chunk of code at a time { - AssertMsg( s_TempMemoryPolyhedron.iReferenceCount == 0, "Temporary polyhedron memory being rewritten before released" ); #ifdef DBGFLAG_ASSERT + AssertMsg( s_TempMemoryPolyhedron.iReferenceCount == 0, "Temporary polyhedron memory being rewritten before released" ); ++s_TempMemoryPolyhedron.iReferenceCount; #endif s_TempMemoryPolyhedron_Buffer.SetCount( (sizeof( Vector ) * iVertices) + @@ -185,13 +208,52 @@ enum PolyhedronPointPlanarity POINT_ALIVE }; -struct GeneratePolyhedronFromPlanes_Point +struct GeneratePolyhedronFromPlanes_Point //must be aligned to 16 byte boundary { - Vector ptPosition; + fltx4 ptPosition; //w = -1 to make plane distance an all addition operation after the multiply GeneratePolyhedronFromPlanes_LineLL *pConnectedLines; //keep these in a clockwise order, circular linking float fPlaneDist; //used in plane cutting PolyhedronPointPlanarity planarity; - int iSaveIndices; + union //temporary work variables + { + int iSaveIndices; + }; + +#ifdef DBGFLAG_ASSERT + struct ClipDebugData_t + { + void Reset( void ) { memset( this, 0, sizeof( *this ) ); } + bool bIsNew; + Vector vWorkingStateColorOverride; + bool bVisited; + float fInitialPlaneDistance; + } debugdata; +#endif +}; + +enum PolyhedronLinePlanarity +{ + LINE_ONPLANE = 0, + LINE_ALIVE = (1 << 0), + LINE_DEAD = (1 << 1), + LINE_CUT = LINE_ALIVE | LINE_DEAD, +}; + +PolyhedronLinePlanarity &operator|=( PolyhedronLinePlanarity &a, const PolyhedronLinePlanarity &b ) +{ + a = (PolyhedronLinePlanarity)((int)a | int(b)); + return a; +} + +struct GeneratePolyhedronFromPlanes_LineLL +{ + GeneratePolyhedronFromPlanes_Line *pLine; + //whatever is referencing the line should know which side of the line it's on (points and polygons). + //for polygons, it's the index back to the polygon's self. It's also which point to follow to continue going clockwise, which makes polygon 0 the one on the left side of an upward facing line vector + //for points, it's the OTHER point's index + int iReferenceIndex; + GeneratePolyhedronFromPlanes_LineLL *pPrev; + GeneratePolyhedronFromPlanes_LineLL *pNext; }; struct GeneratePolyhedronFromPlanes_Line @@ -199,53 +261,105 @@ struct GeneratePolyhedronFromPlanes_Line GeneratePolyhedronFromPlanes_Point *pPoints[2]; //the 2 connecting points in no particular order GeneratePolyhedronFromPlanes_Polygon *pPolygons[2]; //viewing from the outside with the point connections going up, 0 is the left polygon, 1 is the right int iSaveIndices; - bool bAlive; //connected to at least one living point - bool bCut; //connected to at least one dead point + PolyhedronLinePlanarity planarity; + bool bNewLengthThisPass; - GeneratePolyhedronFromPlanes_LineLL *pPointLineLinks[2]; //rather than going into a point and searching for its link to this line, lets just cache it to eliminate searching - GeneratePolyhedronFromPlanes_LineLL *pPolygonLineLinks[2]; //rather than going into a polygon and searching for its link to this line, lets just cache it to eliminate searching + GeneratePolyhedronFromPlanes_LineLL PointLineLinks[2]; //rather than going into a point and searching for its link to this line, lets just cache it to eliminate searching + GeneratePolyhedronFromPlanes_LineLL PolygonLineLinks[2]; //rather than going into a polygon and searching for its link to this line, lets just cache it to eliminate searching #ifdef POLYHEDRON_EXTENSIVE_DEBUGGING int iDebugFlags; #endif -}; -struct GeneratePolyhedronFromPlanes_LineLL -{ - GeneratePolyhedronFromPlanes_Line *pLine; - int iReferenceIndex; //whatever is referencing the line should know which side of the line it's on (points and polygons), for polygons, it's which point to follow to continue going clockwise, which makes polygon 0 the one on the left side of an upward facing line vector, for points, it's the OTHER point's index - GeneratePolyhedronFromPlanes_LineLL *pPrev; - GeneratePolyhedronFromPlanes_LineLL *pNext; + void InitLineLinks( void ) + { + PointLineLinks[0].iReferenceIndex = 1; + PointLineLinks[1].iReferenceIndex = 0; + PolygonLineLinks[0].iReferenceIndex = 0; + PolygonLineLinks[1].iReferenceIndex = 1; + + PointLineLinks[0].pLine = PointLineLinks[1].pLine = PolygonLineLinks[0].pLine = PolygonLineLinks[1].pLine = this; + } + +#ifdef DBGFLAG_ASSERT + struct ClipDebugData_t + { + void Reset( void ) { memset( this, 0, sizeof( *this ) ); } + bool bIsNew; //was generated during this cut + Vector vWorkingStateColorOverride; + bool bTested; + } debugdata; +#endif }; struct GeneratePolyhedronFromPlanes_Polygon { Vector vSurfaceNormal; + //float fNormalDist; GeneratePolyhedronFromPlanes_LineLL *pLines; //keep these in a clockwise order, circular linking - bool bMissingASide; + bool bDead; + bool bHasNewPoints; + bool bMovedExistingPoints; + +#ifdef DBGFLAG_ASSERT + struct ClipDebugData_t + { + void Reset( void ) { memset( this, 0, sizeof( *this ) ); } + bool bIsNew; //only one should be new per clip, unless we triangulate + Vector vWorkingStateColorOverride; + } debugdata; +#endif }; struct GeneratePolyhedronFromPlanes_UnorderedPolygonLL //an unordered collection of polygons { - GeneratePolyhedronFromPlanes_Polygon *pPolygon; + GeneratePolyhedronFromPlanes_Polygon polygon; GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pNext; GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPrev; }; struct GeneratePolyhedronFromPlanes_UnorderedLineLL //an unordered collection of lines { - GeneratePolyhedronFromPlanes_Line *pLine; + GeneratePolyhedronFromPlanes_Line line; GeneratePolyhedronFromPlanes_UnorderedLineLL *pNext; GeneratePolyhedronFromPlanes_UnorderedLineLL *pPrev; }; +#pragma pack( push ) +#pragma pack( 16 ) //help align the position to 16 byte boundaries when in arrays struct GeneratePolyhedronFromPlanes_UnorderedPointLL //an unordered collection of points { - GeneratePolyhedronFromPlanes_Point *pPoint; + GeneratePolyhedronFromPlanes_Point point; GeneratePolyhedronFromPlanes_UnorderedPointLL *pNext; GeneratePolyhedronFromPlanes_UnorderedPointLL *pPrev; }; +#pragma pack(pop) +#ifdef DBGFLAG_ASSERT +void Debug_ResetWorkingStateColorOverrides( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines ) +{ + while( pAllPolygons ) + { + pAllPolygons->polygon.debugdata.vWorkingStateColorOverride.Init(); + pAllPolygons = pAllPolygons->pNext; + } + + while( pAllPoints ) + { + pAllPoints->point.debugdata.vWorkingStateColorOverride.Init(); + pAllPoints = pAllPoints->pNext; + } + + while( pAllLines ) + { + pAllLines->line.debugdata.vWorkingStateColorOverride.Init(); + pAllLines = pAllLines->pNext; + } +} +#define DBG_RESETWORKINGSTATECOLORS() Debug_ResetWorkingStateColorOverrides( pAllPolygons, pAllPoints, pAllLines ); +#else +#define DBG_RESETWORKINGSTATECOLORS() +#endif @@ -256,7 +370,10 @@ CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float AssertMsg( (pExistingPolyhedron->iVertexCount >= 3) && (pExistingPolyhedron->iPolygonCount >= 2), "Polyhedron doesn't meet absolute minimum spec" ); - float *pUsefulPlanes = (float *)stackalloc( sizeof( float ) * 4 * iPlaneCount ); + const size_t kFltX4Align = sizeof( fltx4 ) - 1; + uint8 *pAlignedAlloc = (uint8 *)stackalloc( (sizeof( fltx4 ) * iPlaneCount) + kFltX4Align ); + pAlignedAlloc = (uint8 *)(((size_t)(pAlignedAlloc + kFltX4Align)) & ~kFltX4Align); + fltx4 *pUsefulPlanes = (fltx4 *)pAlignedAlloc; int iUsefulPlaneCount = 0; Vector *pExistingVertices = pExistingPolyhedron->pVertices; @@ -289,11 +406,11 @@ CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float if( iDeadCount != 0 ) { - //at least one point died, this plane yields useful results - pUsefulPlanes[(iUsefulPlaneCount * 4) + 0] = vNormal.x; - pUsefulPlanes[(iUsefulPlaneCount * 4) + 1] = vNormal.y; - pUsefulPlanes[(iUsefulPlaneCount * 4) + 2] = vNormal.z; - pUsefulPlanes[(iUsefulPlaneCount * 4) + 3] = fPlaneDist; + //at least one point died, this plane yields useful results + SubFloat( pUsefulPlanes[iUsefulPlaneCount], 0 ) = vNormal.x; //PolyhedronFloatStandardize( vNormal.x ); + SubFloat( pUsefulPlanes[iUsefulPlaneCount], 1 ) = vNormal.y; //PolyhedronFloatStandardize( vNormal.y ); + SubFloat( pUsefulPlanes[iUsefulPlaneCount], 2 ) = vNormal.z; //PolyhedronFloatStandardize( vNormal.z ); + SubFloat( pUsefulPlanes[iUsefulPlaneCount], 3 ) = fPlaneDist; //PolyhedronFloatStandardize( fPlaneDist ); ++iUsefulPlaneCount; } } @@ -319,10 +436,18 @@ CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float pExistingPolyhedron->iPolygonCount ); } - memutils::copy( pReturn->pVertices, pExistingPolyhedron->pVertices, pReturn->iVertexCount ); - memutils::copy( pReturn->pLines, pExistingPolyhedron->pLines, pReturn->iLineCount ); - memutils::copy( pReturn->pIndices, pExistingPolyhedron->pIndices, pReturn->iIndexCount ); - memutils::copy( pReturn->pPolygons, pExistingPolyhedron->pPolygons, pReturn->iPolygonCount ); + memcpy( pReturn->pVertices, pExistingPolyhedron->pVertices, sizeof( Vector ) * pReturn->iVertexCount ); + memcpy( pReturn->pLines, pExistingPolyhedron->pLines, sizeof( Polyhedron_IndexedLine_t ) * pReturn->iLineCount ); + memcpy( pReturn->pIndices, pExistingPolyhedron->pIndices, sizeof( Polyhedron_IndexedLineReference_t ) * pReturn->iIndexCount ); + memcpy( pReturn->pPolygons, pExistingPolyhedron->pPolygons, sizeof( Polyhedron_IndexedPolygon_t ) * pReturn->iPolygonCount ); + +#if defined( DEBUG_POLYHEDRON_CONVERSION ) + //last bit of debugging from whatever outside source wants this stupid thing + if( g_pPolyhedronCarvingDebugStepCallback != NULL ) + { + AssertMsg( g_pPolyhedronCarvingDebugStepCallback( pReturn ), "Outside conversion failed" ); + } +#endif return pReturn; } @@ -330,35 +455,61 @@ CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float //convert the polyhedron to linked geometry - GeneratePolyhedronFromPlanes_Point *pStartPoints = (GeneratePolyhedronFromPlanes_Point *)stackalloc( pExistingPolyhedron->iVertexCount * sizeof( GeneratePolyhedronFromPlanes_Point ) ); - GeneratePolyhedronFromPlanes_Line *pStartLines = (GeneratePolyhedronFromPlanes_Line *)stackalloc( pExistingPolyhedron->iLineCount * sizeof( GeneratePolyhedronFromPlanes_Line ) ); - GeneratePolyhedronFromPlanes_Polygon *pStartPolygons = (GeneratePolyhedronFromPlanes_Polygon *)stackalloc( pExistingPolyhedron->iPolygonCount * sizeof( GeneratePolyhedronFromPlanes_Polygon ) ); + GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints = (GeneratePolyhedronFromPlanes_UnorderedPointLL *)stackalloc( pExistingPolyhedron->iVertexCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedPointLL ) ); + GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines = (GeneratePolyhedronFromPlanes_UnorderedLineLL *)stackalloc( pExistingPolyhedron->iLineCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedLineLL ) ); + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons = (GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)stackalloc( pExistingPolyhedron->iPolygonCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedPolygonLL ) ); - GeneratePolyhedronFromPlanes_LineLL *pStartLineLinks = (GeneratePolyhedronFromPlanes_LineLL *)stackalloc( pExistingPolyhedron->iLineCount * 4 * sizeof( GeneratePolyhedronFromPlanes_LineLL ) ); - - int iCurrentLineLinkIndex = 0; +#if defined( USE_WORLD_CENTERED_POSITIONS ) + fltx4 vPointOffset = LoadZeroSIMD(); +#endif //setup points for( int i = 0; i != pExistingPolyhedron->iVertexCount; ++i ) { - pStartPoints[i].ptPosition = pExistingPolyhedron->pVertices[i]; - pStartPoints[i].pConnectedLines = NULL; //we won't be circular linking until later + SubFloat( pPoints[i].point.ptPosition, 0 ) = pExistingPolyhedron->pVertices[i].x; + SubFloat( pPoints[i].point.ptPosition, 1 ) = pExistingPolyhedron->pVertices[i].y; + SubFloat( pPoints[i].point.ptPosition, 2 ) = pExistingPolyhedron->pVertices[i].z; + SubFloat( pPoints[i].point.ptPosition, 3 ) = -1.0f; +#if defined( USE_WORLD_CENTERED_POSITIONS ) + vPointOffset = AddSIMD( vPointOffset, pPoints[i].point.ptPosition ); +#endif + pPoints[i].point.pConnectedLines = NULL; //we won't be circular linking until later } +#if defined( USE_WORLD_CENTERED_POSITIONS ) + //move everything towards origin for more precise math + vPointOffset = MulSIMD( vPointOffset, ReplicateX4( -1.0f / (float)pExistingPolyhedron->iVertexCount ) ); + vPointOffset = FloorSIMD( vPointOffset ); + SubFloat( vPointOffset, 3 ) = -1.0f; + + for( int i = 0; i != pExistingPolyhedron->iVertexCount; ++i ) + { + pPoints[i].point.ptPosition = AddSIMD( pPoints[i].point.ptPosition, vPointOffset ); + SubFloat( pPoints[i].point.ptPosition, 3 ) = -1.0f; + } + for( int i = 0; i != iUsefulPlaneCount; ++i ) + { + fltx4 vMul = MulSIMD( pUsefulPlanes[i], vPointOffset ); + SubFloat( pUsefulPlanes[i], 3 ) = SubFloat(vMul, 0) + SubFloat(vMul, 1) + SubFloat(vMul, 2) - SubFloat(vMul, 3 ); + } + fltx4 vResultOffset = NegSIMD( vPointOffset ); + SubFloat( vResultOffset, 3 ) = 0.0f; +#else + fltx4 vResultOffset = LoadZeroSIMD(); +#endif + //setup lines and interlink to points (line links are not yet circularly linked, and are unordered) for( int i = 0; i != pExistingPolyhedron->iLineCount; ++i ) { + pLines[i].line.InitLineLinks(); for( int j = 0; j != 2; ++j ) { - pStartLines[i].pPoints[j] = &pStartPoints[pExistingPolyhedron->pLines[i].iPointIndices[j]]; + pLines[i].line.pPoints[j] = &pPoints[pExistingPolyhedron->pLines[i].iPointIndices[j]].point; - GeneratePolyhedronFromPlanes_LineLL *pLineLink = &pStartLineLinks[iCurrentLineLinkIndex++]; - pStartLines[i].pPointLineLinks[j] = pLineLink; - pLineLink->pLine = &pStartLines[i]; - pLineLink->iReferenceIndex = 1 - j; + GeneratePolyhedronFromPlanes_LineLL *pLineLink = &pLines[i].line.PointLineLinks[j]; //pLineLink->pPrev = NULL; - pLineLink->pNext = pStartLines[i].pPoints[j]->pConnectedLines; - pStartLines[i].pPoints[j]->pConnectedLines = pLineLink; + pLineLink->pNext = pLines[i].line.pPoints[j]->pConnectedLines; + pLines[i].line.pPoints[j]->pConnectedLines = pLineLink; } } @@ -367,38 +518,31 @@ CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float //setup polygons for( int i = 0; i != pExistingPolyhedron->iPolygonCount; ++i ) { - pStartPolygons[i].vSurfaceNormal = pExistingPolyhedron->pPolygons[i].polyNormal; + pPolygons[i].polygon.vSurfaceNormal = pExistingPolyhedron->pPolygons[i].polyNormal; Polyhedron_IndexedLineReference_t *pOffsetPolyhedronLines = &pExistingPolyhedron->pIndices[pExistingPolyhedron->pPolygons[i].iFirstIndex]; - - GeneratePolyhedronFromPlanes_LineLL *pFirstLink = &pStartLineLinks[iCurrentLineLinkIndex]; - pStartPolygons[i].pLines = pFirstLink; //technically going to link to itself on first pass, then get linked properly immediately afterward + GeneratePolyhedronFromPlanes_LineLL *pFirstLink = &pLines[pOffsetPolyhedronLines[0].iLineIndex].line.PolygonLineLinks[pOffsetPolyhedronLines[0].iEndPointIndex]; + pPolygons[i].polygon.pLines = pFirstLink; //technically going to link to itself on first pass, then get linked properly immediately afterward for( int j = 0; j != pExistingPolyhedron->pPolygons[i].iIndexCount; ++j ) { - GeneratePolyhedronFromPlanes_LineLL *pLineLink = &pStartLineLinks[iCurrentLineLinkIndex++]; - pLineLink->pLine = &pStartLines[pOffsetPolyhedronLines[j].iLineIndex]; - pLineLink->iReferenceIndex = pOffsetPolyhedronLines[j].iEndPointIndex; - - pLineLink->pLine->pPolygons[pLineLink->iReferenceIndex] = &pStartPolygons[i]; - pLineLink->pLine->pPolygonLineLinks[pLineLink->iReferenceIndex] = pLineLink; + GeneratePolyhedronFromPlanes_LineLL *pLineLink = &pLines[pOffsetPolyhedronLines[j].iLineIndex].line.PolygonLineLinks[pOffsetPolyhedronLines[j].iEndPointIndex]; + pLineLink->pLine->pPolygons[pLineLink->iReferenceIndex] = &pPolygons[i].polygon; - pLineLink->pPrev = pStartPolygons[i].pLines; - pStartPolygons[i].pLines->pNext = pLineLink; - pStartPolygons[i].pLines = pLineLink; + pLineLink->pPrev = pPolygons[i].polygon.pLines; + pPolygons[i].polygon.pLines->pNext = pLineLink; + pPolygons[i].polygon.pLines = pLineLink; } - pFirstLink->pPrev = pStartPolygons[i].pLines; - pStartPolygons[i].pLines->pNext = pFirstLink; + pFirstLink->pPrev = pPolygons[i].polygon.pLines; + pPolygons[i].polygon.pLines->pNext = pFirstLink; } - Assert( iCurrentLineLinkIndex == (pExistingPolyhedron->iLineCount * 4) ); - //go back to point line links so we can circularly link them as well as order them now that every point has all its line links for( int i = 0; i != pExistingPolyhedron->iVertexCount; ++i ) { //interlink the points { - GeneratePolyhedronFromPlanes_LineLL *pLastVisitedLink = pStartPoints[i].pConnectedLines; + GeneratePolyhedronFromPlanes_LineLL *pLastVisitedLink = pPoints[i].point.pConnectedLines; GeneratePolyhedronFromPlanes_LineLL *pCurrentLink = pLastVisitedLink; do @@ -409,13 +553,13 @@ CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float } while( pCurrentLink ); //circular link - pLastVisitedLink->pNext = pStartPoints[i].pConnectedLines; - pStartPoints[i].pConnectedLines->pPrev = pLastVisitedLink; + pLastVisitedLink->pNext = pPoints[i].point.pConnectedLines; + pPoints[i].point.pConnectedLines->pPrev = pLastVisitedLink; } //fix ordering - GeneratePolyhedronFromPlanes_LineLL *pFirstLink = pStartPoints[i].pConnectedLines; + GeneratePolyhedronFromPlanes_LineLL *pFirstLink = pPoints[i].point.pConnectedLines; GeneratePolyhedronFromPlanes_LineLL *pWorkLink = pFirstLink; GeneratePolyhedronFromPlanes_LineLL *pSearchLink; GeneratePolyhedronFromPlanes_Polygon *pLookingForPolygon; @@ -445,62 +589,64 @@ CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float } while( pWorkLink != pFirstLink ); } - GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints = (GeneratePolyhedronFromPlanes_UnorderedPointLL *)stackalloc( pExistingPolyhedron->iVertexCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedPointLL ) ); - GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines = (GeneratePolyhedronFromPlanes_UnorderedLineLL *)stackalloc( pExistingPolyhedron->iLineCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedLineLL ) ); - GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons = (GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)stackalloc( pExistingPolyhedron->iPolygonCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedPolygonLL ) ); - //setup point collection { pPoints[0].pPrev = NULL; - pPoints[0].pPoint = &pStartPoints[0]; pPoints[0].pNext = &pPoints[1]; int iLastPoint = pExistingPolyhedron->iVertexCount - 1; for( int i = 1; i != iLastPoint; ++i ) { pPoints[i].pPrev = &pPoints[i - 1]; - pPoints[i].pPoint = &pStartPoints[i]; pPoints[i].pNext = &pPoints[i + 1]; } pPoints[iLastPoint].pPrev = &pPoints[iLastPoint - 1]; - pPoints[iLastPoint].pPoint = &pStartPoints[iLastPoint]; pPoints[iLastPoint].pNext = NULL; } //setup line collection { pLines[0].pPrev = NULL; - pLines[0].pLine = &pStartLines[0]; pLines[0].pNext = &pLines[1]; int iLastLine = pExistingPolyhedron->iLineCount - 1; for( int i = 1; i != iLastLine; ++i ) { pLines[i].pPrev = &pLines[i - 1]; - pLines[i].pLine = &pStartLines[i]; pLines[i].pNext = &pLines[i + 1]; } pLines[iLastLine].pPrev = &pLines[iLastLine - 1]; - pLines[iLastLine].pLine = &pStartLines[iLastLine]; pLines[iLastLine].pNext = NULL; } //setup polygon collection { pPolygons[0].pPrev = NULL; - pPolygons[0].pPolygon = &pStartPolygons[0]; pPolygons[0].pNext = &pPolygons[1]; int iLastPolygon = pExistingPolyhedron->iPolygonCount - 1; for( int i = 1; i != iLastPolygon; ++i ) { pPolygons[i].pPrev = &pPolygons[i - 1]; - pPolygons[i].pPolygon = &pStartPolygons[i]; pPolygons[i].pNext = &pPolygons[i + 1]; } pPolygons[iLastPolygon].pPrev = &pPolygons[iLastPolygon - 1]; - pPolygons[iLastPolygon].pPolygon = &pStartPolygons[iLastPolygon]; pPolygons[iLastPolygon].pNext = NULL; } - return ClipLinkedGeometry( pPolygons, pLines, pPoints, pUsefulPlanes, iUsefulPlaneCount, fOnPlaneEpsilon, bUseTemporaryMemory ); + CPolyhedron *pRetVal = ClipLinkedGeometry( pPolygons, pLines, pPoints, pExistingPolyhedron->iVertexCount, pUsefulPlanes, iUsefulPlaneCount, fOnPlaneEpsilon, bUseTemporaryMemory, vResultOffset ); +#if defined( USE_WORLD_CENTERED_POSITIONS ) && defined( DEBUG_POLYHEDRON_CONVERSION ) + //last bit of debugging from whatever outside source wants this stupid thing + if( pRetVal && (g_pPolyhedronCarvingDebugStepCallback != NULL) ) + { + VMatrix matScaleCentered; + matScaleCentered.Identity(); + matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f; + matScaleCentered.SetTranslation( -pRetVal->Center() * 10.0f ); + + DumpPolyhedronToGLView( pRetVal, "AssertPolyhedron.txt", &matScaleCentered ); + AssertMsg( g_pPolyhedronCarvingDebugStepCallback( pRetVal ), "Outside conversion failed. Offset failure" ); //this REALLY sucks. Because the difference between success and failure was a translation of all points by the same vector. LAME + } +#endif //#if defined( USE_WORLD_CENTERED_POSITIONS ) + + return pRetVal; } @@ -522,7 +668,7 @@ Vector FindPointInPlanes( const float *pPlanes, int planeCount ) -bool FindConvexShapeLooseAABB( const float *pInwardFacingPlanes, int iPlaneCount, Vector *pAABBMins, Vector *pAABBMaxs ) //bounding box of the convex shape (subject to floating point error) +bool FindConvexShapeLooseAABB( const fltx4 *pInwardFacingPlanes, int iPlaneCount, Vector *pAABBMins, Vector *pAABBMaxs ) //bounding box of the convex shape (subject to floating point error) { //returns false if the AABB hasn't been set if( pAABBMins == NULL && pAABBMaxs == NULL ) //no use in actually finding out what it is @@ -534,18 +680,14 @@ bool FindConvexShapeLooseAABB( const float *pInwardFacingPlanes, int iPlaneCount int iVertCount; }; - float *pMovedPlanes = (float *)stackalloc( iPlaneCount * 4 * sizeof( float ) ); - //Vector vPointInPlanes = FindPointInPlanes( pInwardFacingPlanes, iPlaneCount ); - + const size_t kPlaneAlign = sizeof( Vector4D ) - 1; + uint8 *pAlignedAlloc = (uint8 *)stackalloc( (sizeof( Vector4D ) * iPlaneCount) + kPlaneAlign ); + pAlignedAlloc = (uint8 *)(((size_t)(pAlignedAlloc + kPlaneAlign)) & ~kPlaneAlign); + Vector4D *pMovedPlanes = (Vector4D *)pAlignedAlloc; for( int i = 0; i != iPlaneCount; ++i ) { - pMovedPlanes[(i * 4) + 0] = pInwardFacingPlanes[(i * 4) + 0]; - pMovedPlanes[(i * 4) + 1] = pInwardFacingPlanes[(i * 4) + 1]; - pMovedPlanes[(i * 4) + 2] = pInwardFacingPlanes[(i * 4) + 2]; - pMovedPlanes[(i * 4) + 3] = pInwardFacingPlanes[(i * 4) + 3] - 100.0f; //move planes out a lot to kill some imprecision problems + pMovedPlanes[i].Init( SubFloat( pInwardFacingPlanes[i], 0 ), SubFloat( pInwardFacingPlanes[i], 1 ), SubFloat( pInwardFacingPlanes[i], 2 ), SubFloat( pInwardFacingPlanes[i], 3 ) - 100.0f ); //move planes out a lot to kill some imprecision problems } - - //vAABBMins = vAABBMaxs = FindPointInPlanes( pPlanes, iPlaneCount ); float *vertsIn = NULL; //we'll be allocating a new buffer for this with each new polygon, and moving it off to the polygon array @@ -557,14 +699,14 @@ bool FindConvexShapeLooseAABB( const float *pInwardFacingPlanes, int iPlaneCount for ( int i = 0; i < iPlaneCount; i++ ) { - Vector *pPlaneNormal = (Vector *)&pInwardFacingPlanes[i*4]; - float fPlaneDist = pInwardFacingPlanes[(i*4) + 3]; + Vector vPlaneNormal = pMovedPlanes[i].AsVector3D(); + float fPlaneDist = pMovedPlanes[i].w;// + 50.0f; if( vertsIn == NULL ) vertsIn = (float *)stackalloc( (iPlaneCount + 4) * (sizeof( float ) * 3) ); // Build a big-ass poly in this plane - int vertCount = PolyFromPlane( (Vector *)vertsIn, *pPlaneNormal, fPlaneDist, 100000.0f ); + int vertCount = PolyFromPlane( (Vector *)vertsIn, vPlaneNormal, fPlaneDist, 100000.0f ); //chop it by every other plane for( int j = 0; j < iPlaneCount; j++ ) @@ -574,7 +716,7 @@ bool FindConvexShapeLooseAABB( const float *pInwardFacingPlanes, int iPlaneCount continue; // Chop the polygon against this plane - vertCount = ClipPolyToPlane( (Vector *)vertsIn, vertCount, (Vector *)vertsOut, *(Vector *)&pMovedPlanes[j*4], pMovedPlanes[(j*4) + 3], 0.0f ); + vertCount = ClipPolyToPlane( (Vector *)vertsIn, vertCount, (Vector *)vertsOut, pMovedPlanes[j].AsVector3D(), pMovedPlanes[j].w, 0.0f ); //swap the input and output arrays vertsSwap = vertsIn; vertsIn = vertsOut; vertsOut = vertsSwap; @@ -669,7 +811,7 @@ bool FindConvexShapeLooseAABB( const float *pInwardFacingPlanes, int iPlaneCount -CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, bool bUseTemporaryMemory ) +CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, bool bUseTemporaryMemory, fltx4 vShiftResultPositions ) { Assert( (pPolygons != NULL) && (pLines != NULL) && (pPoints != NULL) ); unsigned int iPolyCount = 0, iLineCount = 0, iPointCount = 0, iIndexCount = 0; @@ -678,7 +820,7 @@ CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_Uno do { ++iPolyCount; - GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pActivePolygonWalk->pPolygon->pLines; + GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pActivePolygonWalk->polygon.pLines; GeneratePolyhedronFromPlanes_LineLL *pFirstLine = pLineWalk; Assert( pLineWalk != NULL ); @@ -724,8 +866,13 @@ CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_Uno pActivePointWalk = pPoints; for( unsigned int i = 0; i != iPointCount; ++i ) { - pVertexArray[i] = pActivePointWalk->pPoint->ptPosition; - pActivePointWalk->pPoint->iSaveIndices = i; //storing array indices +#if defined( USE_WORLD_CENTERED_POSITIONS ) + fltx4 vShiftedResult = AddSIMD( pActivePointWalk->point.ptPosition, vShiftResultPositions ); + pVertexArray[i].Init( SubFloat( vShiftedResult, 0 ), SubFloat( vShiftedResult, 1 ), SubFloat( vShiftedResult, 2 ) ); +#else + pVertexArray[i].Init( SubFloat( pActivePointWalk->point.ptPosition, 0 ), SubFloat( pActivePointWalk->point.ptPosition, 1 ), SubFloat( pActivePointWalk->point.ptPosition, 2 ) ); +#endif + pActivePointWalk->point.iSaveIndices = i; //storing array indices pActivePointWalk = pActivePointWalk->pNext; } @@ -733,10 +880,10 @@ CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_Uno pActiveLineWalk = pLines; for( unsigned int i = 0; i != iLineCount; ++i ) { - pLineArray[i].iPointIndices[0] = (unsigned short)pActiveLineWalk->pLine->pPoints[0]->iSaveIndices; - pLineArray[i].iPointIndices[1] = (unsigned short)pActiveLineWalk->pLine->pPoints[1]->iSaveIndices; + pLineArray[i].iPointIndices[0] = (unsigned short)pActiveLineWalk->line.pPoints[0]->iSaveIndices; + pLineArray[i].iPointIndices[1] = (unsigned short)pActiveLineWalk->line.pPoints[1]->iSaveIndices; - pActiveLineWalk->pLine->iSaveIndices = i; //storing array indices + pActiveLineWalk->line.iSaveIndices = i; //storing array indices pActiveLineWalk = pActiveLineWalk->pNext; } @@ -746,14 +893,13 @@ CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_Uno iIndexCount = 0; for( unsigned int i = 0; i != iPolyCount; ++i ) { - pPolyArray[i].polyNormal = pActivePolygonWalk->pPolygon->vSurfaceNormal; + pPolyArray[i].polyNormal = pActivePolygonWalk->polygon.vSurfaceNormal; pPolyArray[i].iFirstIndex = iIndexCount; - GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pActivePolygonWalk->pPolygon->pLines; + GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pActivePolygonWalk->polygon.pLines; GeneratePolyhedronFromPlanes_LineLL *pFirstLine = pLineWalk; do { - //pIndexArray[iIndexCount] = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->iWorkData; //startpoint of each line, iWorkData is the index of the vertex pIndexArray[iIndexCount].iLineIndex = pLineWalk->pLine->iSaveIndices; pIndexArray[iIndexCount].iEndPointIndex = pLineWalk->iReferenceIndex; @@ -766,7 +912,7 @@ CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_Uno pActivePolygonWalk = pActivePolygonWalk->pNext; } -#if defined( _DEBUG ) && defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) && defined( DEBUG_DUMP_POLYHEDRONS_TO_NUMBERED_GLVIEWS ) +#if defined( DBGFLAG_ASSERT ) && defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) && defined( DEBUG_DUMP_POLYHEDRONS_TO_NUMBERED_GLVIEWS ) char szCollisionFile[128]; CreateDumpDirectory( "PolyhedronDumps" ); Q_snprintf( szCollisionFile, 128, "PolyhedronDumps/NewStyle_PolyhedronDump%i.txt", g_iPolyhedronDumpCounter ); @@ -777,12 +923,20 @@ CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_Uno DumpPolyhedronToGLView( pReturn, "PolyhedronDumps/NewStyle_PolyhedronDump_All-Appended.txt", &s_matIdentity ); #endif +#if defined( DEBUG_POLYHEDRON_CONVERSION ) && 0 //probably too redundant to check here + //last bit of debugging from whatever outside source wants this stupid thing + if( (g_pPolyhedronCarvingDebugStepCallback != NULL) && (pReturn != NULL) ) + { + AssertMsg( g_pPolyhedronCarvingDebugStepCallback( pReturn ), "Outside conversion failed" ); + } +#endif + return pReturn; } -#ifdef _DEBUG +#ifdef DBGFLAG_ASSERT void DumpPointListToGLView( GeneratePolyhedronFromPlanes_UnorderedPointLL *pHead, PolyhedronPointPlanarity planarity, const Vector &vColor, const char *szDumpFile, const VMatrix *pTransform ) { @@ -794,10 +948,12 @@ void DumpPointListToGLView( GeneratePolyhedronFromPlanes_UnorderedPointLL *pHead while( pHead ) { - if( pHead->pPoint->planarity == planarity ) + if( pHead->point.planarity == planarity ) { const Vector vPointExtents( 0.5f, 0.5f, 0.01f ); - DumpAABBToGLView( (*pTransform) * pHead->pPoint->ptPosition, vPointExtents, vColor, pFile ); + fltx4 f4Pos = pHead->point.ptPosition; + Vector vPos( SubFloat( f4Pos, 0 ), SubFloat( f4Pos, 1 ), SubFloat( f4Pos, 2 ) ); + DumpAABBToGLView( (*pTransform) * vPos, vPointExtents, vColor, pFile ); } pHead = pHead->pNext; } @@ -819,8 +975,12 @@ const char * DumpPolyhedronCutHistory( const CUtlVector &DumpedHi if( DumpedHistory[i] != NULL ) { Q_snprintf( szDumpFile, 100, "FailedPolyhedronCut_%d.txt", i ); - DumpPolyhedronToGLView( DumpedHistory[i], szDumpFile, pTransform ); - DumpPlaneToGlView( CutHistory[i], 1.0f, szDumpFile, pTransform ); + + DumpPolyhedronToGLView( DumpedHistory[i], szDumpFile, pTransform, "wb" ); + if( CutHistory.Count() > i ) + { + DumpPlaneToGlView( CutHistory[i], 1.0f, szDumpFile, pTransform ); + } } } @@ -831,367 +991,1464 @@ const char * DumpPolyhedronCutHistory( const CUtlVector &DumpedHi } #ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS -#define AssertMsg_DumpPolyhedron(condition, message)\ +#define DUMP_POLYHEDRON_SCALE 10.0f +bool g_bDumpNullPolyhedrons = false; +#define AssertMsg_DumpPolyhedron_Destructors(condition, destructors, message)\ if( (condition) == false )\ {\ - VMatrix matTransform;\ - matTransform.Identity();\ - matTransform[0][0] = matTransform[1][1] = matTransform[2][2] = 25.0f;\ - matTransform.SetTranslation( -DebugCutHistory.Tail()->Center() * 25.0f );\ - const char *szLastDumpFile = DumpPolyhedronCutHistory( DebugCutHistory, PlaneCutHistory, &matTransform );\ - DumpPointListToGLView( pAllPoints, POINT_ALIVE, Vector( 0.9f, 0.9f, 0.9f ), szLastDumpFile, &matTransform );\ - DumpPointListToGLView( pAllPoints, POINT_ONPLANE, Vector( 0.5f, 0.5f, 0.5f ), szLastDumpFile, &matTransform );\ - DumpPointListToGLView( pDeadPointCollection, POINT_DEAD, Vector( 0.1f, 0.1f, 0.1f ), szLastDumpFile, &matTransform );\ - if( pStartPoint )\ + if( ((destructors).DebugCutHistory.Count() != 0) && ((destructors).PlaneCutHistory.Count() != 0) )\ {\ - FILE *pFileDumpRepairProgress = fopen( szLastDumpFile, "ab" );\ - DumpAABBToGLView( matTransform * pStartPoint->ptPosition, Vector( 2.0f, 0.05f, 0.05f ), Vector( 0.0f, 1.0f, 0.0f ), pFileDumpRepairProgress );\ - DumpAABBToGLView( matTransform * pWorkPoint->ptPosition, Vector( 2.0f, 0.05f, 0.05f ), Vector( 1.0f, 0.0f, 0.0f ), pFileDumpRepairProgress );\ - fclose( pFileDumpRepairProgress );\ + VMatrix matTransform;\ + matTransform.Identity();\ + matTransform[0][0] = matTransform[1][1] = matTransform[2][2] = DUMP_POLYHEDRON_SCALE;\ + matTransform.SetTranslation( -(destructors).DebugCutHistory.Tail()->Center() * DUMP_POLYHEDRON_SCALE );\ + const char *szLastDumpFile = DumpPolyhedronCutHistory( (destructors).DebugCutHistory, (destructors).PlaneCutHistory, &matTransform );\ + DumpPointListToGLView( (destructors).pAllPoints, POINT_ALIVE, Vector( 0.9f, 0.9f, 0.9f ), szLastDumpFile, &matTransform );\ + DumpPointListToGLView( (destructors).pAllPoints, POINT_ONPLANE, Vector( 0.5f, 0.5f, 0.5f ), szLastDumpFile, &matTransform );\ + DumpPointListToGLView( (destructors).pDeadPointCollection, POINT_DEAD, Vector( 0.1f, 0.1f, 0.1f ), szLastDumpFile, &matTransform );\ + DumpWorkingStatePolyhedron( (destructors).pAllPolygons, (destructors).pDeadPolygonCollection, (destructors).pAllLines, (destructors).pDeadLineCollection, (destructors).pAllPoints, (destructors).pDeadPointCollection, "FailedPolyhedronCut_LastCutDebug.txt", &matTransform );\ + DumpPlaneToGlView( (destructors).PlaneCutHistory.Tail(), 1.0f, "FailedPolyhedronCut_LastCutDebug.txt", &matTransform );\ }\ AssertMsg( condition, message );\ } #else -#define AssertMsg_DumpPolyhedron(condition, message) AssertMsg( condition, message ) +#define AssertMsg_DumpPolyhedron_Destructors(condition, destructors, message) AssertMsg( condition, message ) #endif -#define Assert_DumpPolyhedron(condition) AssertMsg_DumpPolyhedron( condition, #condition ) #else -#define AssertMsg_DumpPolyhedron(condition, message) NULL; -#define Assert_DumpPolyhedron(condition) NULL; +#define AssertMsg_DumpPolyhedron_Destructors(condition, destructors, message) NULL; #endif -CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, const float *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory ) -{ - const float fNegativeOnPlaneEpsilon = -fOnPlaneEpsilon; +#define Assert_DumpPolyhedron_Destructors(condition, destructors) AssertMsg_DumpPolyhedron_Destructors( condition, destructors, #condition ) -#ifdef _DEBUG +#define AssertMsg_DumpPolyhedron(condition, message) AssertMsg_DumpPolyhedron_Destructors(condition, destructors, message) +#define Assert_DumpPolyhedron(condition) Assert_DumpPolyhedron_Destructors( condition, destructors ) + + +//a little class that acts like a small block heap, using stack memory given to it +class CStackMemoryDispenser +{ +public: + CStackMemoryDispenser( void *pStackAllocation, size_t iStackAllocationSize ) + { + m_pDispenserBuffer = (unsigned char *)pStackAllocation; + m_iDispenserSizeLeft = iStackAllocationSize; + m_pDeleteList = NULL; + } + + ~CStackMemoryDispenser( void ) + { + RecurseDelete( m_pDeleteList ); + } + + void *Allocate( size_t iSize, size_t iAlignTo = 16 ) + { + AssertMsg( ((iAlignTo - 1) & iAlignTo) == 0, "Alignment must be a power of 2" ); + size_t iAlignOffset = iAlignTo - ((size_t)m_pDispenserBuffer) & (iAlignTo - 1); + m_iDispenserSizeLeft -= iAlignOffset; + m_pDispenserBuffer += iAlignOffset; + + if( iSize > m_iDispenserSizeLeft ) + { + //allocate a new buffer + size_t iNewBufferSize = MAX( 128 * 1024, (iSize + iAlignTo) * 2 ); //either allocate 128k or enough to hold 2x the allocation. + unsigned char *pNewBuffer = new unsigned char [iNewBufferSize]; //allocate 128k at a time + *(void **)pNewBuffer = NULL; + + //insert this allocation into the linked list of allocations to delete on destruct + void **pWriteDeleteAddress = &m_pDeleteList; + while( *pWriteDeleteAddress != NULL ) + { + pWriteDeleteAddress = (void **)*pWriteDeleteAddress; + } + *pWriteDeleteAddress = pNewBuffer; + + //save this as the new dispenser buffer, skipping the linked list pointer + m_pDispenserBuffer = pNewBuffer + sizeof( void * ); + m_iDispenserSizeLeft = iNewBufferSize - sizeof( void * ); + + iAlignOffset = iAlignTo - ((size_t)m_pDispenserBuffer) & (iAlignTo - 1); //recompute alignment offset + m_iDispenserSizeLeft -= iAlignOffset; + m_pDispenserBuffer += iAlignOffset; + } + + void *pRetVal = m_pDispenserBuffer; + m_pDispenserBuffer += iSize; + m_iDispenserSizeLeft -= iSize; + Assert( (((size_t)pRetVal) & (iAlignTo - 1)) == 0 ); + return pRetVal; + } + +private: + static void RecurseDelete( void *pDelete ) + { + if( pDelete != NULL ) + { + RecurseDelete( *(void **)pDelete ); + delete [](void**)pDelete; + } + } + + unsigned char *m_pDispenserBuffer; + size_t m_iDispenserSizeLeft; + void *m_pDeleteList; //a linked list of pointers to actual memory allocations we had to make that need to be deleted. The first thing in each allocation is a reserved space for another pointer +}; + +template +class CStackItemDispenser +{ +public: + CStackItemDispenser( CStackMemoryDispenser &MemoryDispenser ) : m_FallbackDispenser( MemoryDispenser ) + { + COMPILE_TIME_ASSERT( sizeof( T ) > sizeof( void * ) ); + m_pHead = NULL; + } + + T *Allocate( void ) + { + if( m_pHead != NULL ) + { + T *pRetVal = m_pHead; + m_pHead = *(T **)m_pHead; +#ifdef DBGFLAG_ASSERT + memset( pRetVal, 0xCCCCCCCC, sizeof( T ) ); +#endif + return pRetVal; + } + else + { + return (T *)m_FallbackDispenser.Allocate( sizeof( T ) ); + } + } + + void Free( T *pFree ) + { + *(T **)pFree = m_pHead; + m_pHead = pFree; + } + +private: + CStackMemoryDispenser &m_FallbackDispenser; + T *m_pHead; +}; + + + +inline void ComputePlanarDistances( GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, int iPointCount, fltx4 fPlane ) +{ + uint8 *pAlignedAlloc = (uint8 *)stackalloc( (iPointCount) * sizeof(fltx4) + 15 ); + pAlignedAlloc = (uint8 *)(((size_t)(pAlignedAlloc + 15)) & ~15); + fltx4 *pIntermediateResults = (fltx4 *)pAlignedAlloc; + + int i = 0; + GeneratePolyhedronFromPlanes_UnorderedPointLL *pPointWalk = pAllPoints; + do + { + Assert( SubFloat( pPointWalk->point.ptPosition, 3 ) == -1.0f ); + pIntermediateResults[i] = MulSIMD( fPlane, pPointWalk->point.ptPosition ); + ++i; + pPointWalk = pPointWalk->pNext; + } while( pPointWalk != NULL ); + + i = 0; + pPointWalk = pAllPoints; + do + { + pPointWalk->point.fPlaneDist = SubFloat( pIntermediateResults[i], 0 ) + SubFloat( pIntermediateResults[i], 1 ) + SubFloat( pIntermediateResults[i], 2 ) + SubFloat( pIntermediateResults[i], 3 ); + DBG_ONLY( pPointWalk->point.debugdata.fInitialPlaneDistance = pPointWalk->point.fPlaneDist; ); + ++i; + pPointWalk = pPointWalk->pNext; + } while( pPointWalk != NULL ); +} + +class CClipLinkedGeometryDestructors +{ +public: + int &iPointCount; + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pAllPolygons; + GeneratePolyhedronFromPlanes_UnorderedLineLL *&pAllLines; + GeneratePolyhedronFromPlanes_UnorderedPointLL *&pAllPoints; + GeneratePolyhedronFromPlanes_UnorderedPointLL *&pDeadPointCollection; + +#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pDeadPolygonCollection; + GeneratePolyhedronFromPlanes_UnorderedLineLL *&pDeadLineCollection; + + CClipLinkedGeometryDestructors( int &iPointCount_IN, + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pAllPolygons_IN, + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pDeadPolygonCollection_IN, + GeneratePolyhedronFromPlanes_UnorderedLineLL *&pAllLines_IN, + GeneratePolyhedronFromPlanes_UnorderedLineLL *&pDeadLineCollection_IN, + GeneratePolyhedronFromPlanes_UnorderedPointLL *&pAllPoints_IN, + GeneratePolyhedronFromPlanes_UnorderedPointLL *&pDeadPointCollection_IN ) + : iPointCount( iPointCount_IN ), pAllPolygons( pAllPolygons_IN ), pAllLines( pAllLines_IN ), pDeadPolygonCollection( pDeadPolygonCollection_IN ), pDeadLineCollection( pDeadLineCollection_IN ), pAllPoints( pAllPoints_IN ), pDeadPointCollection( pDeadPointCollection_IN ) {}; + +#else + CStackItemDispenser &polygonAllocator; + CStackItemDispenser &lineAllocator; + + CClipLinkedGeometryDestructors( int &iPointCount_IN, + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pAllPolygons_IN, + CStackItemDispenser &polygonAllocator_IN, + GeneratePolyhedronFromPlanes_UnorderedLineLL *&pAllLines_IN, + CStackItemDispenser &lineAllocator_IN, + GeneratePolyhedronFromPlanes_UnorderedPointLL *&pAllPoints_IN, + GeneratePolyhedronFromPlanes_UnorderedPointLL *&pDeadPointCollection_IN ) + : iPointCount( iPointCount_IN ), pAllPolygons( pAllPolygons_IN ), pAllLines( pAllLines_IN ), polygonAllocator( polygonAllocator_IN ), lineAllocator( lineAllocator_IN ), pAllPoints( pAllPoints_IN ), pDeadPointCollection( pDeadPointCollection_IN ) {}; +#endif + +#if defined( DBGFLAG_ASSERT ) + //let some generic debug data hitch a ride on this structure since it goes pretty much everywhere CUtlVector DebugCutHistory; CUtlVector PlaneCutHistory; - GeneratePolyhedronFromPlanes_Point *pStartPoint = NULL; - GeneratePolyhedronFromPlanes_Point *pWorkPoint = NULL; + CUtlVector DebugCutPlaneIndex; + bool bDebugTrigger; - static int iPolyhedronClipCount = 0; - ++iPolyhedronClipCount; - - DebugCutHistory.AddToTail( ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false ) ); + ~CClipLinkedGeometryDestructors( void ) + { + for( int i = 0; i != DebugCutHistory.Count(); ++i ) + { + if( DebugCutHistory[i] != NULL ) + { + DebugCutHistory[i]->Release(); + } + } + } +#endif +}; + +static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedPointLL *DestructPoint( GeneratePolyhedronFromPlanes_UnorderedPointLL *pKillPoint, CClipLinkedGeometryDestructors &destructors ) +{ +#if defined( DBGFLAG_ASSERT ) + { + GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPointWalk = destructors.pDeadPointCollection; + while( pDeadPointWalk ) + { + Assert( pDeadPointWalk != pKillPoint ); + pDeadPointWalk = pDeadPointWalk->pNext; + } + } #endif - //clear out polygon work variables + + GeneratePolyhedronFromPlanes_UnorderedPointLL *pRetVal = pKillPoint->pNext; + DBG_ONLY( pKillPoint->point.planarity = POINT_DEAD; ); + if( pKillPoint->pNext ) { - GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons; - do + pKillPoint->pNext->pPrev = pKillPoint->pPrev; + } + + if( pKillPoint == destructors.pAllPoints ) + { + destructors.pAllPoints = pKillPoint->pNext; + } + else + { + pKillPoint->pPrev->pNext = pKillPoint->pNext; + } + + pKillPoint->pNext = destructors.pDeadPointCollection; + destructors.pDeadPointCollection = pKillPoint; + + --destructors.iPointCount; + + return pRetVal; +} +static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedPointLL *DestructPoint( GeneratePolyhedronFromPlanes_Point *pKillPoint, CClipLinkedGeometryDestructors &destructors ) +{ +#ifdef OSX + Assert( &(((GeneratePolyhedronFromPlanes_UnorderedPointLL *)pKillPoint)->point) == pKillPoint ); +#else + // This COMPILE_TIME_ASSERT was breaking gcc under OSX + COMPILE_TIME_ASSERT( offsetof(GeneratePolyhedronFromPlanes_UnorderedPointLL, point) == 0 ); +#endif + return DestructPoint( (GeneratePolyhedronFromPlanes_UnorderedPointLL *)pKillPoint, destructors ); +} + +static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedLineLL *DestructLine( GeneratePolyhedronFromPlanes_UnorderedLineLL *pKillLine, CClipLinkedGeometryDestructors &destructors ) +{ + GeneratePolyhedronFromPlanes_UnorderedLineLL *pRetVal = pKillLine->pNext; + DBG_ONLY( pKillLine->line.planarity = LINE_DEAD; ); + + if( pKillLine->pNext ) + { + pKillLine->pNext->pPrev = pKillLine->pPrev; + } + + if( pKillLine == destructors.pAllLines ) + { + destructors.pAllLines = pKillLine->pNext; + } + else + { + pKillLine->pPrev->pNext = pKillLine->pNext; + } + +#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS + pKillLine->pNext = destructors.pDeadLineCollection; + destructors.pDeadLineCollection = pKillLine; +#else + destructors.lineAllocator.Free( pKillLine ); +#endif + + return pRetVal; +} +static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedLineLL *DestructLine( GeneratePolyhedronFromPlanes_Line *pKillLine, CClipLinkedGeometryDestructors &destructors ) +{ +#ifdef OSX + Assert( &(((GeneratePolyhedronFromPlanes_UnorderedLineLL *)pKillLine)->line) == pKillLine ); +#else + // This COMPILE_TIME_ASSERT was breaking gcc under OSX + COMPILE_TIME_ASSERT( offsetof(GeneratePolyhedronFromPlanes_UnorderedLineLL, line) == 0 ); +#endif + return DestructLine( (GeneratePolyhedronFromPlanes_UnorderedLineLL *)pKillLine, destructors ); +} + +static FORCEINLINE void UnlinkLine( GeneratePolyhedronFromPlanes_Line *pUnlinkLine ) +{ + //disconnect the line from everything + for( int i = 0; i != 2; ++i ) + { + pUnlinkLine->PointLineLinks[i].pNext->pPrev = pUnlinkLine->PointLineLinks[i].pPrev; + pUnlinkLine->PointLineLinks[i].pPrev->pNext = pUnlinkLine->PointLineLinks[i].pNext; + + pUnlinkLine->PolygonLineLinks[i].pNext->pPrev = pUnlinkLine->PolygonLineLinks[i].pPrev; + pUnlinkLine->PolygonLineLinks[i].pPrev->pNext = pUnlinkLine->PolygonLineLinks[i].pNext; + + pUnlinkLine->pPoints[i]->pConnectedLines = pUnlinkLine->PointLineLinks[i].pNext; + pUnlinkLine->pPolygons[i]->pLines = pUnlinkLine->PolygonLineLinks[i].pNext; + + Assert( (pUnlinkLine->pPoints[i]->pConnectedLines != &pUnlinkLine->PointLineLinks[i]) || (pUnlinkLine->pPoints[i]->planarity == POINT_DEAD) ); + Assert( (pUnlinkLine->pPolygons[i]->pLines != &pUnlinkLine->PolygonLineLinks[i]) || (pUnlinkLine->pPolygons[i]->bDead == true) ); + } +} + +static FORCEINLINE void UnlinkLine( GeneratePolyhedronFromPlanes_UnorderedLineLL *pUnlinkLine ) +{ + UnlinkLine( &pUnlinkLine->line ); +} + + + +static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedPolygonLL *DestructPolygon( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pKillPolygon, CClipLinkedGeometryDestructors &destructors ) +{ + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pRetVal = pKillPolygon->pNext; + if( pKillPolygon->pNext ) + { + pKillPolygon->pNext->pPrev = pKillPolygon->pPrev; + } + + if( pKillPolygon == destructors.pAllPolygons ) + { + destructors.pAllPolygons = pKillPolygon->pNext; + } + else + { + pKillPolygon->pPrev->pNext = pKillPolygon->pNext; + } + +#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS + pKillPolygon->pNext = destructors.pDeadPolygonCollection; + destructors.pDeadPolygonCollection = pKillPolygon; +#else + destructors.polygonAllocator.Free( pKillPolygon ); +#endif + + return pRetVal; +} + +static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedPolygonLL *DestructPolygon( GeneratePolyhedronFromPlanes_Polygon *pKillPolygon, CClipLinkedGeometryDestructors &destructors ) +{ +#ifdef OSX + Assert( &(((GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)pKillPolygon)->polygon) == pKillPolygon ); +#else + // This COMPILE_TIME_ASSERT was breaking gcc under OSX + COMPILE_TIME_ASSERT( offsetof(GeneratePolyhedronFromPlanes_UnorderedPolygonLL, polygon) == 0 ); +#endif + return DestructPolygon( (GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)pKillPolygon, destructors ); +} + +//remove a known degenerate polygon. Returns INVALID pointer to the line we destroy in here. +GeneratePolyhedronFromPlanes_Line *RemoveDegeneratePolygon( GeneratePolyhedronFromPlanes_Polygon *pDegeneratePolygon, CClipLinkedGeometryDestructors &destructors ) +{ + Assert_DumpPolyhedron( pDegeneratePolygon->pLines->pNext == pDegeneratePolygon->pLines->pPrev ); + Assert_DumpPolyhedron( pDegeneratePolygon->pLines->pNext != pDegeneratePolygon->pLines ); //just for the sake of paranoia. Check that it's not a single-lined polygon + + Assert_DumpPolyhedron( (pDegeneratePolygon->pLines->pLine->pPoints[pDegeneratePolygon->pLines->iReferenceIndex] == pDegeneratePolygon->pLines->pNext->pLine->pPoints[1 - pDegeneratePolygon->pLines->pNext->iReferenceIndex]) && + (pDegeneratePolygon->pLines->pLine->pPoints[1 - pDegeneratePolygon->pLines->iReferenceIndex] == pDegeneratePolygon->pLines->pNext->pLine->pPoints[pDegeneratePolygon->pLines->pNext->iReferenceIndex]) ); + + //both the lines are the same, just ditch one and link up the real polygons + GeneratePolyhedronFromPlanes_LineLL *pKeepLine = pDegeneratePolygon->pLines; + GeneratePolyhedronFromPlanes_LineLL *pDeadLine = pKeepLine->pNext; + GeneratePolyhedronFromPlanes_Line *pRetVal = pDeadLine->pLine; + +#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + pDegeneratePolygon->bDead = true; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it. + pDeadLine->pLine->planarity = LINE_DEAD; +#endif + + pKeepLine->pLine->pPolygons[pKeepLine->iReferenceIndex] = pDeadLine->pLine->pPolygons[1 - pDeadLine->iReferenceIndex]; + + //unlink pDeadLine from the dead polygon so it doesn't mess with pKeepLine when we unlink it + pDeadLine->pLine->PolygonLineLinks[pDeadLine->iReferenceIndex].pNext = &pDeadLine->pLine->PolygonLineLinks[pDeadLine->iReferenceIndex]; + pDeadLine->pLine->PolygonLineLinks[pDeadLine->iReferenceIndex].pPrev = &pDeadLine->pLine->PolygonLineLinks[pDeadLine->iReferenceIndex]; + + //insert pKeepLine just after pDeadLine on pDeadLine's flip side polygon, so when we unlink pDeadLine, everything links into place smoothly + pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex].pPrev = &pDeadLine->pLine->PolygonLineLinks[1 - pDeadLine->iReferenceIndex]; + pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex].pNext = pDeadLine->pLine->PolygonLineLinks[1 - pDeadLine->iReferenceIndex].pNext; + pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex].pPrev->pNext = &pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex]; + pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex].pNext->pPrev = &pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex]; + + UnlinkLine( pDeadLine->pLine ); + DestructLine( pDeadLine->pLine, destructors ); + DestructPolygon( pDegeneratePolygon, destructors ); + return pRetVal; +} + + +//search/kill redundant points on the specified polygon +void RemoveDegeneratePoints( GeneratePolyhedronFromPlanes_Polygon *pSearchPolygon, CClipLinkedGeometryDestructors &destructors ) +{ + AssertMsg( destructors.pAllPolygons->pNext != NULL && destructors.pAllPolygons->pNext->pNext != NULL, "RemoveDegeneratePoints() is not safe to run on 2D polyhedrons, early out before you get here" ); + GeneratePolyhedronFromPlanes_LineLL *pHeadLine = pSearchPolygon->pLines; + GeneratePolyhedronFromPlanes_LineLL *pWalkLine = pHeadLine; + do + { + while( true ) //inner loop to support retesting the same line over and over again( even if it's the head ) { - pActivePolygonWalk->pPolygon->bMissingASide = false; - pActivePolygonWalk = pActivePolygonWalk->pNext; - } while( pActivePolygonWalk ); + GeneratePolyhedronFromPlanes_LineLL *pPointLineLink = &pWalkLine->pLine->PointLineLinks[1 - pWalkLine->iReferenceIndex]; + + Assert_DumpPolyhedron( (pPointLineLink->pLine->pPolygons[0]->pLines != pPointLineLink->pLine->pPolygons[0]->pLines->pNext) && + (pPointLineLink->pLine->pPolygons[0]->pLines != pPointLineLink->pLine->pPolygons[0]->pLines->pNext->pNext) && + (pPointLineLink->pLine->pPolygons[1]->pLines != pPointLineLink->pLine->pPolygons[1]->pLines->pNext) && + (pPointLineLink->pLine->pPolygons[1]->pLines != pPointLineLink->pLine->pPolygons[1]->pLines->pNext->pNext) ); + + //try iterating forward 2 lines, jumping over already-dead lines. + //if we end up where we started, the point is redundant + + //go forward 1 + GeneratePolyhedronFromPlanes_LineLL *pCircleBackLineLink = pPointLineLink->pNext; + + //and again + pCircleBackLineLink = pCircleBackLineLink->pNext; + + //point is connected to only 2 lines. This can only be part of a convex if that convex is entirely 2D or if the lines perfectly agree with eachother. + //Based on the assumption that the convex is 3D. We can force the lines to perfectly agree with each other by eliminating one and patching the other to do the work of both. + if( pCircleBackLineLink == pPointLineLink ) + { + //connect the root of the next line to our root, this way multiple occurrences in a row can chain to the same root + GeneratePolyhedronFromPlanes_Point *pDeadPoint = pWalkLine->pLine->pPoints[1 - pWalkLine->iReferenceIndex]; + GeneratePolyhedronFromPlanes_LineLL *pRootLine = &pWalkLine->pPrev->pLine->PointLineLinks[1 - pWalkLine->pPrev->iReferenceIndex]; + GeneratePolyhedronFromPlanes_LineLL *pSurvivingLine = pWalkLine; + + Assert_DumpPolyhedron( pDeadPoint->planarity != POINT_DEAD ); + Assert_DumpPolyhedron( pRootLine->pLine->planarity != LINE_DEAD ); + + if( pWalkLine->pPrev == pHeadLine ) + { + pHeadLine = pWalkLine; + } + + pSurvivingLine = &pSurvivingLine->pLine->PointLineLinks[1 - pSurvivingLine->iReferenceIndex]; //convert it to point space +#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + pDeadPoint->planarity = POINT_DEAD; + pRootLine->pLine->planarity = LINE_DEAD; +#endif + + //the dead line/point removal code will unlink these properly. But will leave surviving line's endpoint pointing at the dead point. + //relink surviving to the root point + { + //unlink dead point from surviving line + pSurvivingLine->pNext->pPrev = pSurvivingLine->pPrev; + pSurvivingLine->pPrev->pNext = pSurvivingLine->pNext; + + //arbitrarily insert after dead line on the root point, before or after doesn't matter as it'll be unlinked soon anyway + pSurvivingLine->pNext = pRootLine->pNext; + pSurvivingLine->pNext->pPrev = pSurvivingLine; + pSurvivingLine->pPrev = pRootLine; + pRootLine->pNext = pSurvivingLine; + + //steal root from dead line + pSurvivingLine->pLine->pPoints[1 - pSurvivingLine->iReferenceIndex] = pRootLine->pLine->pPoints[1 - pRootLine->iReferenceIndex]; + } + + //pRootLine is fully connected to the root point, dead point, and both polygons. Unlink should work properly + UnlinkLine( pRootLine->pLine ); + DestructLine( pRootLine->pLine, destructors ); + DestructPoint( pDeadPoint, destructors ); + } + else + { + break; + } + } + + pWalkLine = pWalkLine->pNext; + } while (pWalkLine != pHeadLine); +} + +//given two lines that are both connected to the same two points, merge them. +//returns true if any deleted line was on the new polygon's edge. (could have deleted pValidLineForNewPolygon) +//pMergeLine[1] needs to be deleted after completion +static bool MergeTwoLines( GeneratePolyhedronFromPlanes_Line *pMergeLines[2], int iDyingPolygonReferenceIndices[2], CClipLinkedGeometryDestructors &destructors, bool bAllowNullPolygonCollapse ) +{ + //pMergeLines[0] will be the surviving line and pMergeLines[1] will be eliminated. + Assert( pMergeLines[0]->pPolygons[iDyingPolygonReferenceIndices[0]] == pMergeLines[1]->pPolygons[iDyingPolygonReferenceIndices[1]] ); + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pKillPolygon = (GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)pMergeLines[1]->pPolygons[iDyingPolygonReferenceIndices[1]]; + + //merge the polygon pointer from good side of 1 to the dead side of 0 + pMergeLines[0]->pPolygons[iDyingPolygonReferenceIndices[0]] = pMergeLines[1]->pPolygons[1 - iDyingPolygonReferenceIndices[1]]; + + bool bDeletedALineBorderingNewPolygon = (pMergeLines[1]->pPolygons[0] == NULL) || (pMergeLines[1]->pPolygons[1] == NULL); + + //swap the link from the polygon attached to 1 so it points at 0 instead of 1 + { + GeneratePolyhedronFromPlanes_LineLL *pEditLink = &pMergeLines[1]->PolygonLineLinks[1 - iDyingPolygonReferenceIndices[1]]; + GeneratePolyhedronFromPlanes_LineLL *pSwappedLink = &pMergeLines[0]->PolygonLineLinks[iDyingPolygonReferenceIndices[0]]; + pSwappedLink->pNext = pEditLink->pNext; + pSwappedLink->pPrev = pEditLink->pPrev; + pSwappedLink->pPrev->pNext = pSwappedLink; + pSwappedLink->pNext->pPrev = pSwappedLink; + if( pMergeLines[1]->pPolygons[1 - iDyingPolygonReferenceIndices[1]]->pLines == pEditLink ) + { + pMergeLines[1]->pPolygons[1 - iDyingPolygonReferenceIndices[1]]->pLines = pSwappedLink; + } + } + + //have all associated points use 0 as their head pointer in case any were using 1 as a head pointer. + pMergeLines[0]->pPoints[0]->pConnectedLines = &pMergeLines[0]->PointLineLinks[0]; + pMergeLines[0]->pPoints[1]->pConnectedLines = &pMergeLines[0]->PointLineLinks[1]; + + //unlink 1 from both points + { + for( int i = 0; i != 2; ++i ) + { + GeneratePolyhedronFromPlanes_LineLL *pUnlinkLine = &pMergeLines[1]->PointLineLinks[i]; + pUnlinkLine->pNext->pPrev = pUnlinkLine->pPrev; + pUnlinkLine->pPrev->pNext = pUnlinkLine->pNext; + } + } + + //kill the line + DestructLine( pMergeLines[1], destructors ); + + if( pKillPolygon || bAllowNullPolygonCollapse ) + { + if( pKillPolygon ) //kill the polygon as well + { + DestructPolygon( pKillPolygon, destructors ); + } + + //in cases where we collapse a polygon, it's possible for the new line to be completely redundant along with 1 point in a set of 2 polygons + for( int i = 0; i != 2; ++i ) + { + GeneratePolyhedronFromPlanes_LineLL *pPointLink = &pMergeLines[0]->PointLineLinks[i]; + GeneratePolyhedronFromPlanes_LineLL *pNextLink = pPointLink->pNext; + + if( (pNextLink->pNext == pPointLink) && //only 2 lines connected to this point + (pPointLink->pLine->pPolygons[1 - pPointLink->iReferenceIndex] == pNextLink->pLine->pPolygons[pNextLink->iReferenceIndex]) && + (pPointLink->pLine->pPolygons[pPointLink->iReferenceIndex] == pNextLink->pLine->pPolygons[1 - pNextLink->iReferenceIndex]) ) //and they're bounded by the same 2 polygons + { + Assert( pPointLink->pLine->pPolygons[pPointLink->iReferenceIndex] == pNextLink->pLine->pPolygons[1 - pNextLink->iReferenceIndex] ); //one rotation around a point should yield this as a shared polygon in all cases + + GeneratePolyhedronFromPlanes_Point *pRedundantPoint = pMergeLines[0]->pPoints[i]; + GeneratePolyhedronFromPlanes_LineLL *pSurvivingLine = pRedundantPoint->pConnectedLines; + GeneratePolyhedronFromPlanes_LineLL *pRedundantLine = pSurvivingLine->pNext; + Assert( pSurvivingLine->pNext == pRedundantLine ); + Assert( pRedundantLine->pNext == pSurvivingLine ); + + //link over the redundant point in the surviving line + pSurvivingLine->pLine->pPoints[1 - pSurvivingLine->iReferenceIndex] = pRedundantLine->pLine->pPoints[pRedundantLine->iReferenceIndex]; + + //link over the redunant line from the side opposite the redundant point + { + GeneratePolyhedronFromPlanes_LineLL *pRedunantPointLink = &pRedundantLine->pLine->PointLineLinks[pRedundantLine->iReferenceIndex]; + GeneratePolyhedronFromPlanes_LineLL *pSwapLink = &pSurvivingLine->pLine->PointLineLinks[1 - pSurvivingLine->iReferenceIndex]; + pSwapLink->pNext = pRedunantPointLink->pNext; + pSwapLink->pPrev = pRedunantPointLink->pPrev; + pSwapLink->pNext->pPrev = pSwapLink; + pSwapLink->pPrev->pNext = pSwapLink; + pRedundantLine->pLine->pPoints[pRedundantLine->iReferenceIndex]->pConnectedLines = pSwapLink; + } + + //link past the redundant line in both polygons + for( int j = 0; j != 2; ++j ) + { + if( pRedundantLine->pLine->pPolygons[i] == NULL ) + { + bDeletedALineBorderingNewPolygon = true; + } + else + { + GeneratePolyhedronFromPlanes_LineLL *pLinkOver = &pRedundantLine->pLine->PolygonLineLinks[j]; + + pLinkOver->pNext->pPrev = pLinkOver->pPrev; + pLinkOver->pPrev->pNext = pLinkOver->pNext; + + if( pRedundantLine->pLine->pPolygons[j]->pLines == pLinkOver ) + pRedundantLine->pLine->pPolygons[j]->pLines = pLinkOver->pNext; + } + } + + //kill the redundant line + DestructLine( pRedundantLine->pLine, destructors ); + + //kill the redundant point + DestructPoint( pRedundantPoint, destructors ); + break; + } + } + } + + return bDeletedALineBorderingNewPolygon; +} + + +static inline GeneratePolyhedronFromPlanes_Point *AllocatePoint( GeneratePolyhedronFromPlanes_UnorderedPointLL * &pAllPoints, CStackItemDispenser &pointAllocator, int &iPointCount ) +{ + pAllPoints->pPrev = pointAllocator.Allocate(); + DBG_ONLY( pAllPoints->pPrev->point.debugdata.Reset() ); + pAllPoints->pPrev->pNext = pAllPoints; + pAllPoints = pAllPoints->pPrev; + pAllPoints->pPrev = NULL; + DBG_ONLY( pAllPoints->point.debugdata.bIsNew = true; ); + + Assert( (((size_t)&pAllPoints->point.ptPosition) & 15) == 0 ); + ++iPointCount; + + return &pAllPoints->point; +} + + +static inline GeneratePolyhedronFromPlanes_Line *AllocateLine( GeneratePolyhedronFromPlanes_UnorderedLineLL * &pAllLines, CStackItemDispenser &lineAllocator ) +{ + //before we forget, add this line to the active list + pAllLines->pPrev = lineAllocator.Allocate(); + DBG_ONLY( pAllLines->pPrev->line.debugdata.Reset(); ); + pAllLines->pPrev->pNext = pAllLines; + pAllLines = pAllLines->pPrev; + pAllLines->pPrev = NULL; + + pAllLines->line.InitLineLinks(); + pAllLines->line.planarity = LINE_ONPLANE; + pAllLines->line.bNewLengthThisPass = true; + + DBG_ONLY( pAllLines->line.debugdata.bIsNew = true; ); + return &pAllLines->line; +} + +static inline GeneratePolyhedronFromPlanes_Polygon *AllocatePolygon( GeneratePolyhedronFromPlanes_UnorderedPolygonLL * &pAllPolygons, CStackItemDispenser &polygonAllocator, const Vector &vSurfaceNormal/*, float fPlaneDist*/ ) +{ + pAllPolygons->pPrev = polygonAllocator.Allocate(); + DBG_ONLY( pAllPolygons->pPrev->polygon.debugdata.Reset() ); + pAllPolygons->pPrev->pNext = pAllPolygons; + pAllPolygons = pAllPolygons->pPrev; + pAllPolygons->pPrev = NULL; + + pAllPolygons->polygon.bDead = false; //technically missing all it's sides, but we're fixing it now + pAllPolygons->polygon.bHasNewPoints = true; + pAllPolygons->polygon.bMovedExistingPoints = false; + pAllPolygons->polygon.vSurfaceNormal = vSurfaceNormal; + //pAllPolygons->polygon.fNormalDist = fPlaneDist; + + DBG_ONLY( pAllPolygons->polygon.debugdata.bIsNew = true; ); + return &pAllPolygons->polygon; +} + +#if defined( DBGFLAG_ASSERT ) +int g_iDebugPolyhedronClipProcess = -1; +#endif + +struct MarkPlanarityControlStruct_t +{ + MarkPlanarityControlStruct_t( GeneratePolyhedronFromPlanes_Polygon *pPolygon, + CStackItemDispenser &lineAlloc, GeneratePolyhedronFromPlanes_UnorderedLineLL *&pLines, + CStackItemDispenser &pointAlloc, GeneratePolyhedronFromPlanes_UnorderedPointLL *&pPoints, int &iPoints, float fPlaneEpsilon ) + : pNewPolygon( pPolygon ), + lineAllocator( lineAlloc ), pAllLines( pLines ), + pointAllocator( pointAlloc ), pAllPoints( pPoints ), iPointCount( iPoints ), + fOnPlaneEpsilon( fPlaneEpsilon ), fNegativeOnPlaneEpsilon( -fPlaneEpsilon ) + { + bAllPointsDead = true; + + GeneratePolyhedronFromPlanes_Line *pStartLine = AllocateLine( pAllLines, lineAllocator ); + { + //A bit of setup on the dummy line, links to nothing + pStartLine->pPolygons[0] = NULL; + pStartLine->PolygonLineLinks[0].pNext = &pStartLine->PolygonLineLinks[0]; + pStartLine->PolygonLineLinks[0].pPrev = &pStartLine->PolygonLineLinks[0]; + + pStartLine->pPolygons[1] = pNewPolygon; + pStartLine->PolygonLineLinks[1].pNext = &pStartLine->PolygonLineLinks[1]; + pStartLine->PolygonLineLinks[1].pPrev = &pStartLine->PolygonLineLinks[1]; + + pStartLine->pPoints[0] = NULL; + pStartLine->PointLineLinks[0].pNext = &pStartLine->PointLineLinks[0]; + pStartLine->PointLineLinks[0].pPrev = &pStartLine->PointLineLinks[0]; + + pStartLine->pPoints[1] = NULL; + pStartLine->PointLineLinks[1].pNext = &pStartLine->PointLineLinks[1]; + pStartLine->PointLineLinks[1].pPrev = &pStartLine->PointLineLinks[1]; + + pStartLine->planarity = LINE_ONPLANE; + } + + pActivePolyLine = &pStartLine->PolygonLineLinks[1]; + + pPolygon->pLines = pActivePolyLine; + } + + GeneratePolyhedronFromPlanes_LineLL *pActivePolyLine; + GeneratePolyhedronFromPlanes_Polygon *pNewPolygon; + + CStackItemDispenser &lineAllocator; + GeneratePolyhedronFromPlanes_UnorderedLineLL *&pAllLines; + + CStackItemDispenser &pointAllocator; + GeneratePolyhedronFromPlanes_UnorderedPointLL *&pAllPoints; + int &iPointCount; + + float fOnPlaneEpsilon; + float fNegativeOnPlaneEpsilon; + bool bAllPointsDead; + +#if defined( DBGFLAG_ASSERT ) + fltx4 vCutPlane; +#endif +}; + +GeneratePolyhedronFromPlanes_Line *MarkPlanarity_CreateNewPolyLine( MarkPlanarityControlStruct_t &control ) +{ + GeneratePolyhedronFromPlanes_Line *pNewLine = AllocateLine( control.pAllLines, control.lineAllocator ); + //make sure we can link into it + { + pNewLine->pPoints[0] = NULL; + pNewLine->PointLineLinks[0].pPrev = NULL; + pNewLine->PointLineLinks[0].pNext = NULL; + + pNewLine->pPoints[1] = NULL; + pNewLine->PointLineLinks[1].pPrev = NULL; + pNewLine->PointLineLinks[1].pNext = NULL; + + pNewLine->pPolygons[0] = NULL; + pNewLine->PolygonLineLinks[0].pNext = NULL; + pNewLine->PolygonLineLinks[0].pPrev = NULL; + + pNewLine->pPolygons[1] = control.pNewPolygon; + pNewLine->PolygonLineLinks[1].pNext = control.pActivePolyLine->pNext; + pNewLine->PolygonLineLinks[1].pPrev = control.pActivePolyLine; + + control.pActivePolyLine->pNext->pPrev = &pNewLine->PolygonLineLinks[1]; + control.pActivePolyLine->pNext = &pNewLine->PolygonLineLinks[1]; + control.pActivePolyLine = &pNewLine->PolygonLineLinks[1]; + + control.pActivePolyLine->pLine->planarity = LINE_ONPLANE; + } + return pNewLine; +} + +//design the following algorithms to never crawl past the cutting plane. That way we can get consistent results +void Recursive_MarkPlanarity_OnPlane( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control ); + +void Recursive_MarkPlanarity_Dead( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control ) +{ + Assert( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->planarity == POINT_DEAD ); + //Assert( !pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->debugdata.bVisited || (pLineWalk->pLine->planarity == LINE_DEAD) ); + DBG_ONLY( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->debugdata.bVisited = true; ); + + while( pLineWalk->pLine->planarity == LINE_ALIVE ) //any line connected to a dead point can't be alive, therefore has not been traversed since we only travel through dead points + { + Assert( !pLineWalk->pLine->debugdata.bTested && !pLineWalk->pLine->debugdata.bIsNew ); + DBG_ONLY( pLineWalk->pLine->debugdata.bTested = true; ); + GeneratePolyhedronFromPlanes_Point *pConnectedPoint = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]; + if( pConnectedPoint->fPlaneDist > control.fOnPlaneEpsilon ) + { + //point dead, line is also dead + pConnectedPoint->planarity = POINT_DEAD; + pLineWalk->pLine->planarity = LINE_DEAD; + + //branch into the point as a new root + Recursive_MarkPlanarity_Dead( pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pNext, control ); + } + else if( pConnectedPoint->fPlaneDist >= control.fNegativeOnPlaneEpsilon ) + { + //point onplane, line is dead + pConnectedPoint->planarity = POINT_ONPLANE; + pLineWalk->pLine->planarity = LINE_DEAD; + + control.pNewPolygon->bMovedExistingPoints = true; //mark the polygon as using off-plane points + + Recursive_MarkPlanarity_OnPlane( pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pNext, control ); + + control.pActivePolyLine->pLine->pPoints[0] = pConnectedPoint; + control.pActivePolyLine->pLine->PointLineLinks[0].pNext = &pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex]; + control.pActivePolyLine->pLine->PointLineLinks[0].pPrev = pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pPrev; + } + else + { + //point alive, line is cut + Assert( pConnectedPoint->planarity == POINT_ALIVE ); + pLineWalk->pLine->planarity = LINE_CUT; + control.bAllPointsDead = false; + + + int iDeadIndex = 1 - pLineWalk->iReferenceIndex; + int iLivingIndex = pLineWalk->iReferenceIndex; + + GeneratePolyhedronFromPlanes_Point *pDeadPoint = pLineWalk->pLine->pPoints[iDeadIndex]; + GeneratePolyhedronFromPlanes_Point *pLivingPoint = pLineWalk->pLine->pPoints[iLivingIndex]; + + //Generate a new point + GeneratePolyhedronFromPlanes_Point *pNewPoint = AllocatePoint( control.pAllPoints, control.pointAllocator, control.iPointCount ); + { + Assert( (pDeadPoint->fPlaneDist - pLivingPoint->fPlaneDist) > control.fOnPlaneEpsilon ); + float fInvTotalDist = 1.0f/(pDeadPoint->fPlaneDist - pLivingPoint->fPlaneDist); //subtraction because the living index is known to be negative + pNewPoint->ptPosition = SubSIMD( MulSIMD(pLivingPoint->ptPosition, ReplicateX4(pDeadPoint->fPlaneDist * fInvTotalDist)), + MulSIMD(pDeadPoint->ptPosition, ReplicateX4(pLivingPoint->fPlaneDist * fInvTotalDist)) ); + SubFloat( pNewPoint->ptPosition, 3 ) = -1.0f; + +#if defined( DBGFLAG_ASSERT ) //check length of line that will remain in the polyhedron, and doublecheck planar distance of new point + { + fltx4 vAliveLineDiff = SubSIMD( pNewPoint->ptPosition, pLivingPoint->ptPosition ); + Vector vecAliveLineDiff( SubFloat( vAliveLineDiff, 0 ), SubFloat( vAliveLineDiff, 1 ), SubFloat( vAliveLineDiff, 2 ) ); + float fLineLength = vecAliveLineDiff.Length(); + AssertMsg( fLineLength > control.fOnPlaneEpsilon, "Dangerously short line" ); + + fltx4 vDist = MulSIMD( control.vCutPlane, pNewPoint->ptPosition ); + float fDebugDist; + fDebugDist = SubFloat( vDist, 0 ) + SubFloat( vDist, 1 ) + SubFloat( vDist, 2 ) + SubFloat( vDist, 3 ); //just for looking at in watch windows + //Assert( fabs( fDebugDist ) <= control.fOnPlaneEpsilon ); + } +#endif + pNewPoint->planarity = POINT_ONPLANE; + pNewPoint->fPlaneDist = 0.0f; + } + + pLineWalk->pLine->pPolygons[0]->bHasNewPoints = true; + pLineWalk->pLine->pPolygons[1]->bHasNewPoints = true; + + + GeneratePolyhedronFromPlanes_Line *pCompletedPolyLine = control.pActivePolyLine->pLine; + GeneratePolyhedronFromPlanes_Line *pNewPolyLine = MarkPlanarity_CreateNewPolyLine( control ); + + GeneratePolyhedronFromPlanes_Line *pCutLine = pLineWalk->pLine; + GeneratePolyhedronFromPlanes_Line *pNewLivingLine = AllocateLine( control.pAllLines, control.lineAllocator ); + + //going to relink the cut line to be between the dead point and the new point + //also going to create a new line between new point and live point + //The new line will copy the cut line's iReferenceIndex orientation + { + //relink point pointers + pNewLivingLine->pPoints[iDeadIndex] = pNewPoint; + pNewLivingLine->pPoints[iLivingIndex] = pCutLine->pPoints[iLivingIndex]; + pCutLine->pPoints[iLivingIndex] = pNewPoint; + pNewPoint->pConnectedLines = &pNewLivingLine->PointLineLinks[iDeadIndex]; + pCutLine->planarity = LINE_DEAD; + pNewLivingLine->planarity = LINE_ALIVE; + + + //new line steals cut line's living index linkages + pNewLivingLine->PointLineLinks[iLivingIndex].pNext = pCutLine->PointLineLinks[iLivingIndex].pNext; + pNewLivingLine->PointLineLinks[iLivingIndex].pPrev = pCutLine->PointLineLinks[iLivingIndex].pPrev; + pNewLivingLine->PointLineLinks[iLivingIndex].pNext->pPrev = &pNewLivingLine->PointLineLinks[iLivingIndex]; + pNewLivingLine->PointLineLinks[iLivingIndex].pPrev->pNext = &pNewLivingLine->PointLineLinks[iLivingIndex]; + pNewLivingLine->pPoints[iLivingIndex]->pConnectedLines = &pNewLivingLine->PointLineLinks[iLivingIndex]; + + //crosslink cut living to new dead + pCutLine->PointLineLinks[iLivingIndex].pNext = pCutLine->PointLineLinks[iLivingIndex].pPrev = &pNewLivingLine->PointLineLinks[iDeadIndex]; + pNewLivingLine->PointLineLinks[iDeadIndex].pNext = pNewLivingLine->PointLineLinks[iDeadIndex].pPrev = &pCutLine->PointLineLinks[iLivingIndex]; + + //fix up polygon linkages + pNewLivingLine->pPolygons[0] = pCutLine->pPolygons[0]; + pNewLivingLine->pPolygons[1] = pCutLine->pPolygons[1]; + + //insert after cut line for 0 polygon + pNewLivingLine->PolygonLineLinks[0].pNext = pCutLine->PolygonLineLinks[0].pNext; + pNewLivingLine->PolygonLineLinks[0].pPrev = &pCutLine->PolygonLineLinks[0]; + pCutLine->PolygonLineLinks[0].pNext = &pNewLivingLine->PolygonLineLinks[0]; + pNewLivingLine->PolygonLineLinks[0].pNext->pPrev = &pNewLivingLine->PolygonLineLinks[0]; + + //insert before cut line for 1 polygon + pNewLivingLine->PolygonLineLinks[1].pNext = &pCutLine->PolygonLineLinks[1]; + pNewLivingLine->PolygonLineLinks[1].pPrev = pCutLine->PolygonLineLinks[1].pPrev; + pCutLine->PolygonLineLinks[1].pPrev = &pNewLivingLine->PolygonLineLinks[1]; + pNewLivingLine->PolygonLineLinks[1].pPrev->pNext = &pNewLivingLine->PolygonLineLinks[1]; + } + + + //We now should have everything we need to finish constructing pCompletedPolyLine + { + //link polygon completed line to new point + pCompletedPolyLine->pPoints[1] = pNewPoint; + + //Make a T junction between cut line, new line, and completed polygon line + pCompletedPolyLine->PointLineLinks[1].pPrev = &pCutLine->PointLineLinks[iLivingIndex]; + pCutLine->PointLineLinks[iLivingIndex].pNext = &pCompletedPolyLine->PointLineLinks[1]; + + pCompletedPolyLine->PointLineLinks[1].pNext = &pNewLivingLine->PointLineLinks[iDeadIndex]; + pNewLivingLine->PointLineLinks[iDeadIndex].pPrev = &pCompletedPolyLine->PointLineLinks[1]; + + + //pCompletedLine->pLine->pPoints[0] should already have valid values that just need to be linked back in + pCompletedPolyLine->PointLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[0]; + pCompletedPolyLine->PointLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[0]; + + //link outwardly into the patched up polygon + pCompletedPolyLine->pPolygons[0] = pNewLivingLine->pPolygons[iDeadIndex]; //left side of line going to living point is the polygon that will survive the planar clip + pCompletedPolyLine->PolygonLineLinks[0].pPrev = &pNewLivingLine->PolygonLineLinks[iDeadIndex]; + pCompletedPolyLine->PolygonLineLinks[0].pNext = pNewLivingLine->PolygonLineLinks[iDeadIndex].pNext; + pCompletedPolyLine->PolygonLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PolygonLineLinks[0]; + pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PolygonLineLinks[0]; + +#if defined( DBGFLAG_ASSERT ) + if( pCompletedPolyLine->pPoints[0] && pCompletedPolyLine->pPoints[1] ) + { + fltx4 vLineTemp = SubSIMD( pCompletedPolyLine->pPoints[1]->ptPosition, pCompletedPolyLine->pPoints[0]->ptPosition ); + AssertMsg( (SubFloat( vLineTemp, 0 ) != 0.0f) || (SubFloat( vLineTemp, 1 ) != 0.0f) || (SubFloat( vLineTemp, 2 ) != 0.0f), "Created zero length line" ); + } +#endif + } + + //keep updating the drag line + pNewPolyLine->pPoints[0] = pNewPoint; + pNewPolyLine->PointLineLinks[0].pNext = &pCutLine->PointLineLinks[iLivingIndex]; + pNewPolyLine->PointLineLinks[0].pPrev = &pNewLivingLine->PointLineLinks[iDeadIndex]; + } + + pLineWalk = pLineWalk->pNext; + } + + /*if( pLineWalk->pLine->planarity == LINE_DEAD ) + { + //left polygon is dead +#if defined( DBGFLAG_ASSERT ) //make sure + { + //walk the polygon and ensure it should be dead + GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalkHead = &pLineWalk->pLine->PolygonLineLinks[1 - pLineWalk->iReferenceIndex]; + GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalk = pDebugLineWalkHead; + do + { + Assert( pDebugLineWalk->pLine->planarity != LINE_ALIVE ); + pDebugLineWalk = pDebugLineWalk->pNext; + } while (pDebugLineWalk != pDebugLineWalkHead); + } +#endif + + Assert( !pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]->bDead ); //not already marked dead + pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]->bDead = true; + }*/ +} + +bool Recursive_CanOnPlanePolyCrawlDead( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control ); + +//Once we start traversing on-plane points, our options reduce. We do this to ensure we never traverse a section that isn't touching the cut plane +void Recursive_MarkPlanarity_OnPlane( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control ) +{ + Assert( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->planarity == POINT_ONPLANE ); + //Assert( !pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->debugdata.bVisited ); + DBG_ONLY( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->debugdata.bVisited = true; ); + + //onplane's are only allowed to crawl within their polygon. So if the very next line is onplane, recurse. Alive has no work to do, and dead will be picked up later in the dead-crawling algorithm + if( pLineWalk->pLine->planarity == LINE_ALIVE ) //not already traversed + { + Assert( !pLineWalk->pLine->debugdata.bTested && !pLineWalk->pLine->debugdata.bIsNew ); + //DBG_ONLY( pLineWalk->pLine->debugdata.bTested = true; ); + GeneratePolyhedronFromPlanes_Point *pConnectedPoint = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]; + if( (pConnectedPoint->fPlaneDist > control.fOnPlaneEpsilon) && !Recursive_CanOnPlanePolyCrawlDead( pLineWalk, control ) ) + { + //do nothing, for consistency we want to be sure we traverse from the dead point to here + } + else if( pConnectedPoint->fPlaneDist >= control.fNegativeOnPlaneEpsilon ) + { + //point onplane, line is onplane, line is part of the new polygon + pConnectedPoint->planarity = POINT_ONPLANE; + pLineWalk->pLine->planarity = LINE_ONPLANE; + + Recursive_MarkPlanarity_OnPlane( pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pNext, control ); + + //Stitch the line we just traversed into the polygon as-is + { + //remove from existing dead polygon + int iDeadSide = 1 - pLineWalk->iReferenceIndex; + pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext->pPrev = pLineWalk->pLine->PolygonLineLinks[iDeadSide].pPrev; + pLineWalk->pLine->PolygonLineLinks[iDeadSide].pPrev->pNext = pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext; + + /*if( pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext == &pLineWalk->pLine->PolygonLineLinks[iDeadSide] ) + { + //this was the last line in the polygon + pLineWalk->pLine->pPolygons[iDeadSide]->pLines = NULL; + pLineWalk->pLine->pPolygons[iDeadSide]->bDead = true; + } + else*/ + { + pLineWalk->pLine->pPolygons[iDeadSide]->pLines = pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext; + } + + //now replace it with the new polygon + pLineWalk->pLine->pPolygons[iDeadSide] = control.pNewPolygon; + + //insert before pActivePolyLine + pLineWalk->pLine->PolygonLineLinks[iDeadSide].pPrev = control.pActivePolyLine->pPrev; + control.pActivePolyLine->pPrev->pNext = &pLineWalk->pLine->PolygonLineLinks[iDeadSide]; + + pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext = control.pActivePolyLine; + control.pActivePolyLine->pPrev = &pLineWalk->pLine->PolygonLineLinks[iDeadSide]; + } + } + else + { + //point alive, line is alive, root point stitched into the new polygon + GeneratePolyhedronFromPlanes_Line *pCompletedPolyLine = control.pActivePolyLine->pLine; + + //We now should have everything we need to finish constructing pCompletedLine + { + //pCompletedLine->pLine->pPoints[0] should already have valid values that just need to be linked back in + pCompletedPolyLine->PointLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[0]; + pCompletedPolyLine->PointLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[0]; + + pCompletedPolyLine->pPoints[1] = pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]; //root point is endpoint for completed polygon line + pCompletedPolyLine->PointLineLinks[1].pPrev = pLineWalk->pPrev; + pCompletedPolyLine->PointLineLinks[1].pNext = pLineWalk; + pCompletedPolyLine->PointLineLinks[1].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[1]; + pCompletedPolyLine->PointLineLinks[1].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[1]; + + //link outwardly into the patched up polygon + pCompletedPolyLine->pPolygons[0] = pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]; //left side of line going to live point is the polygon that will survive the planar clip + pCompletedPolyLine->PolygonLineLinks[0].pPrev = &pLineWalk->pLine->PolygonLineLinks[1 - pLineWalk->iReferenceIndex]; + pCompletedPolyLine->PolygonLineLinks[0].pNext = pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext; + pCompletedPolyLine->PolygonLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PolygonLineLinks[0]; + pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PolygonLineLinks[0]; + } + +#if defined( DBGFLAG_ASSERT ) + if( pCompletedPolyLine->pPoints[0] && pCompletedPolyLine->pPoints[1] ) + { + fltx4 vLineTemp = SubSIMD( pCompletedPolyLine->pPoints[1]->ptPosition, pCompletedPolyLine->pPoints[0]->ptPosition ); + AssertMsg( (SubFloat( vLineTemp, 0 ) != 0.0f) || (SubFloat( vLineTemp, 1 ) != 0.0f) || (SubFloat( vLineTemp, 2 ) != 0.0f), "Created zero length line" ); + } +#endif + + //point 0 will be filled in as we backtrack to a dead point that recursed into Recursive_MarkPlanarity_OnPlane(). If we recursed from another onplane point, we'll be recycling its line + MarkPlanarity_CreateNewPolyLine( control ); + } } - //Collections of dead pointers for reallocation, shouldn't be touched until the current loop iteration is done. - GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPointCollection = NULL; - GeneratePolyhedronFromPlanes_UnorderedLineLL *pDeadLineCollection = NULL; - GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDeadPolygonCollection = NULL; - GeneratePolyhedronFromPlanes_LineLL *pDeadLineLinkCollection = NULL; + /*if( pLineWalk->pLine->planarity == LINE_DEAD ) + { + //left polygon is dead +#if defined( DBGFLAG_ASSERT ) //make sure + { + //walk the polygon and ensure it should be dead + GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalkHead = &pLineWalk->pLine->PolygonLineLinks[1 - pLineWalk->iReferenceIndex]; + GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalk = pDebugLineWalkHead; + do + { + Assert( pDebugLineWalk->pLine->planarity != LINE_ALIVE ); + pDebugLineWalk = pDebugLineWalk->pNext; + } while (pDebugLineWalk != pDebugLineWalkHead); + } +#endif + Assert( !pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]->bDead ); //not already marked dead + pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]->bDead = true; + }*/ +} + + +bool Recursive_CanOnPlanePolyCrawlDead( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control ) +{ + pLineWalk = pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pNext; + if( pLineWalk->pLine->planarity == LINE_ALIVE ) //not already traversed + { + GeneratePolyhedronFromPlanes_Point *pConnectedPoint = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]; + if( pConnectedPoint->fPlaneDist > control.fOnPlaneEpsilon ) + { + GeneratePolyhedronFromPlanes_LineLL *pTestAlive = pLineWalk->pNext; + if( pTestAlive->pLine->pPoints[pTestAlive->iReferenceIndex]->fPlaneDist < control.fNegativeOnPlaneEpsilon ) + { + //couldn't have possibly crawled here from the other direction if we continue the onplane streak + return Recursive_CanOnPlanePolyCrawlDead( pLineWalk, control ); + } + } + if( pConnectedPoint->fPlaneDist >= control.fNegativeOnPlaneEpsilon ) + { + GeneratePolyhedronFromPlanes_LineLL *pTestAlive = pLineWalk->pNext; + if( pTestAlive->pLine->pPoints[pTestAlive->iReferenceIndex]->fPlaneDist < control.fNegativeOnPlaneEpsilon ) + { + //couldn't have possibly crawled here from the other direction + return true; + } + } + } + + + return false; +} + +void RecomputePolygonSurfaceNormal( GeneratePolyhedronFromPlanes_Polygon *pPolygon ) +{ + Vector vAggregateNormal = vec3_origin; + + GeneratePolyhedronFromPlanes_LineLL *pLineWalkHead = pPolygon->pLines; + GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pLineWalkHead->pPrev; + Vector vLastLine; + + fltx4 vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition ); + pLineWalk = pLineWalk->pNext; + vLastLine.Init( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) ); + do + { + vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition ); + Vector vThisLine( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) ); + Vector vCross = vThisLine.Cross( vLastLine ); + vAggregateNormal += vThisLine.Cross( vLastLine ); //intentionally not normalizing until the end. Larger lines deserve more influence in the result + + vLastLine = vThisLine; + pLineWalk = pLineWalk->pNext; + } while ( pLineWalk != pLineWalkHead ); + + vAggregateNormal.NormalizeInPlace(); + pPolygon->vSurfaceNormal = vAggregateNormal; +} + +CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, int iPointCount, const fltx4 *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory, fltx4 vShiftResultPositions ) +{ + //const float fNegativeOnPlaneEpsilon = -fOnPlaneEpsilon; + const float fOnPlaneEpsilonSquared = fOnPlaneEpsilon * fOnPlaneEpsilon; + +#ifdef DBGFLAG_ASSERT + GeneratePolyhedronFromPlanes_Point *pStartPoint = NULL; + + static int iPolyhedronClipCount = 0; + ++iPolyhedronClipCount; +#endif + + size_t iStackMemorySize = (64 * 1024); //start off trying to allocate 128k + void *pStackMemory = stackalloc( iStackMemorySize ); + while( pStackMemory == NULL ) + { + iStackMemorySize = iStackMemorySize >> 1; + pStackMemory = stackalloc( iStackMemorySize ); + } + CStackMemoryDispenser memoryDispenser( pStackMemory, iStackMemorySize ); + + //Collections of dead pointers for reallocation, data in them shouldn't be touched until the current loop iteration is done. + GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPointCollection = NULL; + +#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS + GeneratePolyhedronFromPlanes_UnorderedLineLL *pDeadLineCollection = NULL; + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDeadPolygonCollection = NULL; +#endif + + CStackItemDispenser pointAllocator( memoryDispenser ); + CStackItemDispenser lineAllocator( memoryDispenser ); + CStackItemDispenser polygonAllocator( memoryDispenser ); + +#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS + CClipLinkedGeometryDestructors destructors( iPointCount, pAllPolygons, pDeadPolygonCollection, pAllLines, pDeadLineCollection, pAllPoints, pDeadPointCollection ); +#else + CClipLinkedGeometryDestructors destructors( iPointCount, pAllPolygons, polygonAllocator, pAllLines, lineAllocator, pAllPoints, pDeadPointCollection ); +#endif + + DBG_ONLY( destructors.DebugCutHistory.AddToTail( ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions ) ) ); + DBG_ONLY( destructors.DebugCutPlaneIndex.AddToTail( -1 ) ); for( int iCurrentPlane = 0; iCurrentPlane != iPlaneCount; ++iCurrentPlane ) { - //clear out line work variables +#if defined( DBGFLAG_ASSERT ) + destructors.bDebugTrigger = (g_iDebugPolyhedronClipProcess == iCurrentPlane); + if( destructors.bDebugTrigger ) { + g_iDebugPolyhedronClipProcess = -1; //remove the need for cleanup code wherever someone wanted this to break; + } +#endif + //clear out work variables + { + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons; + do + { + pActivePolygonWalk->polygon.bDead = false; + pActivePolygonWalk->polygon.bHasNewPoints = false; + pActivePolygonWalk->polygon.bMovedExistingPoints = false; + DBG_ONLY( pActivePolygonWalk->polygon.debugdata.Reset(); ); + + pActivePolygonWalk = pActivePolygonWalk->pNext; + } while( pActivePolygonWalk ); + GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalk = pAllLines; do { - pActiveLineWalk->pLine->bAlive = false; - pActiveLineWalk->pLine->bCut = false; + pActiveLineWalk->line.planarity = LINE_ALIVE; + pActiveLineWalk->line.bNewLengthThisPass = false; + DBG_ONLY( pActiveLineWalk->line.debugdata.Reset(); ); pActiveLineWalk = pActiveLineWalk->pNext; } while( pActiveLineWalk ); - } - - //TODO: Move these pointers into a reallocation pool - pDeadPointCollection = NULL; - pDeadLineCollection = NULL; - pDeadLineLinkCollection = NULL; - pDeadPolygonCollection = NULL; - Vector vNormal = *((Vector *)&pOutwardFacingPlanes[(iCurrentPlane * 4) + 0]); - /*double vNormalAsDouble[3]; - vNormalAsDouble[0] = vNormal.x; - vNormalAsDouble[1] = vNormal.y; - vNormalAsDouble[2] = vNormal.z;*/ - float fPlaneDist = pOutwardFacingPlanes[(iCurrentPlane * 4) + 3]; - - //=================================================================================================== - // Step 1: Categorize each point as being either cut, split, or alive - //=================================================================================================== - { - bool bAllPointsDead = true; - bool bAllPointsAlive = true; - - //find point distances from the plane GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints; do { - GeneratePolyhedronFromPlanes_Point *pPoint = pActivePointWalk->pPoint; - float fPointDist = vNormal.Dot( pPoint->ptPosition ) - fPlaneDist; - if( fPointDist > fOnPlaneEpsilon ) - { - pPoint->planarity = POINT_DEAD; //point is dead, bang bang - - //mark connected lines as cut - GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pPoint->pConnectedLines; - GeneratePolyhedronFromPlanes_LineLL *pFirstLine = pLineWalk; - do - { - pLineWalk->pLine->bCut = true; - pLineWalk = pLineWalk->pNext; - } while( pLineWalk != pFirstLine ); - - bAllPointsAlive = false; - } - else if( fPointDist <= fNegativeOnPlaneEpsilon ) - { - pPoint->planarity = POINT_ALIVE; //point is in behind plane, not voted off the island....yet - bAllPointsDead = false; - - //mark connected lines as alive - GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pPoint->pConnectedLines; - GeneratePolyhedronFromPlanes_LineLL *pFirstLine = pLineWalk; - do - { - pLineWalk->pLine->bAlive = true; //mark the line as alive - pLineWalk = pLineWalk->pNext; - } while( pLineWalk != pFirstLine ); - } - else - { - pPoint->planarity = POINT_ONPLANE; //point is on the plane, he's everyone's buddy - - //Project on-plane points leaning towards death closer to the plane. This battles floating point precision decay. - // Consider the case of a large on-plane epsilon leaving protrusions over time - /*if( fPointDist < 0.0f ) - { - double distAsDouble = fPointDist; - double vPositionAsDouble[3]; - vPositionAsDouble[0] = pPoint->ptPosition.x; - vPositionAsDouble[1] = pPoint->ptPosition.y; - vPositionAsDouble[2] = pPoint->ptPosition.z; - - pPoint->ptPosition.x = vPositionAsDouble[0] - (distAsDouble * vNormalAsDouble[0]); - pPoint->ptPosition.y = vPositionAsDouble[1] - (distAsDouble * vNormalAsDouble[1]); - pPoint->ptPosition.z = vPositionAsDouble[2] - (distAsDouble * vNormalAsDouble[2]); - -#if ( 0 && defined( _DEBUG ) ) - float fDebugDist = vNormal.Dot( pPoint->ptPosition ) - fPlaneDist; //just for looking at in watch windows - AssertMsg( fabs( fDebugDist ) < fabs(fPointDist), "Projected point is further from plane than unprojected." ); -#endif - fPointDist = vNormal.Dot( pPoint->ptPosition ) - fPlaneDist; //recompute dist (not guaranteed to be 0.0 like we want) - }*/ - } - - pPoint->fPlaneDist = fPointDist; + pActivePointWalk->point.planarity = POINT_ALIVE; + DBG_ONLY( pActivePointWalk->point.debugdata.Reset(); ); pActivePointWalk = pActivePointWalk->pNext; } while( pActivePointWalk ); + } + + while( pDeadPointCollection != NULL ) + { + GeneratePolyhedronFromPlanes_UnorderedPointLL *pFree = pDeadPointCollection; + pDeadPointCollection = pDeadPointCollection->pNext; + pointAllocator.Free( pFree ); + } - if( bAllPointsDead ) //all the points either died or are on the plane, no polyhedron left at all - { -#ifdef _DEBUG - for( int i = DebugCutHistory.Count(); --i >= 0; ) - { - if( DebugCutHistory[i] ) - DebugCutHistory[i]->Release(); - } - DebugCutHistory.RemoveAll(); +#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS + while( pDeadLineCollection != NULL ) + { + GeneratePolyhedronFromPlanes_UnorderedLineLL *pFree = pDeadLineCollection; + pDeadLineCollection = pDeadLineCollection->pNext; + lineAllocator.Free( pFree ); + } + + while( pDeadPolygonCollection != NULL ) + { + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pFree = pDeadPolygonCollection; + pDeadPolygonCollection = pDeadPolygonCollection->pNext; + polygonAllocator.Free( pFree ); + } #endif +#if defined( DBGFLAG_ASSERT ) + int iPatchMethod = -1; +#endif + + + //find point distances from the plane + ComputePlanarDistances( pAllPoints, iPointCount, pOutwardFacingPlanes[iCurrentPlane] ); + + //find "most dead" point. We'll be using that as a starting point for an algorithm that walks the edge of the new polygon + //this walk method lets us categorize points without getting into impossible situations where 2 diagonal corners of a quad are dead, but the other points are onplane/alive + GeneratePolyhedronFromPlanes_Point *pMostDeadPoint = &pAllPoints->point; + float fMostDeadPointDist = pAllPoints->point.fPlaneDist; + { + GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints->pNext; + do + { + if( pActivePointWalk->point.fPlaneDist > fMostDeadPointDist ) + { + pMostDeadPoint = &pActivePointWalk->point; + fMostDeadPointDist = pActivePointWalk->point.fPlaneDist; + } + + pActivePointWalk = pActivePointWalk->pNext; + } while( pActivePointWalk ); + } + + if( fMostDeadPointDist <= fOnPlaneEpsilon ) + { + //no cuts made + continue; + } + + Vector vSurfaceNormal( SubFloat( pOutwardFacingPlanes[iCurrentPlane], 0 ), SubFloat( pOutwardFacingPlanes[iCurrentPlane], 1 ), SubFloat( pOutwardFacingPlanes[iCurrentPlane], 2 ) ); + GeneratePolyhedronFromPlanes_Polygon *pNewPolygon = AllocatePolygon( pAllPolygons, polygonAllocator, vSurfaceNormal ); + pMostDeadPoint->planarity = POINT_DEAD; + { + MarkPlanarityControlStruct_t control( pNewPolygon, lineAllocator, pAllLines, pointAllocator, pAllPoints, iPointCount, fOnPlaneEpsilon ); +#if defined( DBGFLAG_ASSERT ) + GeneratePolyhedronFromPlanes_LineLL *pStartLine = control.pActivePolyLine; + control.vCutPlane = pOutwardFacingPlanes[iCurrentPlane]; +#endif + Recursive_MarkPlanarity_Dead( pMostDeadPoint->pConnectedLines, control ); + + //it's possible that the crawling algorithm didn't encounter any live points if the plane didn't cut any lines (All intersections between plane and mesh were at an existing vertex) + if( control.bAllPointsDead ) + { + //doublecheck + GeneratePolyhedronFromPlanes_UnorderedPointLL *pPointWalk = pAllPoints; + while( pPointWalk ) + { + if( pPointWalk->point.planarity == POINT_ALIVE ) + { + control.bAllPointsDead = false; + break; + } + pPointWalk = pPointWalk->pNext; + } + } + + if( control.bAllPointsDead ) //all the points either died or are on the plane, no polyhedron left at all + { +#ifdef DBGFLAG_ASSERT + GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints->pNext; + do + { + Assert( pActivePointWalk->point.planarity != POINT_ALIVE ); + + pActivePointWalk = pActivePointWalk->pNext; + } while( pActivePointWalk ); +#endif + +#if defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + Assert_DumpPolyhedron( g_bDumpNullPolyhedrons == false ); //if someone set it to true, we'll dump the polyhedron then halt +#endif return NULL; } - if( bAllPointsAlive ) - continue; //no cuts made + Assert( (control.pActivePolyLine->pNext == pStartLine) || (pStartLine->pLine->pPoints[0] != NULL) ); + Assert( (pStartLine->pLine->pPoints[1] != NULL) || (pStartLine == control.pActivePolyLine) ); - - //Scan for onplane points connected to only other onplane/dead points, these points get downgraded to dead status. + //search/mark dead polygons, there should be a way to do this in the crawl algorithm { - pActivePointWalk = pAllPoints; - do + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygonWalk = pAllPolygons; + + while( pPolygonWalk ) { - if( pActivePointWalk->pPoint->planarity == POINT_ONPLANE ) + if( &pPolygonWalk->polygon != control.pNewPolygon ) { - GeneratePolyhedronFromPlanes_LineLL *pOnPlaneLineWalk = pActivePointWalk->pPoint->pConnectedLines; - GeneratePolyhedronFromPlanes_LineLL *pStartLineWalk = pOnPlaneLineWalk; - bool bDead = true; //assume it's dead and disprove - do + bool bDead = true; + GeneratePolyhedronFromPlanes_LineLL *pStartLine = pPolygonWalk->polygon.pLines; + GeneratePolyhedronFromPlanes_LineLL *pWalkLine = pStartLine; + do { - if ( pOnPlaneLineWalk->pLine->bAlive ) + Assert( pWalkLine->pLine->pPolygons[pWalkLine->iReferenceIndex] == &pPolygonWalk->polygon ); + if( pWalkLine->pLine->planarity & LINE_ALIVE ) { bDead = false; + break; } - else if ( pOnPlaneLineWalk->pLine->bCut ) - { - //connected to a dead point. - if( pOnPlaneLineWalk->pNext->pLine->bCut || pOnPlaneLineWalk->pPrev->pLine->bCut ) - { - //This on-plane point is surrounded by dead points on one polygon of the polyhedron. - // We have to downgrade this point to dead to avoid situations where float imprecision - // turns the polyhedron into a *slightly* concave shape. Concave shapes might break this algorithm, even falsely concave shapes. - bDead = true; - break; - } - } - - pOnPlaneLineWalk = pOnPlaneLineWalk->pNext; - } while( pOnPlaneLineWalk != pStartLineWalk ); - + pWalkLine = pWalkLine->pNext; + } while ( pWalkLine != pStartLine ); if( bDead ) { - pActivePointWalk->pPoint->planarity = POINT_DEAD; - - pOnPlaneLineWalk = pStartLineWalk; - - //mark connected lines as cut - do - { - pOnPlaneLineWalk->pLine->bCut = true; - pOnPlaneLineWalk = pOnPlaneLineWalk->pNext; - } while( pOnPlaneLineWalk != pStartLineWalk ); + pPolygonWalk->polygon.bDead = true; } } - pActivePointWalk = pActivePointWalk->pNext; - } while( pActivePointWalk ); - } -#ifdef _DEBUG - PlaneCutHistory.AddToTail( &pOutwardFacingPlanes[iCurrentPlane * 4] ); -#endif - } - - - - -#ifdef _DEBUG - //Run around the edges of all the polygons and ensure they don't have more than one point of lowered "alive" status (alive > onplane > dead) surrounded by higher status - // It indicates a concave shape. It's impossible to have it occur in theoretical space. But floating point numbers introduce error. - { - GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDebugPolygonWalk = pAllPolygons; - do - { - int iSurroundedCount = 0; - GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalk = pDebugPolygonWalk->pPolygon->pLines; - GeneratePolyhedronFromPlanes_LineLL *pFirstDebugLine = pDebugLineWalk; - - do - { - PolyhedronPointPlanarity currentPlanarity = pDebugLineWalk->pLine->pPoints[pDebugLineWalk->iReferenceIndex]->planarity; - GeneratePolyhedronFromPlanes_LineLL *pNext = pDebugLineWalk->pNext; - PolyhedronPointPlanarity nextPlanarity = pNext->pLine->pPoints[pNext->iReferenceIndex]->planarity; + pPolygonWalk = pPolygonWalk->pNext; + } + } - if( currentPlanarity < nextPlanarity ) + GeneratePolyhedronFromPlanes_LineLL *pFinal = control.pActivePolyLine; + + if( pFinal->pNext->pLine->pPoints[1 - pFinal->pNext->iReferenceIndex] == NULL ) + { + //last line redundant, copy partial data into first line + GeneratePolyhedronFromPlanes_LineLL *pFirst = pFinal->pNext; + Assert_DumpPolyhedron( pFinal != pFirst ); + Assert_DumpPolyhedron( pFirst->pLine->pPoints[pFirst->iReferenceIndex] != NULL ); + Assert_DumpPolyhedron( pFinal->pLine->pPoints[1 - pFinal->iReferenceIndex] != NULL ); + + //link over redundant last line + pFirst->pPrev = pFinal->pPrev; + pFinal->pPrev->pNext = pFirst; + + //fill in first point of first line + pFirst->pLine->pPoints[1 - pFirst->iReferenceIndex] = pFinal->pLine->pPoints[1 - pFinal->iReferenceIndex]; + pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex].pNext = pFinal->pLine->PointLineLinks[1 - pFinal->iReferenceIndex].pNext; + pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex].pPrev = pFinal->pLine->PointLineLinks[1 - pFinal->iReferenceIndex].pPrev; + + pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex].pNext->pPrev = &pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex]; + pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex].pPrev->pNext = &pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex]; + + control.pNewPolygon->pLines = pFirst; + DestructLine( pFinal->pLine, destructors ); + +#if defined( DBGFLAG_ASSERT ) + iPatchMethod = 1; + { + fltx4 vLineTemp = SubSIMD( pFirst->pLine->pPoints[1]->ptPosition, pFirst->pLine->pPoints[0]->ptPosition ); + AssertMsg( (SubFloat( vLineTemp, 0 ) != 0.0f) || (SubFloat( vLineTemp, 1 ) != 0.0f) || (SubFloat( vLineTemp, 2 ) != 0.0f), "Created zero length line" ); + } +#endif + } + else + { + if( pFinal->pLine->pPoints[1 - pFinal->iReferenceIndex] != pFinal->pNext->pLine->pPoints[1 - pFinal->pNext->iReferenceIndex] ) + { + //link up the last line to the first line + GeneratePolyhedronFromPlanes_LineLL *pFirstLine = pFinal->pNext; + + GeneratePolyhedronFromPlanes_Line *pCompletedPolyLine = pFinal->pLine; + + //We now should have everything we need to finish constructing pCompletedLine { - GeneratePolyhedronFromPlanes_LineLL *pPrev = pDebugLineWalk->pPrev; - PolyhedronPointPlanarity prevPlanarity = pPrev->pLine->pPoints[pPrev->iReferenceIndex]->planarity; + //pCompletedLine->pLine->pPoints[0] should already have valid values that just need to be linked back in + pCompletedPolyLine->PointLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[0]; + pCompletedPolyLine->PointLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[0]; - if( currentPlanarity < prevPlanarity ) + pCompletedPolyLine->pPoints[1] = pFirstLine->pLine->pPoints[1 - pFirstLine->iReferenceIndex]; + pCompletedPolyLine->PointLineLinks[1].pPrev = &pFirstLine->pLine->PointLineLinks[1 - pFirstLine->iReferenceIndex]; + pCompletedPolyLine->PointLineLinks[1].pNext = pFirstLine->pLine->PointLineLinks[1 - pFirstLine->iReferenceIndex].pNext; + Assert_DumpPolyhedron( pCompletedPolyLine->PointLineLinks[1].pNext->pLine->planarity != LINE_DEAD ); + pCompletedPolyLine->PointLineLinks[1].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[1]; + pCompletedPolyLine->PointLineLinks[1].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[1]; + + GeneratePolyhedronFromPlanes_LineLL *pLivingPolySide = pCompletedPolyLine->PointLineLinks[1].pNext; + pLivingPolySide = &pLivingPolySide->pLine->PolygonLineLinks[1 - pLivingPolySide->iReferenceIndex]; //convert the link from a point space to polygon space + + //link outwardly into the patched up polygon + pCompletedPolyLine->pPolygons[0] = pLivingPolySide->pLine->pPolygons[pLivingPolySide->iReferenceIndex]; + pCompletedPolyLine->PolygonLineLinks[0].pPrev = &pLivingPolySide->pLine->PolygonLineLinks[pLivingPolySide->iReferenceIndex]; + pCompletedPolyLine->PolygonLineLinks[0].pNext = pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext; + pCompletedPolyLine->PolygonLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PolygonLineLinks[0]; + pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PolygonLineLinks[0]; + Assert_DumpPolyhedron( pCompletedPolyLine->pPolygons[0] != pCompletedPolyLine->pPolygons[1] ); + +#if defined( DBGFLAG_ASSERT ) + iPatchMethod = 2; { - ++iSurroundedCount; + fltx4 vLineTemp = SubSIMD( pCompletedPolyLine->pPoints[1]->ptPosition, pCompletedPolyLine->pPoints[0]->ptPosition ); + AssertMsg( (SubFloat( vLineTemp, 0 ) != 0.0f) || (SubFloat( vLineTemp, 1 ) != 0.0f) || (SubFloat( vLineTemp, 2 ) != 0.0f), "Created zero length line" ); } +#endif } + } + else + { + //first and last lines were on-plane + control.pNewPolygon->pLines = control.pActivePolyLine->pNext; + control.pActivePolyLine->pNext->pPrev = control.pActivePolyLine->pPrev; + control.pActivePolyLine->pPrev->pNext = control.pActivePolyLine->pNext; + DestructLine( control.pActivePolyLine->pLine, destructors ); - pDebugLineWalk = pDebugLineWalk->pNext; - } while( pDebugLineWalk != pFirstDebugLine ); +#if defined( DBGFLAG_ASSERT ) + iPatchMethod = 3; +#endif + } + } - AssertMsg_DumpPolyhedron( iSurroundedCount <= 1, "Concave polygon, cutting process might break. Consider adjusting the on-plane epsilon to better compensate for floating point precision." ); - pDebugPolygonWalk = pDebugPolygonWalk->pNext; - } while( pDebugPolygonWalk ); +#if defined( DBGFLAG_ASSERT ) + { + GeneratePolyhedronFromPlanes_UnorderedLineLL *pLineWalk = pAllLines; + do + { + Assert( pLineWalk->line.pPoints[0] != NULL && pLineWalk->line.pPoints[1] != NULL ); + pLineWalk = pLineWalk->pNext; + } while (pLineWalk); + } +#endif } + + +#ifdef DBGFLAG_ASSERT + destructors.PlaneCutHistory.AddToTail( (float *)&pOutwardFacingPlanes[iCurrentPlane] ); #endif - //=================================================================================================== - // Step 2: Remove dead lines. A dead line is one with a dead point that isn't connected to a living point - //=================================================================================================== + //remove dead lines { GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalk = pAllLines; do { - GeneratePolyhedronFromPlanes_Line *pLine = pActiveLineWalk->pLine; - if( (pLine->bAlive == false) && (pLine->bCut == true) ) //not connected to a live point, but connected to a dead one. Dead line + if( pActiveLineWalk->line.planarity == LINE_DEAD ) { - //remove line from connected polygons - for( int i = 0; i != 2; ++i ) - { - GeneratePolyhedronFromPlanes_Polygon *pPolygon = pLine->pPolygons[i]; - GeneratePolyhedronFromPlanes_LineLL *pLineLink = pLine->pPolygonLineLinks[i]; - - pPolygon->bMissingASide = true; - - if( pLineLink->pNext == pLineLink ) - { - //this was the last line of the polygon, it's dead - pPolygon->pLines = NULL; - } - else - { - //link around this line - pPolygon->pLines = pLineLink->pPrev; //Always have the polygon's head line be just before the gap in the polygon - pLineLink->pNext->pPrev = pLineLink->pPrev; - pLineLink->pPrev->pNext = pLineLink->pNext; - } - - //move the line link to the dead list - pLineLink->pNext = pDeadLineLinkCollection; - pDeadLineLinkCollection = pLineLink; - } - - //remove the line from connected points - for( int i = 0; i != 2; ++i ) - { - GeneratePolyhedronFromPlanes_Point *pPoint = pLine->pPoints[i]; - GeneratePolyhedronFromPlanes_LineLL *pLineLink = pLine->pPointLineLinks[i]; - - if( pLineLink->pNext == pLineLink ) - { - //this is the last line - pPoint->pConnectedLines = NULL; - Assert( pPoint->planarity != POINT_ALIVE ); - pPoint->planarity = POINT_DEAD; //in case it was merely POINT_ONPLANE before - } - else - { - //link around this line - pPoint->pConnectedLines = pLineLink->pNext; //in case pLineLink was the head line - pLineLink->pNext->pPrev = pLineLink->pPrev; - pLineLink->pPrev->pNext = pLineLink->pNext; - } - - //move the line link to the dead list - pLineLink->pNext = pDeadLineLinkCollection; - pDeadLineLinkCollection = pLineLink; - } + UnlinkLine( pActiveLineWalk ); //move the line to the dead list - { - //link past this node - if( pActiveLineWalk->pPrev ) - pActiveLineWalk->pPrev->pNext = pActiveLineWalk->pNext; - else - pAllLines = pActiveLineWalk->pNext; - - if( pActiveLineWalk->pNext ) - pActiveLineWalk->pNext->pPrev = pActiveLineWalk->pPrev; - - GeneratePolyhedronFromPlanes_UnorderedLineLL *pNextLineWalk = pActiveLineWalk->pNext; - - //add to the dead list - pActiveLineWalk->pNext = pDeadLineCollection; - pDeadLineCollection = pActiveLineWalk; - - //next - pActiveLineWalk = pNextLineWalk; - } + pActiveLineWalk = DestructLine( pActiveLineWalk, destructors ); } else { @@ -1200,91 +2457,30 @@ CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL } while( pActiveLineWalk ); } - - //=================================================================================================== - // Step 3: Remove dead polygons. A dead polygon has less than 2 lines. - //=================================================================================================== + //remove dead polygons { GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons; do { - GeneratePolyhedronFromPlanes_Polygon *pPolygon = pActivePolygonWalk->pPolygon; - GeneratePolyhedronFromPlanes_LineLL *pHeadLine = pPolygon->pLines; - - bool bDead = (pHeadLine == NULL) || (pHeadLine->pNext == pHeadLine); - if( !bDead ) + if( pActivePolygonWalk->polygon.bDead ) { - //there's a rare case where a polygon can be almost entirely coplanar with the cut, it comes purely out of the land of imprecision - bDead = true; //assume it's dead, and disprove - - GeneratePolyhedronFromPlanes_LineLL *pTestLineWalk = pHeadLine; - do - { - if( pTestLineWalk->pLine->bAlive ) - { - bDead = false; - break; - } - - pTestLineWalk = pTestLineWalk->pNext; - } while( pTestLineWalk != pHeadLine ); - } - - if( bDead ) - { - //dead polygon, move it to the dead list - - //link around this node - if( pActivePolygonWalk->pPrev ) - pActivePolygonWalk->pPrev->pNext = pActivePolygonWalk->pNext; - else - pAllPolygons = pAllPolygons->pNext; //pActivePolygonWalk was the head node - - if( pActivePolygonWalk->pNext ) - pActivePolygonWalk->pNext->pPrev = pActivePolygonWalk->pPrev; - - GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pNextPolygonWalk = pActivePolygonWalk->pNext; - - //add to the dead list - pActivePolygonWalk->pNext = pDeadPolygonCollection; - pDeadPolygonCollection = pActivePolygonWalk; - - //next - pActivePolygonWalk = pNextPolygonWalk; + pActivePolygonWalk = DestructPolygon( pActivePolygonWalk, destructors ); } else { - AssertMsg_DumpPolyhedron( (pActivePolygonWalk->pPolygon->pLines != NULL) && - (pActivePolygonWalk->pPolygon->pLines != pActivePolygonWalk->pPolygon->pLines->pNext), "Living polygon with less than 2 lines" ); - pActivePolygonWalk = pActivePolygonWalk->pNext; } } while( pActivePolygonWalk ); } - //=================================================================================================== - // Step 4: Remove dead points. - //=================================================================================================== + //remove dead points { GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints; do { - if( pActivePointWalk->pPoint->planarity == POINT_DEAD ) + if( pActivePointWalk->point.planarity == POINT_DEAD ) { - GeneratePolyhedronFromPlanes_UnorderedPointLL *pNext = pActivePointWalk->pNext; - - if( pActivePointWalk->pPrev ) - pActivePointWalk->pPrev->pNext = pActivePointWalk->pNext; - else - pAllPoints = pAllPoints->pNext; - - if( pActivePointWalk->pNext ) - pActivePointWalk->pNext->pPrev = pActivePointWalk->pPrev; - - pActivePointWalk->pNext = pDeadPointCollection; - pDeadPointCollection = pActivePointWalk; - - pActivePointWalk = pNext; + pActivePointWalk = DestructPoint( pActivePointWalk, destructors ); } else { @@ -1293,490 +2489,576 @@ CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL } while( pActivePointWalk ); } - - //=================================================================================================== - // Step 5: Handle cut lines - //=================================================================================================== +#if defined( DEBUG_POLYHEDRON_CONVERSION ) + if( g_pPolyhedronCarvingDebugStepCallback != NULL ) { + CPolyhedron *pTestPolyhedron = ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions ); + Assert_DumpPolyhedron( pTestPolyhedron ); + + if( !g_pPolyhedronCarvingDebugStepCallback( pTestPolyhedron ) ) + { + VMatrix matScaleCentered; + matScaleCentered.Identity(); + matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f; + matScaleCentered.SetTranslation( -pTestPolyhedron->Center() * 10.0f ); + + DumpPolyhedronToGLView( pTestPolyhedron, "AssertPolyhedron.txt", &matScaleCentered, "wb" ); + AssertMsg_DumpPolyhedron( false, "Outside conversion failed" ); + } + + pTestPolyhedron->Release(); + } +#endif + + //When clipping a 2D polyhedron, the result of any clipping is a faux 3rd polygon which is degenerate (2 lines, both connected to the same set of points). Remove it now + if( pNewPolygon->pLines->pNext == pNewPolygon->pLines->pPrev ) + { + AssertMsg_DumpPolyhedron( (pAllPolygons->pNext != NULL) && //more than 1 + (pAllPolygons->pNext->pNext != NULL) && //more than 2 + (pAllPolygons->pNext->pNext->pNext == NULL), //exactly 3 polygons, and no more + "This case should only pop up if the input to the last cutting pass was a 2 sided polyhedron" ); + + RemoveDegeneratePolygon( pNewPolygon, destructors ); + pNewPolygon = NULL; + } + + //remove super-short lines + { + //ideally this behavior should be integrated with the Recursive_MarkPlanarity_Dead() behavior to avoid creating them in the first place. + //But choosing safety over performance for now since Recursive_MarkPlanarity_Dead() is stable right now GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalk = pAllLines; do { - GeneratePolyhedronFromPlanes_Line *pWorkLine = pActiveLineWalk->pLine; - Assert_DumpPolyhedron( (pWorkLine->bAlive == true) || (pWorkLine->bCut == false) ); //all dead lines should have already been removed - - if( pWorkLine->bCut ) + if( pActiveLineWalk->line.bNewLengthThisPass ) { - GeneratePolyhedronFromPlanes_Point **pLinePoints = pWorkLine->pPoints; - - Assert_DumpPolyhedron( (pLinePoints[0]->planarity == POINT_DEAD) || (pLinePoints[1]->planarity == POINT_DEAD) ); //one of the two has to be a dead point - - int iDeadIndex = (pLinePoints[0]->planarity == POINT_DEAD)?(0):(1); - int iLivingIndex = 1 - iDeadIndex; - GeneratePolyhedronFromPlanes_Point *pDeadPoint = pLinePoints[iDeadIndex]; - GeneratePolyhedronFromPlanes_Point *pLivingPoint = pLinePoints[iLivingIndex]; - - Assert_DumpPolyhedron( pLivingPoint->planarity == POINT_ALIVE ); //if this point were on-plane or dead, the line should be dead - - //We'll be de-linking from the old point and generating a new one. We do this so other lines can still access the dead point's untouched data. - - //Generate a new point - GeneratePolyhedronFromPlanes_Point *pNewPoint = (GeneratePolyhedronFromPlanes_Point *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_Point ) ); + fltx4 vDiff = SubSIMD( pActiveLineWalk->line.pPoints[0]->ptPosition, pActiveLineWalk->line.pPoints[1]->ptPosition ); + vDiff = MulSIMD( vDiff, vDiff ); + float fLengthSqr = SubFloat( vDiff, 0 ) + SubFloat( vDiff, 1 ) + SubFloat( vDiff, 2 ); + if( fLengthSqr <= fOnPlaneEpsilonSquared ) //eq for 0.0 epsilon case { - //add this point to the active list - pAllPoints->pPrev = (GeneratePolyhedronFromPlanes_UnorderedPointLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_UnorderedPointLL ) ); - pAllPoints->pPrev->pNext = pAllPoints; - pAllPoints = pAllPoints->pPrev; - pAllPoints->pPrev = NULL; - pAllPoints->pPoint = pNewPoint; + //this line needs to go. But removing it might have repercussions + GeneratePolyhedronFromPlanes_Polygon *pPolygons[2] = { pActiveLineWalk->line.pPolygons[0], pActiveLineWalk->line.pPolygons[1] }; + + GeneratePolyhedronFromPlanes_Line *pShortLine = &pActiveLineWalk->line; + GeneratePolyhedronFromPlanes_Point *pDeadPoint = pShortLine->pPoints[1]; + GeneratePolyhedronFromPlanes_Point *pKeepPoint = pShortLine->pPoints[0]; - - float fInvTotalDist = 1.0f/(pDeadPoint->fPlaneDist - pLivingPoint->fPlaneDist); //subtraction because the living index is known to be negative - pNewPoint->ptPosition = (pLivingPoint->ptPosition * (pDeadPoint->fPlaneDist * fInvTotalDist)) - (pDeadPoint->ptPosition * (pLivingPoint->fPlaneDist * fInvTotalDist)); - -#if ( 0 && defined( _DEBUG ) ) - float fDebugDist = vNormal.Dot( pNewPoint->ptPosition ) - fPlaneDist; //just for looking at in watch windows - AssertMsg_DumpPolyhedron( fabs( fDebugDist ) < fOnPlaneEpsilon, "Generated split point is far from plane" ); - - //verify that the new point isn't sitting on top of another + //reposition point 0 to be the midpoint? In my experience it's best to just leave it alone when dealing with small distances. + + //relink point 1 links to point 0 { - GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints; - do + //update point pointers { - if( pActivePointWalk->pPoint != pNewPoint ) + GeneratePolyhedronFromPlanes_LineLL *pPointLineWalkHead = pDeadPoint->pConnectedLines; + GeneratePolyhedronFromPlanes_LineLL *pPointLineWalk = pPointLineWalkHead; + do { - Vector vDiff = pActivePointWalk->pPoint->ptPosition - pNewPoint->ptPosition; + pPointLineWalk->pLine->pPoints[1- pPointLineWalk->iReferenceIndex] = pKeepPoint; + pPointLineWalk->pLine->pPolygons[pPointLineWalk->iReferenceIndex]->bMovedExistingPoints = true;//also inform touching polygons that we're changing their geometry + pPointLineWalk = pPointLineWalk->pNext; + } while( pPointLineWalk != pPointLineWalkHead ); - AssertMsg_DumpPolyhedron( vDiff.Length() > fOnPlaneEpsilon, "Generated a point on top of another" ); - } - pActivePointWalk = pActivePointWalk->pNext; - } while( pActivePointWalk ); + //except the line we're killing + pShortLine->pPoints[1] = pDeadPoint; + } + + //insert all lines from point 1 as a fan between point 0 prev and the short line. + pShortLine->PointLineLinks[1].pNext->pPrev = pShortLine->PointLineLinks[0].pPrev; + pShortLine->PointLineLinks[0].pPrev->pNext = pShortLine->PointLineLinks[1].pNext; + + pShortLine->PointLineLinks[1].pPrev->pNext = &pShortLine->PointLineLinks[0]; + pShortLine->PointLineLinks[0].pPrev = pShortLine->PointLineLinks[1].pPrev; + + pShortLine->PointLineLinks[1].pNext = &pShortLine->PointLineLinks[1]; + pShortLine->PointLineLinks[1].pPrev = &pShortLine->PointLineLinks[1]; } + +#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + pDeadPoint->planarity = POINT_DEAD; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it. #endif - pNewPoint->planarity = POINT_ONPLANE; - pNewPoint->fPlaneDist = 0.0f; + + UnlinkLine( pShortLine ); + pActiveLineWalk = DestructLine( pShortLine, destructors ); + DestructPoint( pDeadPoint, destructors ); + + for( int i = 0; i != 2; ++i ) + { + if( pPolygons[i]->pLines->pNext == pPolygons[i]->pLines->pPrev ) + { + //this polygon is dead + if( pAllPolygons->pNext->pNext == NULL ) + { + //It's conceivably possible to either start or collapse down to a 2D polyhedron with every line cut save one. + //We can't have 1 polygon, and I don't want to mentally run down what would happen below if we tried to collapse down to 1. + //So just bail now +#if defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + Assert_DumpPolyhedron( g_bDumpNullPolyhedrons == false ); //if someone set it to true, we'll dump the polyhedron then halt +#endif + return NULL; + } + + if( pPolygons[i] == pNewPolygon ) + { + pNewPolygon = NULL; + } + + if( pActiveLineWalk ) + { + GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalkNext = pActiveLineWalk->pNext; + GeneratePolyhedronFromPlanes_Line *pTestDelete = &pActiveLineWalk->line; + + if( pTestDelete == RemoveDegeneratePolygon( pPolygons[i], destructors ) ) + { + pActiveLineWalk = pActiveLineWalkNext; + } + } + else + { + RemoveDegeneratePolygon( pPolygons[i], destructors ); + } + } + } + + continue; //pActiveLineWalk already updated } - - GeneratePolyhedronFromPlanes_LineLL *pNewLineLink = pNewPoint->pConnectedLines = (GeneratePolyhedronFromPlanes_LineLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_LineLL ) ); - pNewLineLink->pLine = pWorkLine; - pNewLineLink->pNext = pNewLineLink; - pNewLineLink->pPrev = pNewLineLink; - pNewLineLink->iReferenceIndex = iLivingIndex; - - pWorkLine->pPoints[iDeadIndex] = pNewPoint; - pWorkLine->pPointLineLinks[iDeadIndex] = pNewLineLink; - pNewPoint->pConnectedLines = pNewLineLink; - - //A new line is needed on each polygon touching the dead point to connect the two new endpoints for split lines. - // So mark connected polygons as missing a side. - for( int i = 0; i != 2; ++i ) - pWorkLine->pPolygons[i]->bMissingASide = true; - - - //Always have a cut polygon's head line be just before the gap in the polygon. - // In this case, we know that one of the two polygons goes clockwise into the dead point, so have that polygon point at this line. - // We don't know enough about the other polygon to do anything here, but another cut line will handle that polygon. So it all works out in the end. - pWorkLine->pPolygons[iDeadIndex]->pLines = pWorkLine->pPolygonLineLinks[iDeadIndex]; } - + pActiveLineWalk = pActiveLineWalk->pNext; } while( pActiveLineWalk ); } - - //=================================================================================================== - // Step 6: Repair polygons that are missing a side. And generate the new coplanar polygon. - //=================================================================================================== +#if defined( DEBUG_POLYHEDRON_CONVERSION ) + if( g_pPolyhedronCarvingDebugStepCallback != NULL ) { - //Find the first polygon missing a side. - // We'll then walk from polygon to polygon using line connections so that we can generate the new polygon in a clockwise manner. - GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons; + CPolyhedron *pTestPolyhedron = ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions ); + Assert_DumpPolyhedron( pTestPolyhedron ); - while( (pActivePolygonWalk != NULL) && (pActivePolygonWalk->pPolygon->bMissingASide == false) ) + if( !g_pPolyhedronCarvingDebugStepCallback( pTestPolyhedron ) ) { - pActivePolygonWalk = pActivePolygonWalk->pNext; + VMatrix matScaleCentered; + matScaleCentered.Identity(); + matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f; + matScaleCentered.SetTranslation( -pTestPolyhedron->Center() * 10.0f ); + + DumpPolyhedronToGLView( pTestPolyhedron, "AssertPolyhedron.txt", &matScaleCentered, "wb" ); + AssertMsg_DumpPolyhedron( false, "Outside conversion failed" ); } - //acquire iteration data -#ifndef _DEBUG - GeneratePolyhedronFromPlanes_Point *pStartPoint; - GeneratePolyhedronFromPlanes_Point *pWorkPoint; + pTestPolyhedron->Release(); + } #endif - GeneratePolyhedronFromPlanes_LineLL *pLastLineLink; - GeneratePolyhedronFromPlanes_Polygon *pWorkPolygon; - GeneratePolyhedronFromPlanes_LineLL *pTestLine; + AssertMsg( pAllPolygons->pNext != NULL, "A polyhedron must have at least 2 sides to be a 2D Polyhedron, and at least 4 to be a 3D polyhedron" ); -#ifdef _DEBUG - GeneratePolyhedronFromPlanes_Polygon *pLastWorkPolygon = NULL; - GeneratePolyhedronFromPlanes_Point *pLastWorkPoint = NULL; -#endif - - if( pActivePolygonWalk ) + //if any polygons had their geometry adjusted (or new poly uses existing on-plane points), we need to recompute the surface normal and check for degenerate points + { + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons; + if( pAllPolygons->pNext->pNext != NULL ) { - //grab the polygon we'll be starting with - GeneratePolyhedronFromPlanes_Polygon *pBrokenPolygon = pActivePolygonWalk->pPolygon; - + //for 3D polyhedrons, remove degenerate points and recompute normals + do { - GeneratePolyhedronFromPlanes_LineLL *pTemp = pBrokenPolygon->pLines->pNext; - pStartPoint = pTemp->pLine->pPoints[1 - pTemp->iReferenceIndex]; - Assert_DumpPolyhedron( pStartPoint->planarity == POINT_ONPLANE ); //every working point should be coplanar - pLastLineLink = pTemp->pLine->pPointLineLinks[1 - pTemp->iReferenceIndex]->pNext; - pWorkPolygon = pBrokenPolygon; - } - - pWorkPoint = pStartPoint; - pTestLine = pLastLineLink->pPrev; //rotate counterclockwise around the point + if( pActivePolygonWalk->polygon.bMovedExistingPoints || pActivePolygonWalk->polygon.bHasNewPoints ) + { + RemoveDegeneratePoints( &pActivePolygonWalk->polygon, destructors ); + RecomputePolygonSurfaceNormal( &pActivePolygonWalk->polygon ); + } + pActivePolygonWalk = pActivePolygonWalk->pNext; + } while( pActivePolygonWalk ); } else { - //apparently the plane was entirely through existing polygonal borders, extremely rare but it can happen with inefficient cutting planes - GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints; - while( (pActivePointWalk != NULL) && (pActivePointWalk->pPoint->planarity != POINT_ONPLANE) ) + //for 2D polyhedrons, only recompute normals. RemoveDegeneratePoints() is not safe for 2D polyhedrons as it assumes the shape is 3 dimensional to avoid angle computations and checks + do { - pActivePointWalk = pActivePointWalk->pNext; - } - - Assert( pActivePointWalk != NULL ); - - pStartPoint = pWorkPoint = pActivePointWalk->pPoint; - GeneratePolyhedronFromPlanes_LineLL *pLines = pWorkPoint->pConnectedLines; - - while( !pLines->pLine->bAlive ) //seek clockwise until we find a line not on the plane - pLines = pLines->pNext; - - while( pLines->pLine->bAlive ) //now seek counterclockwise until we find a line on the plane (in case we started on an alive line last seek) - pLines = pLines->pPrev; - - //now pLines points at one side of the polygon, with pActivePointWalk - pLastLineLink = pLines; - pTestLine = pLines->pPrev; - pWorkPolygon = pTestLine->pLine->pPolygons[1 - pTestLine->iReferenceIndex]; - + if( pActivePolygonWalk->polygon.bMovedExistingPoints || pActivePolygonWalk->polygon.bHasNewPoints ) + { + RecomputePolygonSurfaceNormal( &pActivePolygonWalk->polygon ); + } + pActivePolygonWalk = pActivePolygonWalk->pNext; + } while( pActivePolygonWalk ); } - - //create the new polygon - GeneratePolyhedronFromPlanes_Polygon *pNewPolygon = (GeneratePolyhedronFromPlanes_Polygon *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_Polygon ) ); - { - //before we forget, add this polygon to the active list - pAllPolygons->pPrev = (GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_UnorderedPolygonLL ) ); - pAllPolygons->pPrev->pNext = pAllPolygons; - pAllPolygons = pAllPolygons->pPrev; - pAllPolygons->pPrev = NULL; - pAllPolygons->pPolygon = pNewPolygon; - - pNewPolygon->bMissingASide = false; //technically missing all it's sides, but we're fixing it now - pNewPolygon->vSurfaceNormal = vNormal; - pNewPolygon->pLines = NULL; - } - - - - //=================================================================================================================== - // The general idea of the upcoming algorithm to put together a new polygon and patch broken polygons... - // You have a point and a line the algorithm just jumped across. - // 1. Rotate through the point's line links one time counterclockwise (pPrev) - // 2. If the line is cut, then we make a new bridging line in the polygon between that line and the one counterclockwise to it. (pPrev) - // If the line is on-plane. Skip the bridge line making, but set links to the new polygon as if we'd just created the bridge - // 3. Once we follow a line back to the point where we started, we should be all done. - - do - { - if( pWorkPolygon->bMissingASide ) - { - //during the cutting process we made sure that the head line link was going clockwise into the missing area - GeneratePolyhedronFromPlanes_LineLL *pGapLines[2]; - pGapLines[1] = pTestLine->pLine->pPolygonLineLinks[pTestLine->iReferenceIndex]; //get the same line, but in the polygons linked list. - Assert_DumpPolyhedron( pGapLines[1]->pLine == pTestLine->pLine ); - pGapLines[0] = pGapLines[1]->pPrev; - - Assert_DumpPolyhedron( pWorkPolygon->bMissingASide ); - -#ifdef _DEBUG - { - //ensure that the space between the gap lines is the only space where fixing is required - GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalk = pGapLines[1]->pNext; - - while( pDebugLineWalk != pGapLines[0] ) - { - Assert_DumpPolyhedron( pDebugLineWalk->pLine->bCut == false ); - pDebugLineWalk = pDebugLineWalk->pNext; - } - } -#endif - - GeneratePolyhedronFromPlanes_Line *pJoinLine = (GeneratePolyhedronFromPlanes_Line *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_Line ) ); - { - //before we forget, add this line to the active list - pAllLines->pPrev = (GeneratePolyhedronFromPlanes_UnorderedLineLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_UnorderedLineLL ) ); - pAllLines->pPrev->pNext = pAllLines; - pAllLines = pAllLines->pPrev; - pAllLines->pPrev = NULL; - pAllLines->pLine = pJoinLine; - - pJoinLine->bAlive = false; - pJoinLine->bCut = false; - } - - - pJoinLine->pPoints[0] = pGapLines[0]->pLine->pPoints[pGapLines[0]->iReferenceIndex]; - pJoinLine->pPoints[1] = pGapLines[1]->pLine->pPoints[1 - pGapLines[1]->iReferenceIndex]; - - pJoinLine->pPolygons[0] = pNewPolygon; - pJoinLine->pPolygons[1] = pWorkPolygon; - - //now create all 4 links into the line - GeneratePolyhedronFromPlanes_LineLL *pPointLinks[2]; - pPointLinks[0] = (GeneratePolyhedronFromPlanes_LineLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_LineLL ) ); - pPointLinks[1] = (GeneratePolyhedronFromPlanes_LineLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_LineLL ) ); - - GeneratePolyhedronFromPlanes_LineLL *pPolygonLinks[2]; - pPolygonLinks[0] = (GeneratePolyhedronFromPlanes_LineLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_LineLL ) ); - pPolygonLinks[1] = (GeneratePolyhedronFromPlanes_LineLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_LineLL ) ); - - pPointLinks[0]->pLine = pPointLinks[1]->pLine = pPolygonLinks[0]->pLine = pPolygonLinks[1]->pLine = pJoinLine; - - pJoinLine->pPointLineLinks[0] = pPointLinks[0]; - pJoinLine->pPointLineLinks[1] = pPointLinks[1]; - pJoinLine->pPolygonLineLinks[0] = pPolygonLinks[0]; - pJoinLine->pPolygonLineLinks[1] = pPolygonLinks[1]; - - - - pPointLinks[0]->iReferenceIndex = 1; - pPointLinks[1]->iReferenceIndex = 0; - - //Insert before the link from point 0 to gap line 0 (counterclockwise rotation) - { - GeneratePolyhedronFromPlanes_LineLL *pWorkLink = pGapLines[0]->pLine->pPointLineLinks[pGapLines[0]->iReferenceIndex]; - Assert_DumpPolyhedron( pWorkLink->pLine == pGapLines[0]->pLine ); - - pPointLinks[0]->pPrev = pWorkLink->pPrev; - pPointLinks[0]->pNext = pWorkLink; - - pWorkLink->pPrev->pNext = pPointLinks[0]; - pWorkLink->pPrev = pPointLinks[0]; - } - - //Insert after the link from point 1 to gap line 1 (clockwise rotation) - { - GeneratePolyhedronFromPlanes_LineLL *pWorkLink = pGapLines[1]->pLine->pPointLineLinks[1 - pGapLines[1]->iReferenceIndex]; - Assert_DumpPolyhedron( pWorkLink->pLine == pGapLines[1]->pLine ); - - pPointLinks[1]->pNext = pWorkLink->pNext; - pPointLinks[1]->pPrev = pWorkLink; - - pWorkLink->pNext->pPrev = pPointLinks[1]; - pWorkLink->pNext = pPointLinks[1]; - } - - - - - pPolygonLinks[0]->iReferenceIndex = 0; - pPolygonLinks[1]->iReferenceIndex = 1; - - //Insert before the head line in the new polygon (at the end of the clockwise order) - { - if( pNewPolygon->pLines == NULL ) - { - //this is the first line being added to the polygon - pNewPolygon->pLines = pPolygonLinks[0]; - pPolygonLinks[0]->pNext = pPolygonLinks[0]; - pPolygonLinks[0]->pPrev = pPolygonLinks[0]; - } - else - { - GeneratePolyhedronFromPlanes_LineLL *pWorkLink = pNewPolygon->pLines; - - pPolygonLinks[0]->pNext = pWorkLink; - pPolygonLinks[0]->pPrev = pWorkLink->pPrev; - - pWorkLink->pPrev->pNext = pPolygonLinks[0]; - pWorkLink->pPrev = pPolygonLinks[0]; - } - } - - //Insert after the head line in the work polygon - { - GeneratePolyhedronFromPlanes_LineLL *pWorkLink = pWorkPolygon->pLines; - - pPolygonLinks[1]->pNext = pWorkLink->pNext; - pPolygonLinks[1]->pPrev = pWorkLink; - - pWorkLink->pNext->pPrev = pPolygonLinks[1]; - pWorkLink->pNext = pPolygonLinks[1]; - } - - pWorkPolygon->bMissingASide = false; //repairs are finished - -#ifdef _DEBUG - pLastWorkPolygon = pWorkPolygon; - pLastWorkPoint = pWorkPoint; -#endif - //move to the next point - pWorkPoint = pJoinLine->pPoints[0]; - pLastLineLink = pJoinLine->pPointLineLinks[0]; - Assert_DumpPolyhedron( pWorkPoint->planarity == POINT_ONPLANE ); //every working point should be coplanar - - pTestLine = pLastLineLink->pPrev; - if( pTestLine->pLine->pPoints[pTestLine->iReferenceIndex]->planarity == POINT_ALIVE ) - pWorkPolygon = pTestLine->pLine->pPolygons[pTestLine->iReferenceIndex]; - else - pWorkPolygon = pTestLine->pLine->pPolygons[1 - pTestLine->iReferenceIndex]; - - Assert_DumpPolyhedron( pWorkPolygon != pLastWorkPolygon ); - Assert_DumpPolyhedron( (pWorkPoint == pStartPoint) || - (pGapLines[0]->pLine->bCut == false) || - (pWorkPolygon->bMissingASide == true) ); //if we're not done fixing, and if the shared line was cut, the next polygon must be missing a side - } - else - { - //line is on the plane, meaning the polygon isn't broken and doesn't need patching - Assert_DumpPolyhedron( pTestLine->pLine->bCut == false ); - Assert_DumpPolyhedron( (pTestLine->pLine->pPoints[0]->planarity == POINT_ONPLANE) && (pTestLine->pLine->pPoints[1]->planarity == POINT_ONPLANE) ); - - - //link to this line from the new polygon - GeneratePolyhedronFromPlanes_LineLL *pNewLineLink; - pNewLineLink = (GeneratePolyhedronFromPlanes_LineLL *)stackalloc( sizeof( GeneratePolyhedronFromPlanes_LineLL ) ); - - pNewLineLink->pLine = pTestLine->pLine; - pNewLineLink->iReferenceIndex = pTestLine->iReferenceIndex; - - //Insert before the head line in the new polygon (at the end of the clockwise order) - { - if( pNewPolygon->pLines == NULL ) - { - //this is the first line being added to the polygon - pNewPolygon->pLines = pNewLineLink; - pNewLineLink->pNext = pNewLineLink; - pNewLineLink->pPrev = pNewLineLink; - } - else - { - GeneratePolyhedronFromPlanes_LineLL *pWorkLink = pNewPolygon->pLines; - - pNewLineLink->pNext = pWorkLink; - pNewLineLink->pPrev = pWorkLink->pPrev; - - pWorkLink->pPrev->pNext = pNewLineLink; - pWorkLink->pPrev = pNewLineLink; - } - } - - //Since the entire line is on the plane, that means it used to point to something that used to reside where the new polygon is going - // Update the link to the new the polygon pointer and be on our way - pTestLine->pLine->pPolygons[pTestLine->iReferenceIndex] = pNewPolygon; - pTestLine->pLine->pPolygonLineLinks[pTestLine->iReferenceIndex] = pNewLineLink; - -#ifdef _DEBUG - pLastWorkPolygon = pWorkPolygon; - pLastWorkPoint = pWorkPoint; -#endif - - pWorkPoint = pTestLine->pLine->pPoints[pTestLine->iReferenceIndex]; - pLastLineLink = pTestLine->pLine->pPointLineLinks[pTestLine->iReferenceIndex]; - Assert_DumpPolyhedron( pWorkPoint->planarity == POINT_ONPLANE ); //every working point should be coplanar - - pTestLine = pLastLineLink->pPrev; - if( pTestLine->pLine->pPoints[pTestLine->iReferenceIndex]->planarity == POINT_ALIVE ) - pWorkPolygon = pTestLine->pLine->pPolygons[pTestLine->iReferenceIndex]; - else - pWorkPolygon = pTestLine->pLine->pPolygons[1 - pTestLine->iReferenceIndex]; - - Assert_DumpPolyhedron( pWorkPolygon != pLastWorkPolygon ); - } - } while( pWorkPoint != pStartPoint ); } -#ifdef _DEBUG +#if defined( DEBUG_POLYHEDRON_CONVERSION ) + if( g_pPolyhedronCarvingDebugStepCallback != NULL ) + { + CPolyhedron *pTestPolyhedron = ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions ); + Assert_DumpPolyhedron( pTestPolyhedron ); + + if( !g_pPolyhedronCarvingDebugStepCallback( pTestPolyhedron ) ) + { + VMatrix matScaleCentered; + matScaleCentered.Identity(); + matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f; + matScaleCentered.SetTranslation( -pTestPolyhedron->Center() * 10.0f ); + + DumpPolyhedronToGLView( pTestPolyhedron, "AssertPolyhedron.txt", &matScaleCentered, "wb" ); + AssertMsg_DumpPolyhedron( false, "Outside conversion failed" ); + } + + pTestPolyhedron->Release(); + } +#endif + + //remove points which, although seemingly useful. Actually create concave shapes + { + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons; + do + { + if( pActivePolygonWalk->polygon.bHasNewPoints ) + { + Vector vTestNormal = pActivePolygonWalk->polygon.vSurfaceNormal; + GeneratePolyhedronFromPlanes_LineLL *pLineWalkHead = pActivePolygonWalk->polygon.pLines; + GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pLineWalkHead->pPrev; + Vector vLastLine; + + fltx4 vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition ); + pLineWalk = pLineWalk->pNext; + vLastLine.Init( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) ); + do + { + vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition ); + Vector vThisLine( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) ); + Vector vCross = vThisLine.Cross( vLastLine ); + if( vCross.Dot( vTestNormal ) <= 0.0f ) + { + //this point is a troublemaker + GeneratePolyhedronFromPlanes_Point *pProblemPoint = pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]; + + if( pLineWalk->pPrev == pLineWalkHead ) + { + //about to delete our start/stop point. Avoid infinite loops + pLineWalkHead = pLineWalk; + } + +#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + pProblemPoint->planarity = POINT_DEAD; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it. +#endif + +#if defined( DBGFLAG_ASSERT ) + { + GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalkHead = pProblemPoint->pConnectedLines; + GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalk = pDebugLineWalkHead; + do + { + Assert( (pDebugLineWalk->pLine->pPolygons[0] == &pActivePolygonWalk->polygon) || + (pDebugLineWalk->pLine->pPolygons[1] == &pActivePolygonWalk->polygon) || + (pDebugLineWalk->pLine->pPolygons[0]->vSurfaceNormal.Dot( pDebugLineWalk->pLine->pPolygons[1]->vSurfaceNormal ) > 0.95f) ); + + pDebugLineWalk = pDebugLineWalk->pNext; + } while( pDebugLineWalk != pDebugLineWalkHead ); + } +#endif + + //what we're going to do is eliminate the problem point and every line connected to it save one. + //That one saved line will take the place of both lines on the new poly edge connected to this point + //As part of eliminating all the lines connected to this point, we need to merge polygons separated by those lines. + //To do that we'll just take the first polygon we encounter and stitch the other polygon lines into it. + GeneratePolyhedronFromPlanes_LineLL *pPatchStop = &pLineWalk->pLine->PolygonLineLinks[1 - pLineWalk->iReferenceIndex]; //where the polygon patching algorithm should stop + GeneratePolyhedronFromPlanes_LineLL *pPatchWalk = pLineWalk->pPrev; //rewind to prev line + pPatchWalk = &pPatchWalk->pLine->PolygonLineLinks[1 - pPatchWalk->iReferenceIndex]; //convert to the non-new polygon walk space, pPatchWalk is the on-polygon line we'll be killing + GeneratePolyhedronFromPlanes_Polygon *pPatchPolygon = pPatchWalk->pLine->pPolygons[pPatchWalk->iReferenceIndex]; + + //pPatchStop doubles as the surviving on-polygon line. Go ahead and do all the fix ups necessary to allow us to clean up the dead lines as we come across them + pPatchStop->pLine->pPoints[pPatchStop->iReferenceIndex] = pPatchWalk->pLine->pPoints[pPatchWalk->iReferenceIndex]; + { + GeneratePolyhedronFromPlanes_LineLL *pRelink = &pPatchStop->pLine->PointLineLinks[pPatchStop->iReferenceIndex]; + //unlink pPatchStop from dead point + pRelink->pNext->pPrev = pRelink->pPrev; + pRelink->pPrev->pNext = pRelink->pNext; + + //relink into pPatchWalk's living point (insert arbitrarily before pPatchWalk) + pRelink->pNext = &pPatchWalk->pLine->PointLineLinks[pPatchWalk->iReferenceIndex]; + pRelink->pPrev = pRelink->pNext->pPrev; + pRelink->pNext->pPrev = pRelink; + pRelink->pPrev->pNext = pRelink; + } + + pPatchWalk = pPatchWalk->pNext; //forward to the next line that won't be dead when we're done. From here on it's a clockwise walk on this polygon until we reach a line we'll kill, then we switch to the next + + //go ahead and kill the dead on-polygon line + { + GeneratePolyhedronFromPlanes_Line *pKillLine = pPatchWalk->pPrev->pLine; +#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + pKillLine->planarity = LINE_DEAD; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it. +#endif + UnlinkLine( pKillLine ); + DestructLine( pKillLine, destructors ); + } + + do + { + if( pPatchWalk->pLine->pPoints[pPatchWalk->iReferenceIndex] == pProblemPoint ) + { + //done walking this polygon, stitch it to the next + GeneratePolyhedronFromPlanes_Line *pKillLine = pPatchWalk->pLine; + GeneratePolyhedronFromPlanes_Polygon *pKillPolygon = pPatchWalk->pLine->pPolygons[pPatchWalk->iReferenceIndex]; + + GeneratePolyhedronFromPlanes_LineLL *pPatchPrev = pPatchWalk->pPrev; + pPatchWalk = pPatchWalk->pLine->PolygonLineLinks[1 - pPatchWalk->iReferenceIndex].pNext; //flip to the other side's polygon, then forward to the next line we'll keep + + //patching will be more straightforward if we kill all the dead stuff now +#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + pKillLine->planarity = LINE_DEAD; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it. +#endif + UnlinkLine( pKillLine ); + DestructLine( pKillLine, destructors ); + if( pKillPolygon != pPatchPolygon ) + { +#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + pKillPolygon->bDead = true; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it. +#endif + DestructPolygon( pKillPolygon, destructors ); + } + + //link the far end of the polygon to our start (which should continually be the first living pPatchWalk line on pPatchPolygon + pPatchWalk->pPrev->pNext = pPatchPrev->pNext; + pPatchPrev->pNext->pPrev = pPatchWalk->pPrev; + + pPatchPrev->pNext = pPatchWalk; + pPatchWalk->pPrev = pPatchPrev; //at this point, the two polygons's line linkages are one large polygon except that the new polygon's line are pointing at the dead polygon which will be fixed as we continue to walk it + } + pPatchWalk->pLine->pPolygons[pPatchWalk->iReferenceIndex] = pPatchPolygon; + + pPatchWalk = pPatchWalk->pNext; + } while( pPatchWalk != pPatchStop ); + + //final patch is just slightly different than other patches + { +#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) + pPatchStop->pLine->pPolygons[pPatchStop->iReferenceIndex]->bDead = true; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it. +#endif + DestructPolygon( pPatchStop->pLine->pPolygons[pPatchStop->iReferenceIndex], destructors ); + pPatchStop->pLine->pPolygons[pPatchStop->iReferenceIndex] = pPatchPolygon; + } + + DestructPoint( pProblemPoint, destructors ); + + Assert( pAllPolygons->pNext != NULL ); + if( pAllPolygons->pNext->pNext != NULL ) + { + RemoveDegeneratePoints( pPatchPolygon, destructors ); + } + + //vLastLine and vThisLine are invalidated. Luckily we just need to recompute vThisLine and it'll copy to vLastLine + vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition ); + vThisLine.Init( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) ); + } + + vLastLine = vThisLine; + pLineWalk = pLineWalk->pNext; + } while ( pLineWalk != pLineWalkHead ); + } + + pActivePolygonWalk = pActivePolygonWalk->pNext; + } while( pActivePolygonWalk ); + } + + +#ifdef DBGFLAG_ASSERT //verify that repairs are complete { + DBG_RESETWORKINGSTATECOLORS(); + GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDebugPolygonWalk = pAllPolygons; do { - AssertMsg_DumpPolyhedron( pDebugPolygonWalk->pPolygon->bMissingASide == false, "Some polygons not repaired after cut" ); + GeneratePolyhedronFromPlanes_LineLL *pLineStart = pDebugPolygonWalk->polygon.pLines; + GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pLineStart; + GeneratePolyhedronFromPlanes_Point *pCheckPoint = pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]; + + int iCount = 0; + //walk around the polygon according to its line links + do + { + Assert_DumpPolyhedron( pLineWalk->pLine->planarity != LINE_DEAD ); + Assert_DumpPolyhedron( (pLineWalk->pLine->pPoints[0]->planarity != POINT_DEAD) && (pLineWalk->pLine->pPoints[1]->planarity != POINT_DEAD) ); + AssertMsg_DumpPolyhedron( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex] == pCheckPoint, "Line endpoint mismatch" ); //last line's endpoint does not match up with this lines start point + AssertMsg_DumpPolyhedron( pLineWalk->pLine->pPolygons[pLineWalk->iReferenceIndex] == &pDebugPolygonWalk->polygon, "Line links to wrong polygon" ); + +#if 1 + fltx4 f4Diff1 = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition ); + fltx4 f4Diff2 = SubSIMD( pLineWalk->pNext->pLine->pPoints[pLineWalk->pNext->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition ); + Vector vDiff1( SubFloat( f4Diff1, 0 ), SubFloat( f4Diff1, 1 ), SubFloat( f4Diff1, 2 ) ); + Vector vDiff2( SubFloat( f4Diff2, 0 ), SubFloat( f4Diff2, 1 ), SubFloat( f4Diff2, 2 ) ); + + Vector vCross = vDiff2.Cross( vDiff1 ); + float fDot = vCross.Dot( pDebugPolygonWalk->polygon.vSurfaceNormal ); + AssertMsg_DumpPolyhedron( fDot >= 0.0f, "Concave polygon" ); +#endif + + + pCheckPoint = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]; + pLineWalk = pLineWalk->pNext; + ++iCount; + } while( pLineWalk != pLineStart ); + AssertMsg_DumpPolyhedron( iCount >= 3, "Degenerate polygon" ); + + + +#if 0 + fltx4 vSurfaceNormal; + SubFloat( vSurfaceNormal, 0 ) = pDebugPolygonWalk->polygon.vSurfaceNormal.x; + SubFloat( vSurfaceNormal, 1 ) = pDebugPolygonWalk->polygon.vSurfaceNormal.y; + SubFloat( vSurfaceNormal, 2 ) = pDebugPolygonWalk->polygon.vSurfaceNormal.z; + SubFloat( vSurfaceNormal, 3 ) = 0.0f; +#endif + + pLineStart = &pLineStart->pLine->PointLineLinks[1 - pLineStart->iReferenceIndex]; + pLineWalk = pLineStart; + //walk around the polygon again using point traversal + do + { + AssertMsg_DumpPolyhedron( pLineWalk->pLine->pPolygons[pLineWalk->iReferenceIndex] == &pDebugPolygonWalk->polygon, "bad line/polygon linkage" ); + +#if 0 + //at each point of each polygon, make sure every line connected to that point goes away from our normal + { + GeneratePolyhedronFromPlanes_LineLL *pConcavityWalk = &pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex]; + GeneratePolyhedronFromPlanes_LineLL *pConcavityWalkStop = pConcavityWalk->pPrev; + pConcavityWalk = pConcavityWalk->pNext; + while ( pConcavityWalk != pConcavityWalkStop ) + { + fltx4 vLine = SubSIMD( pConcavityWalk->pLine->pPoints[pConcavityWalk->iReferenceIndex]->ptPosition, pConcavityWalk->pLine->pPoints[1 - pConcavityWalk->iReferenceIndex]->ptPosition ); + fltx4 vMul = MulSIMD( vSurfaceNormal, vLine ); + float fDot = SubFloat( vMul, 0 ) + SubFloat( vMul, 1 ) + SubFloat( vMul, 2 ); + AssertMsg_DumpPolyhedron( fDot <= kfPointRounding, "Concave polyhedron" ); + pConcavityWalk = pConcavityWalk->pNext; + } + } +#endif + + pLineWalk = pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pPrev; + } while( pLineWalk != pLineStart ); + pDebugPolygonWalk = pDebugPolygonWalk->pNext; } while( pDebugPolygonWalk ); + bool bTwoPolygons = (pAllPolygons->pNext->pNext == NULL); + GeneratePolyhedronFromPlanes_UnorderedPointLL *pDebugPointWalk = pAllPoints; do { - AssertMsg_DumpPolyhedron( pDebugPointWalk->pPoint->pConnectedLines, "Point connected to no lines after cut" ); - pDebugPointWalk = pDebugPointWalk->pNext; + int iCount = 0; + AssertMsg_DumpPolyhedron( pDebugPointWalk->point.pConnectedLines, "Point connected to no lines after cut" ); + GeneratePolyhedronFromPlanes_LineLL *pLineStart = pDebugPointWalk->point.pConnectedLines; + GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pLineStart; + GeneratePolyhedronFromPlanes_Point *pTestPoint = &pDebugPointWalk->point; + do + { + fltx4 f4Diff = SubSIMD( pTestPoint->ptPosition, pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition ); + Vector vDiff( SubFloat( f4Diff, 0 ), SubFloat( f4Diff, 1 ), SubFloat( f4Diff, 2 ) ); + float fLengthSqr = vDiff.LengthSqr(); + AssertMsg_DumpPolyhedron( fLengthSqr > 0.0f, "Generated a point on top of another" ); + AssertMsg_DumpPolyhedron( pLineWalk->pLine->pPolygons[pLineWalk->iReferenceIndex] == pLineWalk->pNext->pLine->pPolygons[1 - pLineWalk->pNext->iReferenceIndex], "Bad line/polygon linkage" ); + + GeneratePolyhedronFromPlanes_LineLL *pSubLineWalk = pLineWalk->pNext; + while( pSubLineWalk != pLineStart ) + { + AssertMsg_DumpPolyhedron( (pLineWalk->pLine->pPolygons[pLineWalk->iReferenceIndex] != pSubLineWalk->pLine->pPolygons[pSubLineWalk->iReferenceIndex]) && + (pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex] != pSubLineWalk->pLine->pPolygons[1 - pSubLineWalk->iReferenceIndex]), "Point connected to two other points on the same polygon" ); + + pSubLineWalk = pSubLineWalk->pNext; + } + pLineWalk = pLineWalk->pNext; + ++iCount; + } while( pLineWalk != pLineStart ); + + AssertMsg_DumpPolyhedron( bTwoPolygons || iCount >= 3, "Degenerate point" ); //there can be two-sided polyhedrons where this is not a degenerate case + + pDebugPointWalk = pDebugPointWalk->pNext; } while( pDebugPointWalk ); + + + GeneratePolyhedronFromPlanes_UnorderedLineLL *pDebugLineWalk = pAllLines; + do + { + AssertMsg_DumpPolyhedron( (pDebugLineWalk->line.pPolygons[0] != NULL) && (pDebugLineWalk->line.pPolygons[1] != NULL), "There's a polygon missing" ); + AssertMsg_DumpPolyhedron( pDebugLineWalk->line.pPoints[0] && pDebugLineWalk->line.pPoints[1], "Line missing a point" ); + +#if 0 + fltx4 f4Line = SubSIMD( pDebugLineWalk->line.pPoints[0]->ptPosition, pDebugLineWalk->line.pPoints[1]->ptPosition ); + Vector vLine( SubFloat( f4Line, 0 ), SubFloat( f4Line, 1 ), SubFloat( f4Line, 2 ) ); + float fLength = vLine.Length(); + AssertMsg_DumpPolyhedron( fLength > FLT_EPSILON, "Ridiculously short line" ); + + if( fLength > fOnPlaneEpsilon ) + { + if( fLength > 1.0f ) + vLine *= 1.0f / fLength; + + float fDots[2] = { vLine.Dot( pDebugLineWalk->line.pPolygons[0]->vSurfaceNormal ), vLine.Dot( pDebugLineWalk->line.pPolygons[1]->vSurfaceNormal ) }; + AssertMsg_DumpPolyhedron( (fabs( fDots[0] ) < (1.0f/128.0f) ) && + (fabs( fDots[1] ) < (1.0f/128.0f) ), + "Line is not orthogonal to plane normal it's surrounding" ); + } +#endif + + pDebugLineWalk = pDebugLineWalk->pNext; + } while( pDebugLineWalk ); + pStartPoint = NULL; } + CPolyhedron *pHistoryPolyhedron = ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions ); + +#if defined( DEBUG_POLYHEDRON_CONVERSION ) + //last bit of debugging from whatever outside source wants this stupid thing + if( g_pPolyhedronCarvingDebugStepCallback != NULL ) + { + if( !g_pPolyhedronCarvingDebugStepCallback( pHistoryPolyhedron ) ) + { + VMatrix matScaleCentered; + matScaleCentered.Identity(); + matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f; + matScaleCentered.SetTranslation( -pHistoryPolyhedron->Center() * 10.0f ); + + DumpPolyhedronToGLView( pHistoryPolyhedron, "AssertPolyhedron.txt", &matScaleCentered, "wb" ); + AssertMsg_DumpPolyhedron( false, "Outside conversion failed" ); + } + } +#endif + //maintain the cut history - DebugCutHistory.AddToTail( ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false ) ); + destructors.DebugCutHistory.AddToTail( pHistoryPolyhedron ); + destructors.DebugCutPlaneIndex.AddToTail( iCurrentPlane ); #endif } -#ifdef _DEBUG - for( int i = DebugCutHistory.Count(); --i >= 0; ) - { - if( DebugCutHistory[i] ) - DebugCutHistory[i]->Release(); - } - DebugCutHistory.RemoveAll(); -#endif - - return ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, bUseTemporaryMemory ); + return ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, bUseTemporaryMemory, vShiftResultPositions ); } #define STARTPOINTTOLINELINKS(iPointNum, lineindex1, iOtherPointIndex1, lineindex2, iOtherPointIndex2, lineindex3, iOtherPointIndex3 )\ - StartingBoxPoints[iPointNum].pConnectedLines = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 0];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 0].pLine = &StartingBoxLines[lineindex1];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 0].iReferenceIndex = iOtherPointIndex1;\ - StartingBoxLines[lineindex1].pPointLineLinks[1 - iOtherPointIndex1] = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 0];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 0].pPrev = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 2];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 0].pNext = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 1];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 1].pLine = &StartingBoxLines[lineindex2];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 1].iReferenceIndex = iOtherPointIndex2;\ - StartingBoxLines[lineindex2].pPointLineLinks[1 - iOtherPointIndex2] = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 1];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 1].pPrev = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 0];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 1].pNext = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 2];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 2].pLine = &StartingBoxLines[lineindex3];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 2].iReferenceIndex = iOtherPointIndex3;\ - StartingBoxLines[lineindex3].pPointLineLinks[1 - iOtherPointIndex3] = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 2];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 2].pPrev = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 1];\ - StartingPoints_To_Lines_Links[(iPointNum * 3) + 2].pNext = &StartingPoints_To_Lines_Links[(iPointNum * 3) + 0]; + StartingPointList[iPointNum].point.pConnectedLines = &StartingLineList[lineindex1].line.PointLineLinks[1 - iOtherPointIndex1];\ + StartingPointList[iPointNum].point.pConnectedLines->pNext = &StartingLineList[lineindex2].line.PointLineLinks[1 - iOtherPointIndex2];\ + StartingPointList[iPointNum].point.pConnectedLines->pPrev = &StartingLineList[lineindex3].line.PointLineLinks[1 - iOtherPointIndex3];\ + StartingPointList[iPointNum].point.pConnectedLines->pNext->pNext = StartingPointList[iPointNum].point.pConnectedLines->pPrev;\ + StartingPointList[iPointNum].point.pConnectedLines->pNext->pPrev = StartingPointList[iPointNum].point.pConnectedLines;\ + StartingPointList[iPointNum].point.pConnectedLines->pPrev->pNext = StartingPointList[iPointNum].point.pConnectedLines;\ + StartingPointList[iPointNum].point.pConnectedLines->pPrev->pPrev = StartingPointList[iPointNum].point.pConnectedLines->pNext; #define STARTBOXCONNECTION( linenum, point1, point2, poly1, poly2 )\ - StartingBoxLines[linenum].pPoints[0] = &StartingBoxPoints[point1];\ - StartingBoxLines[linenum].pPoints[1] = &StartingBoxPoints[point2];\ - StartingBoxLines[linenum].pPolygons[0] = &StartingBoxPolygons[poly1];\ - StartingBoxLines[linenum].pPolygons[1] = &StartingBoxPolygons[poly2]; + StartingLineList[linenum].line.pPoints[0] = &StartingPointList[point1].point;\ + StartingLineList[linenum].line.pPoints[1] = &StartingPointList[point2].point;\ + StartingLineList[linenum].line.pPolygons[0] = &StartingPolygonList[poly1].polygon;\ + StartingLineList[linenum].line.pPolygons[1] = &StartingPolygonList[poly2].polygon; #define STARTPOLYGONTOLINELINKS( polynum, lineindex1, iThisPolyIndex1, lineindex2, iThisPolyIndex2, lineindex3, iThisPolyIndex3, lineindex4, iThisPolyIndex4 )\ - StartingBoxPolygons[polynum].pLines = &StartingPolygon_To_Lines_Links[(polynum * 4) + 0];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 0].pLine = &StartingBoxLines[lineindex1];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 0].iReferenceIndex = iThisPolyIndex1;\ - StartingBoxLines[lineindex1].pPolygonLineLinks[iThisPolyIndex1] = &StartingPolygon_To_Lines_Links[(polynum * 4) + 0];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 0].pPrev = &StartingPolygon_To_Lines_Links[(polynum * 4) + 3];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 0].pNext = &StartingPolygon_To_Lines_Links[(polynum * 4) + 1];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 1].pLine = &StartingBoxLines[lineindex2];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 1].iReferenceIndex = iThisPolyIndex2;\ - StartingBoxLines[lineindex2].pPolygonLineLinks[iThisPolyIndex2] = &StartingPolygon_To_Lines_Links[(polynum * 4) + 1];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 1].pPrev = &StartingPolygon_To_Lines_Links[(polynum * 4) + 0];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 1].pNext = &StartingPolygon_To_Lines_Links[(polynum * 4) + 2];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 2].pLine = &StartingBoxLines[lineindex3];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 2].iReferenceIndex = iThisPolyIndex3;\ - StartingBoxLines[lineindex3].pPolygonLineLinks[iThisPolyIndex3] = &StartingPolygon_To_Lines_Links[(polynum * 4) + 2];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 2].pPrev = &StartingPolygon_To_Lines_Links[(polynum * 4) + 1];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 2].pNext = &StartingPolygon_To_Lines_Links[(polynum * 4) + 3];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 3].pLine = &StartingBoxLines[lineindex4];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 3].iReferenceIndex = iThisPolyIndex4;\ - StartingBoxLines[lineindex4].pPolygonLineLinks[iThisPolyIndex4] = &StartingPolygon_To_Lines_Links[(polynum * 4) + 3];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 3].pPrev = &StartingPolygon_To_Lines_Links[(polynum * 4) + 2];\ - StartingPolygon_To_Lines_Links[(polynum * 4) + 3].pNext = &StartingPolygon_To_Lines_Links[(polynum * 4) + 0]; - + StartingPolygonList[polynum].polygon.pLines = &StartingLineList[lineindex1].line.PolygonLineLinks[iThisPolyIndex1];\ + StartingPolygonList[polynum].polygon.pLines->pNext = &StartingLineList[lineindex2].line.PolygonLineLinks[iThisPolyIndex2];\ + StartingPolygonList[polynum].polygon.pLines->pPrev = &StartingLineList[lineindex4].line.PolygonLineLinks[iThisPolyIndex4];\ + StartingPolygonList[polynum].polygon.pLines->pNext->pPrev = StartingPolygonList[polynum].polygon.pLines;\ + StartingPolygonList[polynum].polygon.pLines->pPrev->pNext = StartingPolygonList[polynum].polygon.pLines;\ + StartingPolygonList[polynum].polygon.pLines->pNext->pNext = &StartingLineList[lineindex3].line.PolygonLineLinks[iThisPolyIndex3];\ + StartingPolygonList[polynum].polygon.pLines->pPrev->pPrev = StartingPolygonList[polynum].polygon.pLines->pNext->pNext;\ + StartingPolygonList[polynum].polygon.pLines->pNext->pNext->pPrev = StartingPolygonList[polynum].polygon.pLines->pNext;\ + StartingPolygonList[polynum].polygon.pLines->pPrev->pPrev->pNext = StartingPolygonList[polynum].polygon.pLines->pPrev; CPolyhedron *GeneratePolyhedronFromPlanes( const float *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory ) { @@ -1784,69 +3066,143 @@ CPolyhedron *GeneratePolyhedronFromPlanes( const float *pOutwardFacingPlanes, in //this version will start with a cube and hack away at it (retaining point connection information) to produce a polyhedron with no guesswork involved, this method should be rock solid //the polygon clipping functions we're going to use want inward facing planes - float *pFlippedPlanes = (float *)stackalloc( (iPlaneCount * 4) * sizeof( float ) ); - for( int i = 0; i != iPlaneCount * 4; ++i ) + const size_t kFltX4Align = sizeof( fltx4 ) - 1; + uint8 *pAlignedAlloc = (uint8 *)stackalloc( (sizeof( fltx4 ) * iPlaneCount) + kFltX4Align ); + pAlignedAlloc = (uint8 *)(((size_t)(pAlignedAlloc + kFltX4Align)) & ~kFltX4Align); + fltx4 *pAlteredPlanes = (fltx4 *)pAlignedAlloc; + for( int i = 0; i != iPlaneCount; ++i ) { - pFlippedPlanes[i] = -pOutwardFacingPlanes[i]; + SubFloat( pAlteredPlanes[i], 0 ) = -pOutwardFacingPlanes[(i * 4) + 0]; + SubFloat( pAlteredPlanes[i], 1 ) = -pOutwardFacingPlanes[(i * 4) + 1]; + SubFloat( pAlteredPlanes[i], 2 ) = -pOutwardFacingPlanes[(i * 4) + 2]; + SubFloat( pAlteredPlanes[i], 3 ) = -pOutwardFacingPlanes[(i * 4) + 3]; } //our first goal is to find the size of a cube big enough to encapsulate all points that will be in the final polyhedron - Vector vAABBMins, vAABBMaxs; - if( FindConvexShapeLooseAABB( pFlippedPlanes, iPlaneCount, &vAABBMins, &vAABBMaxs ) == false ) + Vector vAABBMinsVec, vAABBMaxsVec; + if( FindConvexShapeLooseAABB( pAlteredPlanes, iPlaneCount, &vAABBMinsVec, &vAABBMaxsVec ) == false ) return NULL; //no shape to work with apparently + fltx4 vAABBMins; + SubFloat( vAABBMins, 0 ) = vAABBMinsVec.x; + SubFloat( vAABBMins, 1 ) = vAABBMinsVec.y; + SubFloat( vAABBMins, 2 ) = vAABBMinsVec.z; + SubFloat( vAABBMins, 3 ) = 1.0f; + + fltx4 vAABBMaxs; + SubFloat( vAABBMaxs, 0 ) = vAABBMaxsVec.x; + SubFloat( vAABBMaxs, 1 ) = vAABBMaxsVec.y; + SubFloat( vAABBMaxs, 2 ) = vAABBMaxsVec.z; + SubFloat( vAABBMaxs, 3 ) = 1.0f; + +#if defined( USE_WORLD_CENTERED_POSITIONS ) + fltx4 vPointOffset = MulSIMD( AddSIMD( vAABBMins, vAABBMaxs ), ReplicateX4( -0.5f ) ); + vPointOffset = FloorSIMD( vPointOffset ); + SubFloat( vPointOffset, 3 ) = -1.0f; +#endif + + for( int i = 0; i != iPlaneCount; ++i ) + { + pAlteredPlanes[i] = NegSIMD( pAlteredPlanes[i] ); + } + +#if defined( USE_WORLD_CENTERED_POSITIONS ) + //shift all the planes toward origin + for( int i = 0; i != iPlaneCount; ++i ) + { + fltx4 vMul = MulSIMD( pAlteredPlanes[i], vPointOffset ); + SubFloat( pAlteredPlanes[i], 3 ) = SubFloat( vMul, 0 ) + SubFloat( vMul, 1 ) + SubFloat( vMul, 2 ) - SubFloat( vMul, 3 ); + } + //shift the AABB (and all starting points) toward the origin + vAABBMins = AddSIMD( vAABBMins, vPointOffset ); + vAABBMaxs = AddSIMD( vAABBMaxs, vPointOffset ); + fltx4 vResultOffset = NegSIMD( vPointOffset ); + SubFloat( vResultOffset, 3 ) = 0.0f; +#else + fltx4 vResultOffset = LoadZeroSIMD(); +#endif //grow the bounding box to a larger size since it's probably inaccurate a bit { - Vector vGrow = (vAABBMaxs - vAABBMins) * 0.5f; - vGrow.x += 100.0f; - vGrow.y += 100.0f; - vGrow.z += 100.0f; - - vAABBMaxs += vGrow; - vAABBMins -= vGrow; + fltx4 vGrow = MulSIMD( SubSIMD( vAABBMaxs, vAABBMins ), Four_PointFives ); + vGrow = AddSIMD( vGrow, ReplicateX4( 10.0f ) ); + SubFloat( vGrow, 3 ) = 0.0f; + + vAABBMins = SubSIMD( vAABBMins, vGrow ); + vAABBMaxs = AddSIMD( vAABBMaxs, vGrow ); } + + vAABBMins = FloorSIMD( vAABBMins ); + vAABBMaxs = FloorSIMD( vAABBMaxs ); - //generate our starting cube using the 2x AABB so we can start hacking away at it - - + vAABBMinsVec.x = SubFloat( vAABBMins, 0 ); + vAABBMinsVec.y = SubFloat( vAABBMins, 1 ); + vAABBMinsVec.z = SubFloat( vAABBMins, 2 ); + vAABBMaxsVec.x = SubFloat( vAABBMaxs, 0 ); + vAABBMaxsVec.y = SubFloat( vAABBMaxs, 1 ); + vAABBMaxsVec.z = SubFloat( vAABBMaxs, 2 ); + + //generate our starting cube using the 2x AABB so we can start hacking away at it //create our starting box on the stack - GeneratePolyhedronFromPlanes_Point StartingBoxPoints[8]; - GeneratePolyhedronFromPlanes_Line StartingBoxLines[12]; - GeneratePolyhedronFromPlanes_Polygon StartingBoxPolygons[6]; - GeneratePolyhedronFromPlanes_LineLL StartingPoints_To_Lines_Links[24]; //8 points, 3 lines per point - GeneratePolyhedronFromPlanes_LineLL StartingPolygon_To_Lines_Links[24]; //6 polygons, 4 lines per poly - GeneratePolyhedronFromPlanes_UnorderedPolygonLL StartingPolygonList[6]; //6 polygons GeneratePolyhedronFromPlanes_UnorderedLineLL StartingLineList[12]; //12 lines GeneratePolyhedronFromPlanes_UnorderedPointLL StartingPointList[8]; //8 points + for( int i = 0; i != 12; ++i ) + { + StartingLineList[i].line.InitLineLinks(); + } + //I had to work all this out on a whiteboard if it seems completely unintuitive. { - StartingBoxPoints[0].ptPosition.Init( vAABBMins.x, vAABBMins.y, vAABBMins.z ); + SubFloat( StartingPointList[0].point.ptPosition, 0 ) = vAABBMinsVec.x; + SubFloat( StartingPointList[0].point.ptPosition, 1 ) = vAABBMinsVec.y; + SubFloat( StartingPointList[0].point.ptPosition, 2 ) = vAABBMinsVec.z; + SubFloat( StartingPointList[0].point.ptPosition, 3 ) = -1.0f; STARTPOINTTOLINELINKS( 0, 0, 1, 4, 1, 3, 0 ); - StartingBoxPoints[1].ptPosition.Init( vAABBMins.x, vAABBMaxs.y, vAABBMins.z ); + SubFloat( StartingPointList[1].point.ptPosition, 0 ) = vAABBMinsVec.x; + SubFloat( StartingPointList[1].point.ptPosition, 1 ) = vAABBMaxsVec.y; + SubFloat( StartingPointList[1].point.ptPosition, 2 ) = vAABBMinsVec.z; + SubFloat( StartingPointList[1].point.ptPosition, 3 ) = -1.0f; STARTPOINTTOLINELINKS( 1, 0, 0, 1, 1, 5, 1 ); - StartingBoxPoints[2].ptPosition.Init( vAABBMins.x, vAABBMins.y, vAABBMaxs.z ); + SubFloat( StartingPointList[2].point.ptPosition, 0 ) = vAABBMinsVec.x; + SubFloat( StartingPointList[2].point.ptPosition, 1 ) = vAABBMinsVec.y; + SubFloat( StartingPointList[2].point.ptPosition, 2 ) = vAABBMaxsVec.z; + SubFloat( StartingPointList[2].point.ptPosition, 3 ) = -1.0f; STARTPOINTTOLINELINKS( 2, 4, 0, 8, 1, 11, 0 ); - StartingBoxPoints[3].ptPosition.Init( vAABBMins.x, vAABBMaxs.y, vAABBMaxs.z ); + SubFloat( StartingPointList[3].point.ptPosition, 0 ) = vAABBMinsVec.x; + SubFloat( StartingPointList[3].point.ptPosition, 1 ) = vAABBMaxsVec.y; + SubFloat( StartingPointList[3].point.ptPosition, 2 ) = vAABBMaxsVec.z; + SubFloat( StartingPointList[3].point.ptPosition, 3 ) = -1.0f; STARTPOINTTOLINELINKS( 3, 5, 0, 9, 1, 8, 0 ); - StartingBoxPoints[4].ptPosition.Init( vAABBMaxs.x, vAABBMins.y, vAABBMins.z ); + SubFloat( StartingPointList[4].point.ptPosition, 0 ) = vAABBMaxsVec.x; + SubFloat( StartingPointList[4].point.ptPosition, 1 ) = vAABBMinsVec.y; + SubFloat( StartingPointList[4].point.ptPosition, 2 ) = vAABBMinsVec.z; + SubFloat( StartingPointList[4].point.ptPosition, 3 ) = -1.0f; STARTPOINTTOLINELINKS( 4, 2, 0, 3, 1, 7, 1 ); - StartingBoxPoints[5].ptPosition.Init( vAABBMaxs.x, vAABBMaxs.y, vAABBMins.z ); + SubFloat( StartingPointList[5].point.ptPosition, 0 ) = vAABBMaxsVec.x; + SubFloat( StartingPointList[5].point.ptPosition, 1 ) = vAABBMaxsVec.y; + SubFloat( StartingPointList[5].point.ptPosition, 2 ) = vAABBMinsVec.z; + SubFloat( StartingPointList[5].point.ptPosition, 3 ) = -1.0f; STARTPOINTTOLINELINKS( 5, 1, 0, 2, 1, 6, 1 ); - StartingBoxPoints[6].ptPosition.Init( vAABBMaxs.x, vAABBMins.y, vAABBMaxs.z ); + SubFloat( StartingPointList[6].point.ptPosition, 0 ) = vAABBMaxsVec.x; + SubFloat( StartingPointList[6].point.ptPosition, 1 ) = vAABBMinsVec.y; + SubFloat( StartingPointList[6].point.ptPosition, 2 ) = vAABBMaxsVec.z; + SubFloat( StartingPointList[6].point.ptPosition, 3 ) = -1.0f; STARTPOINTTOLINELINKS( 6, 7, 0, 11, 1, 10, 0 ); - StartingBoxPoints[7].ptPosition.Init( vAABBMaxs.x, vAABBMaxs.y, vAABBMaxs.z ); + SubFloat( StartingPointList[7].point.ptPosition, 0 ) = vAABBMaxsVec.x; + SubFloat( StartingPointList[7].point.ptPosition, 1 ) = vAABBMaxsVec.y; + SubFloat( StartingPointList[7].point.ptPosition, 2 ) = vAABBMaxsVec.z; + SubFloat( StartingPointList[7].point.ptPosition, 3 ) = -1.0f; STARTPOINTTOLINELINKS( 7, 6, 0, 10, 1, 9, 0 ); STARTBOXCONNECTION( 0, 0, 1, 0, 5 ); @@ -1876,12 +3232,18 @@ CPolyhedron *GeneratePolyhedronFromPlanes( const float *pOutwardFacingPlanes, in STARTBOXCONNECTION( 10, 7, 6, 2, 4 ); STARTBOXCONNECTION( 11, 6, 2, 3, 4 ); - StartingBoxPolygons[0].vSurfaceNormal.Init( -1.0f, 0.0f, 0.0f ); - StartingBoxPolygons[1].vSurfaceNormal.Init( 0.0f, 1.0f, 0.0f ); - StartingBoxPolygons[2].vSurfaceNormal.Init( 1.0f, 0.0f, 0.0f ); - StartingBoxPolygons[3].vSurfaceNormal.Init( 0.0f, -1.0f, 0.0f ); - StartingBoxPolygons[4].vSurfaceNormal.Init( 0.0f, 0.0f, 1.0f ); - StartingBoxPolygons[5].vSurfaceNormal.Init( 0.0f, 0.0f, -1.0f ); + StartingPolygonList[0].polygon.vSurfaceNormal.Init( -1.0f, 0.0f, 0.0f ); + //StartingPolygonList[0].polygon.fNormalDist = -vAABBMinsVec.x; + StartingPolygonList[1].polygon.vSurfaceNormal.Init( 0.0f, 1.0f, 0.0f ); + //StartingPolygonList[1].polygon.fNormalDist = vAABBMaxsVec.y; + StartingPolygonList[2].polygon.vSurfaceNormal.Init( 1.0f, 0.0f, 0.0f ); + //StartingPolygonList[2].polygon.fNormalDist = vAABBMaxsVec.x; + StartingPolygonList[3].polygon.vSurfaceNormal.Init( 0.0f, -1.0f, 0.0f ); + //StartingPolygonList[3].polygon.fNormalDist = -vAABBMinsVec.y; + StartingPolygonList[4].polygon.vSurfaceNormal.Init( 0.0f, 0.0f, 1.0f ); + //StartingPolygonList[4].polygon.fNormalDist = vAABBMaxsVec.z; + StartingPolygonList[5].polygon.vSurfaceNormal.Init( 0.0f, 0.0f, -1.0f ); + //StartingPolygonList[5].polygon.fNormalDist = -vAABBMinsVec.z; STARTPOLYGONTOLINELINKS( 0, 0, 1, 5, 1, 8, 0, 4, 0 ); @@ -1893,27 +3255,21 @@ CPolyhedron *GeneratePolyhedronFromPlanes( const float *pOutwardFacingPlanes, in { - StartingPolygonList[0].pPolygon = &StartingBoxPolygons[0]; StartingPolygonList[0].pNext = &StartingPolygonList[1]; StartingPolygonList[0].pPrev = NULL; - StartingPolygonList[1].pPolygon = &StartingBoxPolygons[1]; StartingPolygonList[1].pNext = &StartingPolygonList[2]; StartingPolygonList[1].pPrev = &StartingPolygonList[0]; - StartingPolygonList[2].pPolygon = &StartingBoxPolygons[2]; StartingPolygonList[2].pNext = &StartingPolygonList[3]; StartingPolygonList[2].pPrev = &StartingPolygonList[1]; - StartingPolygonList[3].pPolygon = &StartingBoxPolygons[3]; StartingPolygonList[3].pNext = &StartingPolygonList[4]; StartingPolygonList[3].pPrev = &StartingPolygonList[2]; - StartingPolygonList[4].pPolygon = &StartingBoxPolygons[4]; StartingPolygonList[4].pNext = &StartingPolygonList[5]; StartingPolygonList[4].pPrev = &StartingPolygonList[3]; - StartingPolygonList[5].pPolygon = &StartingBoxPolygons[5]; StartingPolygonList[5].pNext = NULL; StartingPolygonList[5].pPrev = &StartingPolygonList[4]; } @@ -1921,91 +3277,81 @@ CPolyhedron *GeneratePolyhedronFromPlanes( const float *pOutwardFacingPlanes, in { - StartingLineList[0].pLine = &StartingBoxLines[0]; StartingLineList[0].pNext = &StartingLineList[1]; StartingLineList[0].pPrev = NULL; - StartingLineList[1].pLine = &StartingBoxLines[1]; StartingLineList[1].pNext = &StartingLineList[2]; StartingLineList[1].pPrev = &StartingLineList[0]; - StartingLineList[2].pLine = &StartingBoxLines[2]; StartingLineList[2].pNext = &StartingLineList[3]; StartingLineList[2].pPrev = &StartingLineList[1]; - StartingLineList[3].pLine = &StartingBoxLines[3]; StartingLineList[3].pNext = &StartingLineList[4]; StartingLineList[3].pPrev = &StartingLineList[2]; - StartingLineList[4].pLine = &StartingBoxLines[4]; StartingLineList[4].pNext = &StartingLineList[5]; StartingLineList[4].pPrev = &StartingLineList[3]; - StartingLineList[5].pLine = &StartingBoxLines[5]; StartingLineList[5].pNext = &StartingLineList[6]; StartingLineList[5].pPrev = &StartingLineList[4]; - StartingLineList[6].pLine = &StartingBoxLines[6]; StartingLineList[6].pNext = &StartingLineList[7]; StartingLineList[6].pPrev = &StartingLineList[5]; - StartingLineList[7].pLine = &StartingBoxLines[7]; StartingLineList[7].pNext = &StartingLineList[8]; StartingLineList[7].pPrev = &StartingLineList[6]; - StartingLineList[8].pLine = &StartingBoxLines[8]; StartingLineList[8].pNext = &StartingLineList[9]; StartingLineList[8].pPrev = &StartingLineList[7]; - StartingLineList[9].pLine = &StartingBoxLines[9]; StartingLineList[9].pNext = &StartingLineList[10]; StartingLineList[9].pPrev = &StartingLineList[8]; - StartingLineList[10].pLine = &StartingBoxLines[10]; StartingLineList[10].pNext = &StartingLineList[11]; StartingLineList[10].pPrev = &StartingLineList[9]; - StartingLineList[11].pLine = &StartingBoxLines[11]; StartingLineList[11].pNext = NULL; StartingLineList[11].pPrev = &StartingLineList[10]; } { - StartingPointList[0].pPoint = &StartingBoxPoints[0]; StartingPointList[0].pNext = &StartingPointList[1]; StartingPointList[0].pPrev = NULL; - StartingPointList[1].pPoint = &StartingBoxPoints[1]; StartingPointList[1].pNext = &StartingPointList[2]; StartingPointList[1].pPrev = &StartingPointList[0]; - StartingPointList[2].pPoint = &StartingBoxPoints[2]; StartingPointList[2].pNext = &StartingPointList[3]; StartingPointList[2].pPrev = &StartingPointList[1]; - StartingPointList[3].pPoint = &StartingBoxPoints[3]; StartingPointList[3].pNext = &StartingPointList[4]; StartingPointList[3].pPrev = &StartingPointList[2]; - StartingPointList[4].pPoint = &StartingBoxPoints[4]; StartingPointList[4].pNext = &StartingPointList[5]; StartingPointList[4].pPrev = &StartingPointList[3]; - StartingPointList[5].pPoint = &StartingBoxPoints[5]; StartingPointList[5].pNext = &StartingPointList[6]; StartingPointList[5].pPrev = &StartingPointList[4]; - StartingPointList[6].pPoint = &StartingBoxPoints[6]; StartingPointList[6].pNext = &StartingPointList[7]; StartingPointList[6].pPrev = &StartingPointList[5]; - StartingPointList[7].pPoint = &StartingBoxPoints[7]; StartingPointList[7].pNext = NULL; StartingPointList[7].pPrev = &StartingPointList[6]; } } - return ClipLinkedGeometry( StartingPolygonList, StartingLineList, StartingPointList, pOutwardFacingPlanes, iPlaneCount, fOnPlaneEpsilon, bUseTemporaryMemory ); + CPolyhedron *pRetVal = ClipLinkedGeometry( StartingPolygonList, StartingLineList, StartingPointList, 8, pAlteredPlanes, iPlaneCount, fOnPlaneEpsilon, bUseTemporaryMemory, vResultOffset ); + +#if defined( DEBUG_POLYHEDRON_CONVERSION ) + //last bit of debugging from whatever outside source wants this stupid thing + if( (g_pPolyhedronCarvingDebugStepCallback != NULL) && (pRetVal != NULL) ) + { + AssertMsg( g_pPolyhedronCarvingDebugStepCallback( pRetVal ), "Outside conversion failed" ); + } +#endif + + return pRetVal; } @@ -2049,7 +3395,7 @@ CPolyhedron *GeneratePolyhedronFromPlanes( const float *pOutwardFacingPlanes, in -#ifdef _DEBUG +#ifdef DBGFLAG_ASSERT void DumpAABBToGLView( const Vector &vCenter, const Vector &vExtents, const Vector &vColor, FILE *pFile ) { #ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS @@ -2203,18 +3549,263 @@ void DumpLineToGLView( const Vector &vPoint1, const Vector &vColor1, const Vecto #endif } -void DumpPolyhedronToGLView( const CPolyhedron *pPolyhedron, const char *pFilename, const VMatrix *pTransform ) +#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS + +void DumpWorkingStatePolygons( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, bool bIsDeadList, const VMatrix *pTransform, FILE *pFile ) +{ + while( pPolygons ) + { + GeneratePolyhedronFromPlanes_Polygon *pPoly = &pPolygons->polygon; + +#ifdef DBGFLAG_ASSERT + if( pPoly->pLines == (void *)0xCCCCCCCC ) + { + //this function gets called when something is already wrong. No need to crash instead of reporting something useful + pPolygons = pPolygons->pNext; + continue; + } +#endif + if( (pPoly->pLines == NULL) || (pPoly->pLines == pPoly->pLines->pNext) ) + { + pPolygons = pPolygons->pNext; + continue; //less than 3 points in the polygon so far, undrawable + } + + Vector vColor; //dead is dark grey, missing a side is red, complete is green, new is blue + if( pPoly->debugdata.vWorkingStateColorOverride != vec3_origin ) + { + vColor = pPoly->debugdata.vWorkingStateColorOverride; + } + else + { + if( bIsDeadList ) + { + vColor = Vector( 0.25f, 0.25f, 0.25f ); + } + else + { + /*if(pPoly->bMissingASide) + { + vColor = Vector( 1.0f, 0.0f, 0.0f ); + + Vector vPatchLineColor( 1.0f, 0.0f, 1.0f ); + fltx4 f4P1 = pPoly->pLines->pLine->pPoints[pPoly->pLines->iReferenceIndex]->ptPosition; + fltx4 f4P2 = pPoly->pLines->pNext->pLine->pPoints[1 - pPoly->pLines->pNext->iReferenceIndex]->ptPosition; + Vector vP1( SubFloat( f4P1, 0 ), SubFloat( f4P1, 1 ), SubFloat( f4P1, 2 ) ); + Vector vP2( SubFloat( f4P2, 0 ), SubFloat( f4P2, 1 ), SubFloat( f4P2, 2 ) ); + Vector vP1 = (*pTransform) * vP1; + Vector vP2 = (*pTransform) * vP2; + DumpLineToGLView( vP1, vPatchLineColor, vP2, vPatchLineColor, 0.1f, pFile ); + } + else*/ + { + if( pPoly->debugdata.bIsNew ) + vColor = Vector( 0.0f, 0.0f, 1.0f ); + else + vColor = Vector( 0.0f, 1.0f, 0.0f ); + } + } + vColor *= 0.33f; + } + + + + GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pPoly->pLines; + GeneratePolyhedronFromPlanes_LineLL *pLineWalkStart = pLineWalk; + int iVertexCount = 1; + do + { + ++iVertexCount; + pLineWalk = pLineWalk->pNext; + } while(pLineWalk != pLineWalkStart); + + fprintf( pFile, "%i\n", iVertexCount ); + fltx4 f4vertex = pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition; + Vector vertex( SubFloat( f4vertex, 0 ), SubFloat( f4vertex, 1 ), SubFloat( f4vertex, 2 ) ); + vertex = (*pTransform) * vertex; + fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vertex.x, vertex.y, vertex.z, vColor.x, vColor.y, vColor.z ); + do + { + f4vertex = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition; + vertex.Init( SubFloat( f4vertex, 0 ), SubFloat( f4vertex, 1 ), SubFloat( f4vertex, 2 ) ); + vertex = (*pTransform) * vertex; + fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vertex.x, vertex.y, vertex.z, vColor.x, vColor.y, vColor.z ); + pLineWalk = pLineWalk->pNext; + } while(pLineWalk != pLineWalkStart); + + pPolygons = pPolygons->pNext; + } +} + +void DumpWorkingStateLines( GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, const VMatrix *pTransform, FILE *pFile ) +{ + while( pLines ) + { + GeneratePolyhedronFromPlanes_Line *pLine = &pLines->line; + + if( (pLine->pPoints[0] == NULL) || (pLine->pPoints[1] == NULL) ) + { + pLines = pLines->pNext; + continue; + } + +#ifdef DBGFLAG_ASSERT + if( (pLine->pPoints[0] == (void *)0xCCCCCCCC) || (pLine->pPoints[1] == (void *)0xCCCCCCCC) ) + { + //this function gets called when something is already wrong. No need to crash instead of reporting something useful + pLines = pLines->pNext; + continue; + } +#endif + + Vector vColor; //dead is dark grey, cut is red, complete is green, re-lengthened is green+blue, onplane is yellow, new is blue + if( pLine->debugdata.vWorkingStateColorOverride != vec3_origin ) + { + vColor = pLine->debugdata.vWorkingStateColorOverride; + } + else + { + if( pLine->debugdata.bIsNew ) + { + if( pLine->planarity == LINE_DEAD ) + { + //created and then ditched + vColor = Vector( 0.5f, 0.5f, 1.0f ); + } + else + { + vColor = Vector( 0.0f, 0.0f, 1.0f ); + } + } + else + { + switch( pLine->planarity ) + { + case LINE_ONPLANE: + vColor = Vector( 1.0f, 1.0f, 0.0f ); + break; + case LINE_ALIVE: + /*if( pLine->debugdata.initialPlanarity & LINE_DEAD ) + { + vColor = Vector( 0.0f, 1.0f, 1.0f ); //re-lengthened + } + else*/ + { + vColor = Vector( 0.0f, 1.0f, 0.0f ); //always alive + } + break; + case LINE_DEAD: + vColor = Vector( 0.25f, 0.25f, 0.25f ); + break; + case LINE_CUT: + vColor = Vector( 1.0f, 0.0f, 0.0f ); + break; + }; + } + } + + fltx4 f4Pos0 = pLine->pPoints[0]->ptPosition; + Vector vPos0( SubFloat( f4Pos0, 0 ), SubFloat( f4Pos0, 1 ), SubFloat( f4Pos0, 2 ) ); + fltx4 f4Pos1 = pLine->pPoints[1]->ptPosition; + Vector vPos1( SubFloat( f4Pos1, 0 ), SubFloat( f4Pos1, 1 ), SubFloat( f4Pos1, 2 ) ); + DumpLineToGLView( (*pTransform) * vPos0, vColor, (*pTransform) * vPos1, vColor, 0.1f, pFile ); + + pLines = pLines->pNext; + } +} + + +void DumpWorkingStatePoints( GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, const VMatrix *pTransform, FILE *pFile ) +{ + while( pPoints ) + { + GeneratePolyhedronFromPlanes_Point *pPoint = &pPoints->point; + + Vector vColor; //dead is red, alive is green, onplane is red+green, new is blue + if( pPoint->debugdata.vWorkingStateColorOverride != vec3_origin ) + { + vColor = pPoint->debugdata.vWorkingStateColorOverride; + } + else + { + if( pPoint->debugdata.bIsNew ) + { + if( pPoint->planarity == POINT_DEAD ) + { + //created and then merged away + vColor = Vector( 0.5f, 0.5f, 1.0f ); + } + else + { + vColor = Vector( 0.0f, 0.0f, 1.0f ); + } + } + else + { + switch( pPoint->planarity ) + { + case POINT_ONPLANE: + vColor = Vector( 1.0f, 1.0f, 0.0f ); + break; + case POINT_ALIVE: + vColor = Vector( 0.0f, 1.0f, 0.0f ); //always alive + break; + case POINT_DEAD: + vColor = Vector( 1.0f, 0.0f, 0.0f ); + break; + }; + } + } + + fltx4 f4Pos = pPoint->ptPosition; + Vector vPos( SubFloat( f4Pos, 0 ), SubFloat( f4Pos, 1 ), SubFloat( f4Pos, 2 ) ); + DumpAABBToGLView( (*pTransform) * vPos, Vector( 0.15f, 0.15f, 0.15f ), vColor, pFile ); + + pPoints = pPoints->pNext; + } +} + +//dumps color coded information about a polyhedron that's in the middle of a cut (debugging only) +void DumpWorkingStatePolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDeadPolygons, + GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines, GeneratePolyhedronFromPlanes_UnorderedLineLL *pDeadLines, + GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPoints, + const char *pFilename, const VMatrix *pTransform ) +{ + if( pTransform == NULL ) + pTransform = &s_matIdentity; + + printf("Writing %s...\n", pFilename ); + + FILE *pFile = fopen( pFilename, "wb" ); + if( pFile == NULL ) + return; + + DumpWorkingStatePolygons( pAllPolygons, false, pTransform, pFile ); + //DumpWorkingStatePolygons( pDeadPolygons, true, pTransform, pFile ); //they're undrawable, they have less than 3 lines + + DumpWorkingStateLines( pAllLines, pTransform, pFile ); + DumpWorkingStateLines( pDeadLines, pTransform, pFile ); + + DumpWorkingStatePoints( pAllPoints, pTransform, pFile ); + DumpWorkingStatePoints( pDeadPoints, pTransform, pFile ); + + fclose( pFile ); +} +#endif + +void DumpPolyhedronToGLView( const CPolyhedron *pPolyhedron, const char *pFilename, const VMatrix *pTransform, const char *szfileOpenOptions ) { #ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS - if ( (pPolyhedron == NULL) || (pPolyhedron->iVertexCount == 0) ) - return; + Assert( pPolyhedron && (pPolyhedron->iVertexCount > 2) ); if( pTransform == NULL ) pTransform = &s_matIdentity; printf("Writing %s...\n", pFilename ); - FILE *pFile = fopen( pFilename, "ab" ); + FILE *pFile = fopen( pFilename, szfileOpenOptions ); + + const Vector vOne( 1.0f, 1.0f, 1.0f ); //randomizing an array of colors to help spot shared/unshared vertices Vector *pColors = (Vector *)stackalloc( sizeof( Vector ) * pPolyhedron->iVertexCount ); @@ -2246,9 +3837,18 @@ void DumpPolyhedronToGLView( const CPolyhedron *pPolyhedron, const char *pFilena for( counter = 0; counter != pPolyhedron->iLineCount; ++counter ) { - const Vector vOne( 1.0f, 1.0f, 1.0f ); - DumpLineToGLView( pTransformedPoints[pPolyhedron->pLines[counter].iPointIndices[0]], vOne - pColors[pPolyhedron->pLines[counter].iPointIndices[0]], - pTransformedPoints[pPolyhedron->pLines[counter].iPointIndices[1]], vOne - pColors[pPolyhedron->pLines[counter].iPointIndices[1]], + Vector vPoints[2] = { pTransformedPoints[pPolyhedron->pLines[counter].iPointIndices[0]], pTransformedPoints[pPolyhedron->pLines[counter].iPointIndices[1]] }; + const float fShortenEnds = 0.0f; + if( fShortenEnds != 0.0f ) + { + Vector vDiff = vPoints[0] - vPoints[1]; + vDiff.NormalizeInPlace(); + vPoints[0] -= vDiff * fShortenEnds; + vPoints[1] += vDiff * fShortenEnds; + } + + DumpLineToGLView( vPoints[0], vOne - pColors[pPolyhedron->pLines[counter].iPointIndices[0]], + vPoints[1], vOne - pColors[pPolyhedron->pLines[counter].iPointIndices[1]], 0.1f, pFile ); }