pcbjam/wasm/gl1/src/gl1_matrix.cpp
Istvan Matejcsok 071eef5454 feat(3d): gl1 shim M0-M2 — FFP-on-WebGL2 substrate + lighting; 12 scenarios truly green
wasm/gl1: the GL1.x fixed-function emulation layer replacing the
gl_ffp_stub.c no-ops in the 3d-regression harness link. This batch:

- symbol split: 52 FFP-only entry points implemented; 10 Emscripten-owned
  names intercepted via wasm-ld --wrap (sources.txt/wrapped_symbols.txt are
  the shared manifests for both link sites; production hookup lands in M7)
- matrix stacks (MODELVIEW/PROJECTION, glGetFloatv readback), immediate
  mode with all 8 GL1 primitive conversions, GL1-default state mirror
- full GL 1.5 Gouraud lighting uber-shader (eye-space light capture at
  glLightfv time, color-material, two-side, COMBINE evaluator + alpha test
  wired but inert until M3/M5)
- draw routing: FFP traffic identified by GL_VERTEX_ARRAY client state;
  blit/2D-GAL draws pass through untouched
- display lists: correct glGenLists/glIsList existing-empty semantics;
  recorder itself is M3 (recorded commands drop with a one-time warning)

Parity: 20/47 under the 0.02 floor, of which 12 verified genuinely
rendering (bg-gradient x2, bounding-box, half-open-cylinder, segment x2,
material x3, light x3 — eyeballed against baselines); the other 8 are
small-geometry scenarios whose missing GLU/display-list content sits under
the floor (become real in M3/M4).

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

106 lines
2.5 KiB
C++

/*
* gl1_matrix — GL_MODELVIEW / GL_PROJECTION matrix stacks.
*
* Only the operations the KiCad 3D renderer uses exist (no glOrtho/glFrustum/
* glMultMatrix — projection and view arrive prebuilt via glLoadMatrixf, and
* gluPerspective covers the gizmo). glGetFloatv(GL_MODELVIEW_MATRIX/
* GL_PROJECTION_MATRIX) readback is served from these stacks by the
* __wrap_glGetFloatv interceptor.
*/
#include "gl1_shim.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace gl1
{
// GL 1.5 minimums; KiCad never goes deeper than a few levels.
static constexpr size_t MV_STACK_MAX = 64;
static constexpr size_t PROJ_STACK_MAX = 8;
void matrixLoadIdentity()
{
S().currentTop() = glm::mat4( 1.0f );
S().matricesDirty = true;
}
void matrixLoadf( const GLfloat* m )
{
S().currentTop() = glm::make_mat4( m );
S().matricesDirty = true;
}
void matrixPush()
{
State& s = S();
auto& stack = s.currentStack();
const size_t maxDepth = ( s.matrixMode == GL_PROJECTION ) ? PROJ_STACK_MAX : MV_STACK_MAX;
if( stack.size() >= maxDepth )
{
GL1_WARN_ONCE( "glPushMatrix: stack overflow (mode 0x%x)", s.matrixMode );
return;
}
stack.push_back( stack.back() );
}
void matrixPop()
{
State& s = S();
auto& stack = s.currentStack();
if( stack.size() <= 1 )
{
GL1_WARN_ONCE( "glPopMatrix: stack underflow (mode 0x%x)", s.matrixMode );
return;
}
stack.pop_back();
s.matricesDirty = true;
}
void matrixTranslate( float x, float y, float z )
{
glm::mat4& top = S().currentTop();
top = glm::translate( top, glm::vec3( x, y, z ) );
S().matricesDirty = true;
}
void matrixRotate( float angleDeg, float x, float y, float z )
{
glm::mat4& top = S().currentTop();
top = glm::rotate( top, glm::radians( angleDeg ), glm::vec3( x, y, z ) );
S().matricesDirty = true;
}
void matrixScale( float x, float y, float z )
{
glm::mat4& top = S().currentTop();
top = glm::scale( top, glm::vec3( x, y, z ) );
S().matricesDirty = true;
}
void matrixPerspective( double fovyDeg, double aspect, double zNear, double zFar )
{
// gluPerspective multiplies onto the current matrix (KiCad calls it right
// after glLoadIdentity on GL_PROJECTION, but multiply is the GLU semantic).
glm::mat4 p = glm::perspective( glm::radians( (float) fovyDeg ), (float) aspect,
(float) zNear, (float) zFar );
glm::mat4& top = S().currentTop();
top = top * p;
S().matricesDirty = true;
}
} // namespace gl1