pcbjam/tests/3d-regression/native/kicad_stubs_3d.cpp
Istvan Matejcsok ce1473c9ab test(3d): screenshot-baseline TDD suite for the 3D viewer OpenGL->WebGL port — 47 native goldens + red-state WebGL harness
tests/3d-regression mirrors the gal-regression pattern at renderer scale:
shared C++ scenarios call real KiCad 3D-viewer code (opengl_utils, display
lists + DrawCulled stencil subtraction, MODEL_3D VBOs, private generators via
a rob-template accessor, and full reload()+Redraw() composites over a
synthetic BOARD_ADAPTER). A native macOS harness renders them on real OpenGL
into 47 committed goldens (bit-deterministic, FBO capture); the wasm harness
compiles the same TUs against wasm/stubs/gl_ffp_stub.c no-ops so every
scenario renders blank — the TDD red state (parity meter: 47/47 changed).
Comparisons use the CI pixelmatch engine via the new generic compare-dirs.ts
(floors.json levels; manifest.json cmp-guards registry drift).

Documents an upstream bug: appendPostMachiningGeometry's countersink path
adds middle quads without normals, silently erasing the walls of any
display list it is batched into (3d-post-machining.png keeps the lists
separate to record it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:45:58 +02:00

303 lines
6.9 KiB
C++

/**
* Link stubs for the native 3D-renderer test build.
*
* The harness compiles real KiCad 3D-viewer TUs; the symbols those TUs
* reference from elsewhere in KiCad but only reach through dead branches
* (m_board / model-cache / app machinery is never populated here) are defined
* as safe no-ops. One stub per linker error, each annotated with the
* referencing TU. Seeded from tests/gal-regression/native/kicad_stubs.cpp.
*/
#include "kicad_stubs_3d.h"
#include <advanced_config.h>
#include <pgm_base.h>
#include <singleton.h>
#include <kicad_gl/gl_context_mgr.h>
// PGM_BASE holds unique_ptrs to these — complete types needed to define
// its ctor/dtor here.
#include <background_jobs_monitor.h>
#include <notifications_manager.h>
#include <settings/settings_manager.h>
#include <wx/snglinst.h>
#include <board.h>
#include <pad.h>
#include <pcb_track.h>
#include <libraries/library_manager.h>
#include <project_pcb.h>
#include "3d_cache/3d_cache.h"
//=============================================================================
// PGM_BASE / Pgm() — render_3d_opengl.cpp uses
// Pgm().GetGLContextManager()->RunWithoutCtxLock() during reload().
//=============================================================================
class PGM_BASE_TEST : public PGM_BASE
{
public:
PGM_BASE_TEST() { m_singleton.m_GLContextManager = new GL_CONTEXT_MANAGER(); }
void MacOpenFile( const wxString& ) override {}
};
PGM_BASE& Pgm()
{
static PGM_BASE_TEST* s_pgm = nullptr;
if( !s_pgm )
s_pgm = new PGM_BASE_TEST();
return *s_pgm;
}
// PGM_BASE out-of-line methods (pgm_base.cpp is not compiled) — same stub set
// as the GAL harness.
PGM_BASE::PGM_BASE()
{
}
PGM_BASE::~PGM_BASE()
{
}
wxApp& PGM_BASE::App()
{
static wxApp* app = nullptr;
return *app;
}
COMMON_SETTINGS* PGM_BASE::GetCommonSettings() const
{
return nullptr;
}
const wxString& PGM_BASE::GetExecutablePath() const
{
static wxString s;
return s;
}
ENV_VAR_MAP& PGM_BASE::GetLocalEnvVariables() const
{
static ENV_VAR_MAP map;
return map;
}
bool PGM_BASE::SetLanguage( wxString&, bool )
{
return false;
}
const wxString& PGM_BASE::GetTextEditor( bool )
{
static wxString s;
return s;
}
void PGM_BASE::SetTextEditor( const wxString& )
{
}
wxString PGM_BASE::GetLanguageTag()
{
return wxString();
}
void PGM_BASE::SetLanguagePath()
{
}
void PGM_BASE::ReadPdfBrowserInfos()
{
}
bool PGM_BASE::SetLocalEnvVariable( const wxString&, const wxString& )
{
return false;
}
void PGM_BASE::SetLocalEnvVariables()
{
}
void PGM_BASE::WritePdfBrowserInfos()
{
}
void PGM_BASE::SetLanguageIdentifier( int )
{
}
const wxString PGM_BASE::AskUserForPreferredEditor( const wxString& )
{
return wxString();
}
//=============================================================================
// ADVANCED_CFG singleton (advanced_config.cpp is not compiled)
//=============================================================================
const ADVANCED_CFG& ADVANCED_CFG::GetCfg()
{
static ADVANCED_CFG instance;
return instance;
}
ADVANCED_CFG::ADVANCED_CFG()
{
// Only fields the compiled TUs actually read; KiCad default DPI.
m_ScreenDPI = 91;
m_3DRT_BevelHeight_um = 30;
m_3DRT_BevelExtentFactor = 1.0 / 16.0;
}
//=============================================================================
// Profiling clock — deterministic zero for the test harness.
//=============================================================================
int64_t GetRunningMicroSecs()
{
return 0;
}
//=============================================================================
// Destructors of app-machinery members PGM_BASE owns (their real TUs would
// drag the whole settings/library world in; nothing here ever populates them).
//=============================================================================
KICAD_SINGLETON::~KICAD_SINGLETON()
{
delete m_GLContextManager;
m_GLContextManager = nullptr;
}
LIBRARY_MANAGER::~LIBRARY_MANAGER() = default;
SETTINGS_MANAGER::~SETTINGS_MANAGER() = default;
//=============================================================================
// STATUSBAR_REPORTER (reporter.cpp drags fontconfig; Redraw() takes REPORTER*
// but the harness always passes nullptr).
//=============================================================================
#include <reporter.h>
REPORTER& STATUSBAR_REPORTER::Report( const wxString&, SEVERITY )
{
return *this;
}
//=============================================================================
// BOARD / BOARD_ITEM / PAD / PCB_VIA — referenced from create_scene.cpp and
// render_3d_opengl.cpp branches that only run with a real BOARD loaded
// (m_board stays nullptr in this harness).
//=============================================================================
int BOARD::GetCopperLayerCount() const
{
return 2;
}
const EMBEDDED_FILES* BOARD::GetEmbeddedFiles() const
{
return nullptr;
}
const wxString BOARD::GetLayerName( PCB_LAYER_ID aLayer ) const
{
return LayerName( aLayer );
}
int BOARD_ITEM::GetMaxError() const
{
return ARC_HIGH_DEF;
}
bool PAD::TransformHoleToPolygon( SHAPE_POLY_SET&, int, int, ERROR_LOC ) const
{
return false;
}
int PCB_VIA::GetDrillValue() const
{
return 0;
}
void PCB_VIA::LayerPair( PCB_LAYER_ID* aTopLayer, PCB_LAYER_ID* aBottomLayer ) const
{
if( aTopLayer )
*aTopLayer = F_Cu;
if( aBottomLayer )
*aBottomLayer = B_Cu;
}
std::optional<int> PCB_VIA::GetSecondaryDrillSize() const
{
return std::nullopt;
}
std::optional<int> PCB_VIA::GetTertiaryDrillSize() const
{
return std::nullopt;
}
FILLING_MODE PCB_VIA::GetFillingMode() const
{
return static_cast<FILLING_MODE>( 0 );
}
CAPPING_MODE PCB_VIA::GetCappingMode() const
{
return static_cast<CAPPING_MODE>( 0 );
}
PLUGGING_MODE PCB_VIA::GetFrontPluggingMode() const
{
return static_cast<PLUGGING_MODE>( 0 );
}
PLUGGING_MODE PCB_VIA::GetBackPluggingMode() const
{
return static_cast<PLUGGING_MODE>( 0 );
}
COVERING_MODE PCB_VIA::GetFrontCoveringMode() const
{
return static_cast<COVERING_MODE>( 0 );
}
COVERING_MODE PCB_VIA::GetBackCoveringMode() const
{
return static_cast<COVERING_MODE>( 0 );
}
//=============================================================================
// Footprint-model loading chain (Load3dModelsIfNeeded is never called).
//=============================================================================
FOOTPRINT_LIBRARY_ADAPTER* PROJECT_PCB::FootprintLibAdapter( PROJECT* )
{
return nullptr;
}
wxString LIBRARY_MANAGER::GetFullURI( const LIBRARY_TABLE_ROW*, bool )
{
return wxString();
}
std::optional<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER_ADAPTER::GetRow( const wxString&,
LIBRARY_TABLE_SCOPE ) const
{
return std::nullopt;
}
S3DMODEL* S3D_CACHE::GetModel( const wxString&, const wxString&,
std::vector<const EMBEDDED_FILES*> )
{
return nullptr;
}