Releases: jac18281828/gman
Release list
GMAN 1.0.0
- gman path-traces:
gman -r gmanpathtracer scene.rib. A unidirectional path tracer with next-event estimation renders every primitive the ray tracer does, samples each surface's BSDF, and ends a path by Russian roulette from its third vertex.Option "pathtracer" "integer samples" [n]sets the paths per pixel. It renders on one thread. Anambientlightlights nothing under it and is skipped with one warning; a point, distant or spot light casts a shadow ray from every vertex. A camera path through a surface withOpacitybelow 1 passes through it as the ray tracer does, and a path that reaches the pass-through cap stays opaque - Area lights.
AreaLightSource "arealight"turns theSphereorDiskthat follows it into an emitter ofintensity×lightcolor, one-sided on its outward face. The path tracer samples an emitter uniformly by area, aims each shadow ray at a point offset off the emitter's surface so an opaque lamp shadows correctly at any scene scale, and places the sampling density and power through the emitter's own transform, soScaleon a lamp is exact. Next-event estimation picks a light in proportion to its power over distance squared. The z-buffer and the ray tracer render area-light geometry as ordinary surfaces and warn once - Multiple importance sampling. Next-event estimation and BSDF-sampled bounces that reach the same area light are weighted by the power heuristic at every vertex, so a sharp glossy lobe under a large light converges without fireflies. A point on an emitter's own surface never chooses that emitter for its shadow ray, since a lamp cannot light itself. An area light seen through glass counts once, carried by the shadow ray through the dielectric's thin transmittance; a glass lens focusing a lamp onto a floor waits for caustics
- The ray tracer adds bounce light through a radiosity pass:
Option "render" "string indirect" ["radiosity"]. Under-r gmanraytracer, the pass dices the scene into patches, solves the diffuse light bouncing between them by progressive refinement, through translucent surfaces attenuated by1 - Os, and hands each shaded point its indirect irradiance;ambient()adds it to the ambient lights, so each surface weights it by its ownKa, colour bleeding included. Reflected and refracted hits get it too.Option "radiosity" "float elementsize"sets the patch size, defaulting to an eighth of the scene's largest side; a size finer than the mesh's division cap allows warns once, prefixedRadiosity:, and dices at the finest resolution available. The plugin builds by default and installs aslibradiosity.so, so the release downloads carry it. Without the Option, no image changes - Every built-in shader returns a BSDF.
GMANSurfaceShader::bsdfreturns thegman::BSDFclosure the path tracer samples: Lambert ofCsby default; a GGX microfacet lobe beside the diffuse one forplastic,paintedplastic,metalandshinymetal; exact-Fresnel delta lobes formirrorandglass, whose shadow rays pass through glass by its thin transmittance.albedo()reports each shader's diffuse reflectance - 1.0 breaks the plugin ABI; every shader and renderer plugin rebuilds.
GMANLoadShadertakesGMANParameterList const&and constructs one shader instance perSurfacecall, andGMANDestroyShader(GMANShader*)is a required entry point, both declaredextern "C" GMAN_EXPORTingmanshader.h;computeCi/computeOiareconstand return colour by value;GMANSurfaceEnvgains the texture cache it shades through, one per worker.bsdfandalbedoare new virtuals onGMANSurfaceShader. An out-of-tree shader drops any mutable state and function-local statics, and may overridebsdf; one that overridesbsdfmust not call the basealbedo, since the two would recurse.GMANShader::set(GMANParameterList&),set(GMANRenderer&)and the protectedplandrenderermembers are gone;GMANLoadableShader's constructor takes(char const*, GMANParameterList const&)and its copy and move are deleted.GMANAttributes::setSurface,setDisplacement,setAtmosphere,setInterior,setExteriorandGMANOptions::setImagerlose theirGMANRenderer¶meter;GMANAttributes::getSurfacereturnsstd::shared_ptr<GMANSurfaceShader const>andgetSurfaceParametersis gone;GMANOptions::imagerModulebecomes astd::shared_ptr<GMANLoadableShader>, moving everyGMANOptionsmember after it.GMANSurfaceEnvgains a trailingindirectmember (GMANColor const*, null by default), whichambient(), inline in the installed header, adds;gman::shadegains two trailing parameters,TextureCache*thenGMANColor const* indirect, changing its mangled name.gmantexture.hinstalls as<gman/gmantexture.h>withgman::TextureCacheexported, so an out-of-tree renderer can build its own cache.GMANObjectManager's four polygon virtuals give way to one pure virtual,getRSPolygonMesh, over the exportedGMANPolygonMesh; an object manager outside this tree implements it.GMANQuantizebecomes a four-field aggregate (one,min,max,ditheramplitude). New exports:gman::IndirectPassandgman::loadIndirectPass(gmanindirectpass.h),gman::hitSurfacePointandgman::albedo(gmanshading.h),gman::resolveLoadableShader, andGMAN_EXPORTonGMANParametric; a pass module exportsGMANGetLoadableInfo,GMANCreateIndirectPassandGMANDestroyIndirectPassasextern "C".GMANPoint::operator*(GMANMatrix4 const&)andGMANVector::operator*(GMANMatrix4 const&), column-vector products this tree never used, are gone;gman::transformPointandgman::transformDirectionare the row-vector forms.gmanslapi.hstops declaringdistance,ptlinedandrotate, which were never defined gmanstops a file at any error reported atRIE_ERRORor above; 0.9.1 rendered on and exited 0.gmaninstalls its ownRtErrorHandler: it prints each report exactly asRiErrorPrintdoes, then atRIE_ERRORor above throws a stop that ends the file, so no image is written, the exit status is 1, and the run moves on to the next file on the command line. A scene that names a token neither declared nor built in, such asSurface "plastic" "Kdd" [0.2], printedERROR: RIE_BADTOKEN -- GMANDictionary: TOKEN_NOT_FOUND, dropped that parameter, rendered and exited 0; it prints the same line now and exits 1 with no image. A stop beforeWorldBeginleaves the next file free to render; a stop inside aWorldBeginblock leaves every later file failing withWARNING: RIE_ILLSTATE -- Invalid mode for procedureand writing nothing. An error the parser throws rather than reports keeps its behaviour, exit 1 at every severity with the same line. The library's own default handler is stillRiErrorPrint, so an embedder that installs none sees no change, andRiErrorHandlerinstalls a different policygman -r gmanradiosityno longer renders. The 0.9.1 radiosity renderer, which builtOFFand rendered nothing, is gone;libgmanradiosity.sobecomeslibradiosity.so, loaded as a pass by name. A pass that fails to load logs one warning beginningIndirect-light pass "radiosity":, and the frame renders without indirect light- A
Surfacethat will not load reportsRIE_NOSHADERatRIE_ERRORand names the request. 0.9.1 reported a missing module atRIE_SEVERE,SEVERE: RIE_SYSTEM -- Unable to open shared object specified by: libnosuchshader.so: ..., and a module with noGMANLoadShaderor a shader of the wrong type the same way. The report readsERROR: RIE_NOSHADER -- Surface "nosuchshader" failed to load; the default surface shades instead:followed by the loader's own message, and the request binds the default surface with none of the failed request's parameters. Undergmanthe report is a stop like any otherRIE_ERROR, so the exit is still 1 with no image; the default surface shades for an embedder whose handler returns,RiErrorPrintorRiErrorIgnore. Through the C API, aRiSurfaceafter a missing module used to keep the previously bound surface and binds the default now RiDisplacement,RiAtmosphere,RiInterior,RiExteriorandRiImagerresolve a module name asSurfacedoes. Each passed its token todlopenas given, soRiDisplacement("matte", ...)looked for a file namedmatte; a token maps tolib<name>.sonow. A caller that spelled the exact file,"libmatte.so", looks forliblibmatte.so.sonow and fails. A failed load or a wrong type reportsRIE_NOSHADERatRIE_ERRORin place ofRIE_SEVERE, names the request and what renders instead (the surface renders undisplaced,no atmosphere shades the volume,the volume shades without an interior,the volume shades without an exterior,the frame renders without an imager), and resets the kind's module and typed pointer to null; a wrong-type module used to stay assigned.GMANOptionsowns its imager module through ashared_ptr, so the copy of the optionsRiFrameBeginpushes no longer frees the outer options' imager when the frame loads its own. The RIB parser readsAtmosphere,DisplacementandImagerand ignores them, and has noInteriororExteriorrequest, so only the C API reaches this- Output honours
Quantize. RIB'sQuantizerequest reaches the renderer: every driver rounds and dithers each channel by RISpec's rule into the request's[min, max], scaled byone, and clamps it there; the dither is a pure function of the pixel, so a render reproduces. A TIFFDisplaywrites 16 bits a sample oncemaxexceeds 255; PNG, JPEG and PNM stay 8-bit, and a request they cannot honour falls back to the default range with one warning. The nine sample PNGs and ten goldens were regenerated for rounded, dithered output. A plugin output driver'swriteImagenow receives packed samples and their bit depth in place of float colours, andGMANColorRGBis gone - **The framebuffer holds unclamp...
GMAN 0.9.1
- The ray tracer draws
GeneralPolygon,PointsPolygonsandPointsGeneralPolygons, holes included. All three built nothing under-r gmanraytracerin 0.9.0. AGeneralPolygon's first loop is its outline and the rest are holes, each hole tested on its own ring by the even-odd rule, with no triangulation. A points mesh adds one primitive per request and oneGMANRayPolygonper face; the BVH flattens it face by face, so a large mesh culls per face and a hit reports the face. A face under three vertices or otherwise degenerate is skipped. Every ray-traced polygon resolvess/tthrough one rule,gman::resolvePointTexCoords: the object-space default, then"st", then"s"and"t" - The z-buffer dices a polygon by its size on screen. 0.9.0 diced every ear-clipped triangle of a
Polygon,GeneralPolygon,PointsPolygonsorPointsGeneralPolygonsinto a fixed 16-per-edge grid. Each triangle now dices into n = clamp(ceil(L / sqrt(ShadingRate)), 1, 16) per edge, L its longest raster-space edge; a non-positiveShadingRate, a non-finite edge or a corner behind the eye falls back to 16. No scene dices finer than before. On 10,000 small quads at 1600×900, a render drops from 3.53 s and 904 MB peak to 0.61 s and 288 MB - A redeclared
fovtakes effect. AfterDeclare "fov" "float",Projection "perspective" "fov" [30]rendered at 90° and warned "FOV not set", becauseRiWorldBeginlooked the token up in a dictionary other than the one that declared it. It resolves through the renderer's own dictionary now - Ray geometry, the BVH and the shadow walk move into libgman, and a plugin can use them.
GMANRayInterface, the eight ray primitives,GMANRayObjectManager,GMANRayBVHandGMANRayOccludercompiled only into the ray tracer plugin; they now live ingman_core, built whether or notGMAN_BUILD_RAYTRACERis on, and their headers install:gmanrayinterface.h,gmanraybbox.h,gmanraybvh.h,gmanrayobjectmanager.h,gmanrayoccluder.h,gmanraypolygon.h,gmanraypolygonmesh.hand one header per quadric.GMANRayBVH,GMANRayPolygonMeshand the occluder's helpers carryGMAN_EXPORT. The quadrics gaingetObjectToCamera(),GMANRayPolygongainsgetOuterLoop(),getHoles(),getPlaneNormal()andisDegenerate(), andGMANRayPolygonMeshgainsgetFaceCount()andgetFace(). Each class states a const, re-entrant contract for concurrent callers. No rendered image changes - Radiosity groundwork.
GMANRadiosityMeshdices every ray primitive into a camera-space element mesh, holes and mesh faces included, andGMANRadiositySolversolves diffuse interreflection over it by progressive refinement. Both live inrenderers/radiosity/, which still buildsOFFand renders nothing - The README is a front door. It opens on a ray-traced render of
samples/vase.rib, gives a four-line download-and-render for Linux, and shows six one-line edits to the scene beside their renders. The sample itself gains a robot on six wheels, water in the tipping vase and a lamp over the table
GMAN 0.9.0
- 0.9.0 breaks the plugin ABI again.
GMANMatrix4::p3m/p4mare nowconst, changing both symbols' mangled names.gman::shadetakes a trailinggman::Tracer const*defaulting tonullptr— a source caller that omits it still compiles, but the new mangled name means a plugin built against 0.8.x must rebuild before it links against this libgman.gman::Occluder::transmissionand the newgman::Tracer::tracetake a trailingRtFloat surfaceMagnitudewith no default, since a default argument on a virtual binds by static type rather than the override actually called: a plugin implementing either must add the parameter. A shader author rebuilds too:GMANSurfaceEnv::diffuse()andspecular(), inline in the installedinclude/gmanshaderenvironment.h, reachoccludedContribution, which now passessurfaceMagnitudetotransmission— a shader built against 0.8.3 calls it with one argument fewer.GMANSurfaceEnvgainstracerandsurfaceMagnitudemembers and thetrace()call that reaches them, andgman::SurfacePointgains its ownsurfaceMagnitude, whichgman::shadeforwards to the renderer's ownOccluderandTracer; left at 0 it reports no surface.gman::AppearanceandGMANAttributesgain a stored shader parameter list, andGMANSampleBufferandGMANOutputPolygon/GMANOutVertexgain a per-sample or per-pixelGMANAlphaalongside colour (GMANSampleBuffer::zTestAndSettakes a trailing alpha argument too, andGMANOutVertex's is per vertex), so a plugin touching either layout rebuilds.GMANBitmapgainsgetAlphaandsetAlpha, two new virtuals that shift the vtable slot of every virtual declared after them, so a plugin overriding one of those later virtuals rebuilds too. Separately,GMANAttributes,GMANWorldManager,GMANTorusand the light source, volume, displacement and imager shader bases now carryGMAN_EXPORT: a release build had hidden each one's constructor, destructor and typeinfo, so a plugin subclassing or copying one failed to link before and links now, with no plugin-side change needed.GMANPointcarries it too, for the out-of-line comparison operators it already had at 0.8.3;GMANBBox, for its out-of-line constructors andoperator=; andGMANColorSamples, for its copy constructor, destructor andoperator=— all hidden the same way - An existing scene renders differently under either renderer. The default background is black now, per the RenderMan spec, not opaque white: an uncovered pixel used to composite into white. A TIFF
Displaynaming"rgba"writes real per-pixel coverage alpha, premultiplied into RGB, and tags itEXTRASAMPLE_ASSOCALPHA; one naming"rgb"writes 3 TIFF samples instead of 4 regardless, matching what its ownDisplayModeasks for;libgman/gmanoutputpng.cppstill writes 255. A world block with noDisplay, or one explicitly naming"framebuffer", now raisesRIE_ILLSTATE, "No display output to render into.", atRiWorldEndinstead of silently rendering into a 0x0 framebuffer stub. A surface named more than once in a scene — restored byAttributeEnd, or falling back to the default surface on a primitive with noSurfacecall after a parameterized"matte"— now shades with its ownRiSurfaceparameters instead of the last call's, a bug both renderers carried sinceGMANLoadShaderreturns one static instance per plugin; under the ray tracer alone, the same bug showed on any two surfaces naming the same shader, since it shades every primitive only after everySurfacecall has run. APolygon,GeneralPolygon,PointsPolygonsorPointsGeneralPolygonsunder the z-buffer now dices into a fine facet grid before shading instead of Gouraud-blending a handful of once-shaded corners, textured or not: a textured one reads its texture correctly across the whole surface, and an untextured one under a point or spot light now resolves its highlight across the face instead of rendering one blend of corner shades. Measured on 10,000 quads under the z-buffer, dicing costs 0.08 s and 36 MB at 0.8.3 against 2.49 s and 741 MB now, about 31× the time and 21× the memory gmanno longer crashes running one file after another when the second has noWorldBegin, or whenRiEndfollows a failedRiBegin.RiEndleftviewingSystemand the renderer's own pointers dangling instead of null, so a file that rendered followed by one with noWorldBeginfreed the same pointer twice, andRiEndafter a failedRiBegindeleted an uninitialized renderer; every pointerRiEndreleases is null again once it returns- The ray tracer draws
Torusnow, and a same-zHyperboloidit used to skip. A full or partial torus intersects and shades under-r gmanraytracer; a spindle or horn torus (minorradius >= majorradius) still draws nothing, pinnedWILL_FAILbytorusspindle.Patch,PatchMesh,NuPatch,GeneralPolygon,PointsPolygons,PointsGeneralPolygons,Blobby,Points,CurvesandSubdivisionMeshstill build nothing. AHyperboloidwhose two points share a z, drawing nothing since 0.8.3, now intersects the flat annulus it sweeps. A textured ray-tracedPolygonalso interpolates its owns/tacross each hit now, instead of reading(0, 0)everywhere and rendering one flat texel per polygon - The ray tracer scales past a linear scan of every primitive. Each primitive now carries a padded, camera-space bound (
GMANBBoxgainsgetMin()/getMax()and a two-point constructor), and the renderer builds an object-median bounding volume hierarchy over them each frame instead of testing every primitive'sintersect()in turn; traversal visits the nearer child first and prunes a subtree whose interval clears neither the ray nor the best hit found so far. No rendered image changes mirrorandglassjoin the shader plugins, both using a newtrace()shading call for real reflection and refraction.GMANSurfaceEnv::trace(R)casts a ray from the shading point and shades whatever it hits, recursing up to 4 levels deep before returning the renderer's own background; it shades only under-r gmanraytracer, since only the ray tracer binds aTracer— under the default z-buffer it returns black, so both shaders render black there.mirrorscalesKrby what it finds along the reflected direction, andglassweights the reflected and refracted directions by the Fresnel reflectance and transmittance at a fixed index of refraction, 1.5. A shader callingtrace()must read every parameter before its first call and writeCionly after its last, since the call recurses back into shading the same loaded plugin instancefresnel()returns physical reflectance and transmittance now.GMANSurfaceEnv::fresnel(the exportedGMANFresnel) used the wrong tangent identity and the wrong sign for the incidence cosine, so it returnedkrNaN at normal incidence andkr127.6 withkt−126.6 at 45° for eta 1/1.5, dividing by zero at grazing incidence and taking the square root of a negative number at total internal reflection. Both overloads now clamp the incidence cosine and fix the identity and its sign: normal incidence returns 0.04, and 45° at eta 1/1.5 returns 0.050 with 0.950. An out-of-tree shader callingfresnel()renders differently- A ray-traced quadric or torus stays on its own surface far from the camera. The sphere, cone, cylinder, paraboloid and hyperboloid now solve their intersection in double from a shifted, near-the-surface origin instead of losing the discriminant to cancellation at distance; the disk and the flat-annulus
Hyperboloidhave no discriminant to cancel and solve in double from the unshifted origin instead. Every hit now lands within 1e-5 of its implicit surface at any distance tested
GMAN 0.8.3
- The ray tracer renders, builds by default and installs:
gman -r gmanraytracer scene.rib. It tracesSphere,Disk,Polygon,Cylinder,Cone,ParaboloidandHyperboloid; every other primitive,TorusandGeneralPolygonamong them, still builds nothing under it, and aHyperboloidwhose two points share a z draws nothing, pinnedWILL_FAIL. Each non-ambient light casts a shadow ray, anOpacitybelow 1 composites up to 16 layers front to back, and a shadow through a semi-transparent blocker attenuates by1 - Osat every surface it crosses.-r <name>chooses any renderer plugin, glued-rNAMEincluded; without itgmanrenders through the z-buffer as before.GMANViewingSystem::cameraRaynow scales its ray by the field of view, which it ignored at anyfovbut 90; the z-buffer never called it, so no z-buffer render changes - libgman grows the public API a renderer plugin shades through.
gmanshading.hinstallsgman::Appearance,gman::SurfacePoint,gman::Shadingandgman::shade, the shading entry point both renderers now call;gmanocclude.hinstallsgman::Occluder, which a renderer passes togman::shadeto shadow each light.gmanpolygon.hinstallsgman::newellNormalandgman::isDegeneratePolygon,gmanmatrix4.hgainsgman::transformPoint,transformDirectionandtransformNormal, andgmansamplebuffer.hgainsgman::sampleCentre.GMANNormal,GMANDisk,GMANCone,GMANCylinder,GMANParaboloidandGMANHyperboloidgainGMAN_EXPORT, so a plugin can link against them in a release build.GMANSurfaceEnvgains a trailingoccludermember; a shader built against 0.8.2's header ignores it until rebuilt - A parameter declared
integerreaches its request as anRtInt, not as a float's bits. The RIB parser stored every numeric parameter asRtFloatandGMANParameterListread an integer one back through anRtInt*cast, so[4]arrived as 1082130432. A float literal in a declared-integer slot is nowRIE_SYNTAX; an undeclared token reads as a float, as before - A RIB string decodes its escapes, so an escaped quote no longer closes it.
\n,\r,\t,\b,\f,\\,\", an octal escape and a backslash-newline decode; an unknown escape yields the character after the backslash. Before, the first quote after a backslash ended the string, and one inside an array desynced the rest of the file - A RIB integer that does not fit
RtIntraisesRIE_RANGEinstead of wrapping. The tokenizer bounds every integer literal where it reads it, so an out-of-range integer is an error even where a float is wanted:Kd [4294967297]now fails, and4294967297.0means the float. ADeclarearray size pastRtInt's range raisesRIE_RANGEwhere it used to abort onstd::bad_alloconce a request used the token. An inline declaration with a bracket after its seventh word raisesRIE_SYNTAXwhere it used to write past the parser's word array and return silently - A short array on
Polygonor a quadric clamps with a warning instead of reading past its allocation. Those eight requests passed no lengths to their parameter lists, soSphere 1 -1 1 360 "N" [0 0 1]overran a 12-float varying normal. They now take the lengths the RIB supplies, asGeneralPolygonand five others already did, zero-fill the shortfall and name the parameter and both lengths - Two installed declarations change.
gmancolor.h's twelveGMANConvert*colour-space conversions are gone; none carriedGMAN_EXPORT, so no release build could link them.GMANTokenstores its integer asRtIntand renamesgetLongInt()togetInt(). An unknown token type now reportsSEVERE: RIE_BUG -- Unknown token typerather thanRIE_NOMEM, whose code its transposed arguments had borrowed
GMAN 0.8.2
- 0.8.2 breaks the installed ABI, which CMakeLists.txt's own policy reserves for a minor release. A plugin calling a symbol
GMAN_EXPORTno longer tags moves to the tagged surface, or gman itself needs a local patch and rebuild to re-export it. A release build now hides everything the tag omits: measured withnm -gUon the release preset,libgman_coreexported 1604 symbols in 0.8.1 and 793 now, 29 classes tagged alongsideri.hand the shading-language, error and logging headers a shader calls. Hiding followsBUILD_TESTING, so a plaincmake -B buildconfigure still exports everything; any configure withBUILD_TESTINGoff is trimmed, the release preset and its tarballs among them - gman installs as a real CMake package for the first time. 0.8.1's
install(TARGETS)named no export set and skippedgman_shinymetaloutright, so an installedgmancould not loadlibshinymetal.so, and nogmanConfig.cmakelet a programmerfind_package(gman).gman_corenow installs through anEXPORT gmanTargetsset with a generatedgmanConfig.cmakeand aSameMinorVersiongmanConfigVersion.cmake: a consumer writesfind_package(gman CONFIG REQUIRED)and linksgman::gman_core.SameMinorVersiontreats every 0.8.x release as compatible; this one is not, since it trims the exported ABI - 16 headers move out of
include/and stop installing,gmanparallel.hamong them — the header 0.8.0 named as the one threading seam. No installed header includes any of the 16, so only a consumer that included one directly must change. The tree's own shaders adoptnamespace gmanshadertoo, asgmanshader::plasticand its siblings, the convention a new shader plugin now follows. Three diagnostics lose a stale class-name prefix:RiErrorHandlerreportsNo active contextrather thanGMANContext: No active context,invalid Context Handleloses the sameGMANContext:prefix, and an inline-declared parameter's syntax errors drop theirGMANInlineParse:prefix RiBeginon a name ending.ribnow writes RIB, instead of trying and failing to load it as a renderer.RiBegin(name)always built aGMANRenderManImpland tried todlopenlib<name>.so, soRiBegin("scene.rib")looked forlibscene.rib.so, and no scene ever reachedGMANASCII, the class that writes RIB. A name ending.ribnow loadslibgmanrib.soinstead, a plugin dlopened the way a renderer is; any other name,RI_NULLincluded, builds a renderer as before. A failed.ribload raises the load error and leaves no context current, so every later request raisesRIE_NOTSTARTEDuntilRiBeginorRiContextselects one.gman_rib, a plain shared library in 0.8.1 with nogman::target, is gone;RiBegin("x.rib")is the only way to reach the writerRtBooleanis a fixedshort, notboolin C++ orunsigned charin C, and every C++ signature carrying one changes its mangled name. A plugin subclassingGMANRenderManorGMANRenderManImpl, or callingGMANRIBParse::addHandler, must rebuild.RI_NULLwas the wrong width: a bare0passed through a variadicRi*call's...supplies anint-sized slot whereva_argreads a pointer, wrong on any LP64 platform;RI_NULLis now(RtToken)0, pointer-width. Assigning it to a handle type —RtLightHandle,RtObjectHandleorRtContextHandle— needs an explicit cast in C++, and warns without one in C.ri.h's#define NCOMPS 3and__GMAN_RI_Hinclude guard are both gone, the guard replaced by#pragma onceRiIlluminateandRiTransformEndare spelled correctly inri.h. Both were already defined under their correct names ingmancapi.cpp, butri.hdeclaredRiIllumnateandRiTranformEnd, so no program built against the header could link either call. Both misspellings are gone, not kept alongside the fixRiExposure's gain and gamma now correct before a pixel narrows to a byte, not after. Every driver'ssave()calledsetExposurefrom the same option and narrowed toGMANColorRGBfirst, then looked its byte value up in a table built bypow; a channel below 1/255 had already narrowed to 0, and the table's own zero entry meant no gamma could lift it.gman::gammaCorrectednow runs on the floatGMANColorbefore narrowing, in all four drivers. A render at the default gain 1, gamma 1 is bit-for-bit unchanged; gain above 1 can still overflow narrowing toGMANColorRGB, as it did in 0.8.1. Code callingGMANGammaCorrectorGMANOutput::setExposureloses both, deleted;gman::gammaCorrected(color, gain, gamma)in the installedgmangamma.his the replacement- A textured full sphere's texture turns half a turn, and a partial sweep starts at the spec's +x instead of -x.
GMANSphere::getLocationsubtracted half a turn before scalingthetabythetamax, the only one of the seven quadrics carrying that offset, sou=0landed opposite where the RISpec and every other quadric put it. ASpherewiththetamaxbelow 360 now draws its wedge half a turn from where 0.8.1 drew it; an untextured full sphere changes only where its tessellation crossed the old seam - A C-API program that passes a shader a parameter no longer crashes at exit, or at its next
RiBegin/RiEndcycle.RiEnddeletes the context'sGMANDictionary, but a shader plugin stays loaded for the process's life; its staticGMANShaderkept aGMANParameterListbuilt against that freed pointer, read again by the next cycle'sGMANShader::setor by exit. ThegmanCLI never reached this path; any C program that calledRiEndafter passing a shader a parameter did, at exit after a single render. Each list now owns its arrays behind aunique_ptrchosen at construction, so a throw partway through construction frees what it already allocated instead of leaking, and destroying a list never touches the dictionary again.operator=returnsGMANParameterList&, notconst GMANParameterList&, on this exported class
GMAN 0.8.1
- 0.8.0 shipped one tarball of three; this release ships all three. On the 0.8.0 tag push
softprops/action-gh-release@v2failed with "Error creating asset temp dir" onubuntu-24.04-armandmacos-latest, twice each — the small.sha256uploaded and the tarball beside it did not. The release workflow now creates the release and attaches every asset withgh, which ships on the runner image itself rather than arriving as a third-party action holdingcontents: write. The create step edits an existing release in place and the upload passes--clobber, so rerunning one partly-failed leg replaces that leg's assets instead of colliding with them - The documentation no longer cites a file that is not there. 36 references to
SPEC.mdacross 31 files pointed at a document deleted before 0.8.0 - Groundwork for the ray tracer.
include/gmanray.hgivesGMANRayan origin, a normalized direction and a[tmin, tmax]interval, andGMANHitthe record an intersection fills in;GMANViewingSystem::raytransforms a camera-space ray into world space. The ray-tracing plugin remains non-functional and builtOFF, and no rendered image changes
GMAN 0.8.0
- libtiff is now required.
GMAN_WITH_TIFFis gone, and configure fails without libtiff rather than building a renderer that cannot write its own default output. TIFF is theDisplaydefault and the format every texture is read from, so the option only ever chose between a working build and a crippled one - C++20 is the floor, and the 1999 pthread wrapper is gone. GCC 13 or Clang 17 with
<format>builds gman; an older compiler does not.gmanguard.h,gmanmutex.handgmanthread.hare deleted — nothing called them and two of their methods had empty bodies.gmanParallelForininclude/gmanparallel.his the one threading seam, andtests/threadcontainment_test.cppfails any other file that names a thread primitive - The PNG and JPEG drivers are optional, and their headers left
include/. Each driver compiles only where its library is present;GMAN_WITH_PNGandGMAN_WITH_JPEGforce the question and fail configure when the library is missing.gmanoutputpng.handgmanoutputjpeg.hare no longer installed.gman --versionnow lists the drivers a build carries, and aDisplaynaming one it lacks fails withRIE_BADFILEinstead of writing nothing - A plugin no longer declares a copyright.
GMANLoadableObjectInfolost itscopyrightfield, andGMANLoadable::getCopyrightandGMANLog::copyrightare gone: an out-of-tree plugin drops that initializer and rebuilds againstname,author,description.gmanopens with one line —gman 0.8.0 -- LGPL-2.1-or-later, see COPYING— where it printed four lines of license text through a format string that silently dropped both the Pixar notice and the version - Texturing.
MakeTextureturns a rendered image into a texture andtexture(name, s, t)samples it from a surface shader, honoring theswrapandtwrapmodes recorded when it was made. Polygons carry per-vertexsandt, parametric surfaces shade with their own parameterization, and a cache holds each texture's pixels for the render rather than re-reading the file per shade - Environment maps, and a
shinymetalshader that uses them.MakeLatLongEnvironmentbuilds a latitude-longitude map andenvironment(name, R)samples it along a reflection direction. The shader now receives the camera-to-world matrix that a world-space reflection needs;environment()is indexed in world space, per RISpec A.2.4 spotlight.LightSource "spotlight"joins ambient, distant and point lights, withconeangle,conedeltaangleandbeamdistributionin radians as the RISpec specifies — a scene asking for"coneangle" [30]asks for 30 radiansGeneralPolygonandPointsGeneralPolygonsrender, holes and all. Concave outlines triangulate by ear clipping, and each interior loop is bridged into the outline before triangulation. The classification is dimensionless, so a polygon renders the same at any scale or placementPointsPolygonsrenders. One shared vertex array, one vertex count per polygon, with per-vertex parameters indexed through itNuPatchrenders. A NURBS surface evaluates against its knot vectors and rasterizes with the rest;TrimCurveis parsed and stored, and trims nothing yet- The tree is clang-format clean and gated. Every source file is formatted to
.clang-format— 120 columns, left-bound pointers, includes grouped into standard, system, library and gman blocks — with clang-format 22.1.8, andformat-checkfails on any drift. A patch written against an earlier tree needs reformatting before it applies gman -lwrote one byte past its buffer. The log file name was sizedstrlen(rib) + 4where appending.logneeds five, and a RIB path shorter than four characters also read before the buffer. Setting the log file no longer deadlocks on the log's own lock- A malformed RIB no longer leaks memory. Every duplicated string and every pending parameter key is released when array conversion throws partway through, and a
Declarewhose array size overflows is rejected rather than truncated. A bitmap whose pixel count overflowsintis rejected at construction, and assigning one now copies both of its dimensions - The JPEG driver reports a file it cannot open. A failed
fopeninGMANOutputJPEG::savefell into an empty else branch, so the render reported success with nothing on disk; it now throws theRIE_SYSTEMerror the PNG and PNM drivers already threw
Known gaps. No shadows: MakeShadow is accepted and ignored, shadow() does not exist, and every light illuminates through geometry. No bump() — MakeBump is accepted and ignored too. Points and Curves parse and draw nothing. Rendering is still single-threaded: gmanParallelFor is the seam, and no renderer runs through it yet. The REYES, ray-tracing and radiosity plugins remain non-functional and built OFF. Shaders are C++ plugins; the shading-language compiler was never finished.
GMAN 0.7.1
- A malformed RIB could make the renderer read past the end of a heap allocation.
Patch "bilinear" "P" [-1 -1 0 1 -1 0]declares four vertices — twelve floats — and supplies six.GMANParameterListsized its destination from the declared class and copied that many elements from the supplied array, running off the end of the parser's buffer and rendering whatever happened to be next in memory as vertex coordinates. Short arrays are now clamped and zero-filled, with a warning naming the parameter and both lengths. All three copy paths are covered — float, integer and string — since each carried the identical unbounded loop - This affected 0.6.0 and 0.7.0. The defect predates
PatchMesh, where review found it: the trace above is a plainPatch. Anyone running an earlier release should upgrade. A renderer's whole job is parsing files it did not write, which makes an out-of-bounds read on malformed input a defect in the primary input path rather than an edge case - The parser already knew the length and threw it away.
parseParameterListrecorded each array's element count and discarded it. TheRi*Ventry points cannot carry it — those signatures are fixed by the RenderMan interface — so the count now travels beside the call rather than through it. Supplying no count still means "trust the caller", which keeps the public C API's contract intact: a program callingRiPatchVdirectly owns that memory, while a RIB file is untrusted - A zero basis step no longer divides by zero.
validBicubicMeshDim's nonperiodic branch computed(n - 4) % stepwith no guard onstep, where its periodic sibling rejectedstep < 1outright. Unreachable through RIB, which rejects a bad step earlier, and reachable throughGMANPatchPolyObjectManagerdirectly - The memcheck leg tests more than two files. Valgrind in CI ran
sphere.ribandcorpus/menger.riband nothing else, so noPatchMeshfixture and no malformed input ever reached a sanitizer there. That is why the overflow above survived review and a merge. A periodic bicubicPatchMeshnow runs under memcheck
GMAN 0.7.0
- Anti-aliasing.
PixelSamplesandPixelFilterwere parsed, stored and discarded; every render was one sample per pixel whatever the scene asked for. A per-sample buffer now sits behind the z-buffer renderer, and the pixel filter convolves over its support rather than the renderer averaging within a pixel. Every image the renderer produces changes. Samples land at(j+0.5)/nwithin a pixel — a truncating placement would have shifted every image a fraction of a pixel, which no surviving test would have caught Patchrenders, bilinear and bicubic. It had parsed and drawn nothing since 1999. Landing it exposed the defect that had kept it dark:GMANVector4::operator*=(const GMANMatrix4 &)sliced itself to a 3D temporary, transformed that, and threw it away, so a projective transform of a 4-component point silently did nothing. The header had documented that exact bug shape as already fixed for its sibling overloads. Bicubic also needed its tangents normalized before crossing them, or the surface normal scaled with the parameterizationPatchMeshrenders, bilinear and bicubic, periodic and non-periodic. Includes B-spline, Bezier, Catmull-Rom and Hermite bases throughRiBasis, and mixeduwrap/vwrap. Two arithmetic defects came out of it: the last sub-patch of a periodic mesh read one row past the grid instead of wrapping to the first, and a zero-step basis divided by zero before any control point was touched- Two of the five pixel filters were wrong, and nothing had ever called them.
RiTriangleFilterreturned nonzero — sometimes negative — weights outside its support, so a filter that should vanish at its edge instead subtracted light beyond it.RiGaussianFilterwas missing the RISpec's2/widthrescale of its argument, leaving the kernel at 61% of peak at the support edge where the reference sits at 13.5%. Both were found by evaluating the functions rather than by reading them. Box, Catmull-Rom and sinc are pinned as-found by tests rather than "fixed" - The golden-image tolerance is measured rather than guessed. It had accepted 24/255 per channel across up to 1% of pixels — values picked for one scene during the revival and adopted tree-wide. That window absorbed a real regression: reverting the perspective screen-window clip fix moved 732 of 120,000 pixels and the
shadersgolden still passed. Measuring the actual cross-platform spread found macOS matching every pixel exactly and Linux differing by at most six pixels in one scene, so the fraction tightened to 0.1% — about 180x headroom over the worst case observed. That same revert now turnsshadersred - The test suite carries labels, timeouts and a real skip. Every test declares
LABELSand aTIMEOUT; the JPEG driver test skips rather than fails where libjpeg is absent. 34 tests, up from 31 - Intel Mac binaries are gone from releases. The
macos-13leg of0.6.0sat queued for five hours without ever being assigned a runner and published six assets where eight were expected. The matrix is now the three legs that actually get runners: linux-x86_64, linux-arm64, macos-arm64. Building on an Intel Mac is still supported; nobody ships you a binary for it
Known gaps. No texturing — texture(), environment(), shadow() and bump() do not exist, though s/t reach the shader. No shadows: a light illuminates through geometry. No spotlight. NuPatch, curves and points parse but do not rasterize. Single-threaded — the 1999 pthread wrapper is still present, still uncalled, and two of its methods are empty bodies. The REYES, ray-tracing and radiosity plugins remain non-functional and built OFF. Shaders are C++ plugins; the shading-language compiler was never finished.
GMAN 0.6.0
- The first release in 24 years. GMAN was written between 1999 and 2002, shelved at 0.1.0, and resumed in 2026. Everything below is the revival arc; the entries under it are the original history, kept verbatim from the
ChangeLogthis file replaces - It renders. All seven RenderMan quadrics —
Sphere,Cone,Cylinder,Hyperboloid,Paraboloid,Torus,Disk— plus convexPolygon, shaded by matte, plastic or metal, lit byambientlight,distantlightandpointlight, written out as TIFF, PNG, JPEG or PNM. At the start of the revivalgman sphere.ribexited 1 withTOKEN_NOT_FOUNDand produced no file - Builds with CMake 3.25 presets and C++23. Autotools is gone, along with the Windows tree and the
GMANDLLmacro (nowGMAN_EXPORT, across 95 files). Three presets:devis RelWithDebInfo,debugadds ASan and UBSan,releaseis Release - The coordinate chain works end to end. Object → world → camera → screen → NDC → raster.
GMANMatrix4::prjPerspwas rewritten;RiWorldBeginhad shadowed the field of view with its default, and the output driver leaked on every frame - Array parameters reached the renderer for the first time.
fov [45]parsed, then was silently dropped, so every scene rendered at the default field of view whatever it asked for. This defect is why the revival ran phases 0 → 2 → 1 rather than in order - Transform composition order was backwards.
buildTransformaccumulatedCTM_old · Localfor a row-vector chain that requiresLocal · CTM_old, so an in-worldRotateorbited its primitive around the camera instead of the world origin — a sphere's silhouette centroid moved 99 → 192 px across 0 → 30° of rotation and left the frame entirely by 45°. Invisible to the whole test suite at the time, because every rendering fixture used onlyTranslate - Perspective clipping honors the screen window.
GMANPolygonClipper::cliptested perspective geometry against fixed ±1 side planes while computing the correct planes for orthographic. At 640×400 the default window is x ∈ [-1.6, 1.6], so only columns 120–520 of 640 ever drew and 37% of every wide frame was background - Convex
Polygonrasterizes.GMANPolygonhad been declared and wired to nothing. Landing it exposed two more defects it made observable:RiPolygonVsized its parameters wrongly, andGMANRIBParse::parsePolygonhardcoded a vertex count of zero - Primitives carry normals, and shading runs.
calcNormalis wired, backface culling is real rather than assumed, lights illuminate, and C++ surface shaders execute against a populated shading environment - Loading two shaders in one scene no longer aborts.
GMANLoadableShaderlet itsdlopenhandle refcount reach zero atAttributeEndand closed the module while a laterAttributeBeginstill referenced the address the loader recycled. Handles are now retained for the life of the process GMANQuantize::doColorstopped logging once per pixel. A 640×400 render emitted 256,000 identical warnings; it now warns once- The RIB front end covers a working subset of RISpec 3.2. Camera and transform setup, lights, surface shaders, array and inline-declared parameters,
facevaryingparameters, gzip'd streams andReadArchivewith cycle detection. Any request GMAN does not implement warns once, skips and keeps parsing, so an unsupported scene degrades instead of aborting — 23 requests take that path deliberately. Aqsis'menger.ribparses end to end - A real test suite and CI. Golden-image regression over a checked-in RIB corpus, unit tests for the matrix and space chain, and seven CI legs: macOS and Linux against both gcc and clang, an ASan/UBSan build, valgrind memcheck, and a clang-format check. The Linux legs caught what macOS clang cannot see — 26
deleteonnew[], an incompatible function type, ignored-qualifier casts and a 1,332-byte-per-context leak - The license position is settled. LGPL-2.1-or-later with SPDX identifiers across 220 files; the pre-2005 FSF address is corrected and "GNU GMAN Library" is retired — GMAN was never an accepted GNU package. The name is now "GMAN, a RenderMan-compatible renderer", which drops a trademark use that was the project's only real legal exposure
- Documentation describes the renderer that exists. The README is markdown and leads with what GMAN does;
CONTRIBUTING.mdreplaces a coding guide written in 2001;AGENTS.mdcovers the conventions and traps for an AI agent working the tree; the 1999 LaTeX manual no longer cites headers that never existed or a compiler from 1998.INSTALL,disclaimer.txtand the oldChangeLogare deleted
Known gaps. No texturing. No anti-aliasing — one sample per pixel, though PixelSamples and PixelFilter parse and five filter kernels are implemented. Patch, PatchMesh, NuPatch, curves and points parse but do not rasterize. The REYES, ray-tracing and radiosity renderer plugins are present, non-functional and built OFF by default. Shaders are C++ plugins; the shading-language compiler was never finished and is kept only as a record.