1329 lines
65 KiB
Text
1329 lines
65 KiB
Text
SINGE 3.00
|
|
==========
|
|
|
|
Unreleased
|
|
|
|
|
|
API Changes
|
|
-----------
|
|
|
|
- --gamedir names the directory holding the games, the .game files and
|
|
the Singe support folder, so the executable can live anywhere. It
|
|
becomes the working directory; relative names count from it. The
|
|
Menu.sh the engine writes there runs the executable by its full path.
|
|
|
|
- Without --datadir the data directory is now data/ in the game
|
|
directory (2.x wrote beside the game, which may be read only). The
|
|
menu's Menu.sh no longer needs to pass -d data.
|
|
|
|
- math.randomseed accepts fractional seeds (floored), so 2.x games that
|
|
seed with os.clock() products run under Lua 5.4.
|
|
|
|
- A script launched by name (or by its directory) runs with its games.dat
|
|
entry, as the menu would run it: Singe finds the games.dat in the
|
|
script's directory or up to three above, and applies the entry whose
|
|
SCRIPT names the script, LEGACY_SPRITE_ARGS included. Options typed on
|
|
the command line keep their values. Every game in a library therefore
|
|
behaves the same whether it was started from the menu or from a shell.
|
|
|
|
- The name now expands to "SINGE Is Not a Game Emulator", in the banner,
|
|
the manual, INSTALL, and the Windows file description.
|
|
|
|
- Framework.singe applies the legacy sprite argument order once even when
|
|
a game loads it more than once.
|
|
|
|
- Hypseus Singe extensions, so games written for it run unchanged:
|
|
spriteLoadFrames, spriteDrawFrame, spriteFrameWidth, spriteFrameHeight,
|
|
spriteResetColorKey, discAudioSuffix, singeSetQuitKeyEnabled (with the
|
|
Framework alias keyboardCatchQuit), vldpGetScale, vldpFocusArea,
|
|
vldpResetFocus, vldpSetMonochrome, overlaySetMonochrome,
|
|
vldpGetYUVPixel, controllerIsValid and controllerDoRumble. Sprite calls
|
|
take the handle first like the rest of 3.00; the LEGACY_SPRITE_ARGS
|
|
shim covers spriteDrawFrame and spriteResetColorKey.
|
|
|
|
- More Hypseus Singe names, almost all of them supplied by
|
|
Framework.singe over calls Singe already had: setOverlaySize and
|
|
setOverlayResolution over overlaySetResolution, rewriteStatus as a stub,
|
|
and the six music calls over the sound family. setOverlayLinearScale is
|
|
a real engine call: it smooths the overlay as the window scales it,
|
|
which is the default and what Singe has always done, or takes the
|
|
nearest pixel for crisp pixel art.
|
|
|
|
- The picture's place on screen is now one rectangle built from four
|
|
things in order: the window, --scalefactor (range widened from 50-100
|
|
to 25-100), --shiftx and --shifty (new; they move the picture inside the
|
|
room the scale left, -100 hard against one side and 100 against the
|
|
other, which is what Hypseus's own mean -- so at full scale there is no
|
|
room and a shift does nothing), and last the Sinden border, which is
|
|
therefore still a border whatever the shift and the scale did --
|
|
--scalefactor and --sindengun may now be used together. --rotate (new;
|
|
0, 90, 180 or 270 degrees)
|
|
turns the whole presentation, video, overlay, GUIs, 3D scene and
|
|
particles together, filling a portrait window on a quarter turn. Mouse
|
|
and light gun positions follow all of it, so a click lands where it is
|
|
drawn. Scripts move the scale and the rotation while the game runs with
|
|
the Hypseus extensions vldpSetScale, vldpSetRotate and vldpGetRotate.
|
|
|
|
- Bezels. --bezel names cabinet artwork in a bezels folder (--bezeldir
|
|
names another folder, --bezelflip draws the artwork in front of the
|
|
picture instead of behind it), and a Lua sidecar named after the
|
|
artwork declares the cutout the picture is drawn in, in the artwork's
|
|
own pixels: CUTOUT = { x = 200, y = 120, width = 800, height = 500 }.
|
|
With no cutout the picture keeps the window and --scalefactor,
|
|
--shiftx and --shifty place it, as before. A Sinden border goes to the
|
|
edge of the window when artwork is loaded, because the gun's camera
|
|
sees the whole screen; --sindenedge video|window overrides that.
|
|
Scripts ask mainBezelLoaded() which artwork they got and choose with
|
|
setOverlayOnTop whether the overlay sits above it.
|
|
|
|
- The arcade score panel behind the seven scoreBezel calls: credits, and
|
|
a score and a life count for two players, in a shipped RmlUi document
|
|
(Singe/scoreBezel.rml and Singe/scoreBezel.rcss) with a seven segment
|
|
look that a theme or a game may restyle. It is off until a game asks
|
|
for it with scoreBezelEnable, exactly as in Hypseus, and it sits in the
|
|
room a bezel's cutout leaves above or below the picture, or along the
|
|
bottom of the picture when there is none. Hypseus's second argument,
|
|
which chooses among its five scoreboards, is accepted and ignored.
|
|
guiDrawScreen puts a game's own GUIs out there beside it: it is
|
|
guiDraw with the rectangle measured across the whole window.
|
|
|
|
- A settings file. settings.cfg holds the options you would otherwise
|
|
type on every command line, in Lua like controls.cfg and found the same
|
|
four ways: the directory Singe was started in, above and inside the data
|
|
directory, and beside the game's script, with a later file winning key by
|
|
key. Its keys are the long option names without the dashes, so there is
|
|
no second vocabulary, and a switch takes true or false. A settings file
|
|
is a set of defaults: a games.dat entry for the game still overrides it
|
|
and the command line always does. Anything naming the game, naming a
|
|
directory, or acting before the file could be found (--framefile,
|
|
--gamedir, --datadir, --entry, --help, --apiversion, --noconsole, the
|
|
packing tools) belongs on the command line, and a key that is not an
|
|
option, is one of those, or carries a value the option will not take is
|
|
an error naming the file and the key. Singe/settings.cfg.example lists
|
|
every key with its default.
|
|
|
|
- Options a cabinet owner has been missing, none of them with a short
|
|
letter since the alphabet ran out: --screen picks the display (counting
|
|
from 1; a number too large lists the displays that are there),
|
|
--idleexit quits after so many seconds with nothing touched,
|
|
--startsilent starts muted until the first input, --monochrome starts
|
|
the disc picture in grey, --linearscale chooses the overlay's filter,
|
|
--nogamepad ignores the pads as --nomouse ignores the mice, --haptic
|
|
caps the rumble a game may ask for (0 turns it off), --keymapfile names
|
|
one controls.cfg instead of searching four places, --altaudio (and the
|
|
AUDIO_SUFFIX games.dat key) plays <base><suffix>.ogg beside the disc's
|
|
video for another language, --xratio and --yratio carry the gun
|
|
coordinate scales a Hypseus light gun game reads with ratioGetX and
|
|
ratioGetY, --fvalue carries one number to the game through getFValue,
|
|
and --apiversion prints one machine readable line for a front end and
|
|
exits. --nosound, which had been accepted and quietly ignored, now
|
|
actually mutes.
|
|
|
|
- Every controller Singe opens is now a gamepad, whatever it started life
|
|
as. SDL calls a device a gamepad only when it has a mapping for it, and
|
|
an arcade encoder board, a spinner, a wheel or a flight stick usually has
|
|
none, so such a device used to be invisible. Singe now writes a
|
|
straight-through mapping for it -- axes in order to the two sticks and
|
|
then the triggers, buttons in order to the standard names, the first hat
|
|
to the D-pad -- and opens it as an ordinary pad, so it reaches
|
|
controls.cfg, GAMEPAD_0 to GAMEPAD_3 and every controller call like any
|
|
other. A device with more controls than the gamepad model can name loses
|
|
the extras, and one whose buttons are not wired in that order will have
|
|
them in the wrong places: --program traces the mapping in SDL's own
|
|
format so it can be pasted into a gamecontrollerdb.txt and rearranged.
|
|
Singe now reads that file, from the four places it reads controls.cfg,
|
|
and it and SDL's own database both win over the mapping Singe would
|
|
write. --mapjoysticks=false turns the writing off.
|
|
|
|
- Input options for a cabinet: --gamepad_reorder says which physical pad
|
|
fills which slot (enumeration positions from 0, as Hypseus writes them),
|
|
--trigger_threshold says how far an analogue trigger must travel before
|
|
it counts as a button (a per cent of full travel, Hypseus's unit),
|
|
--joymouse lets the first pad's left stick drive the mouse cursor at
|
|
--js_range speed so a light gun game is playable on a stick-only
|
|
cabinet, --manymouse turns the many mouse mode on whatever the game asks
|
|
for so a two gun cabinet can be fixed from outside, and
|
|
--absolutes_only keeps only the devices that report an absolute
|
|
position, which is what a real gun does. controls.cfg gains DEAD_ZONES,
|
|
a dead zone for one axis at a time, and TRIGGER_THRESHOLD; both leave
|
|
every axis on DEAD_ZONE unless set, so nothing changes for anyone who
|
|
does not ask.
|
|
|
|
- SWITCH_MOUSE_DISCONNECT reaches onInputPressed when a mouse or light gun
|
|
is unplugged, so a gun game can say so instead of appearing to have
|
|
died, and joyMouseEnable, joyMouseIsEnabled, controllerGetName and
|
|
mouseGetName let a script drive and describe all of it.
|
|
|
|
- Alt-Enter switches between full screen and a window while the game runs,
|
|
as it does in Hypseus. Singe could not change the mode at all once it
|
|
had started.
|
|
|
|
- Subtitles. srtLoad reads a SubRip .srt and turns its timestamps into
|
|
disc frames at the disc's own frame rate, so a line is found again after
|
|
any search or skip; srtEnable shows them, srtPosition puts them where a
|
|
game wants (1 to 95 per cent down the picture) and srtClear takes one
|
|
off early. They are drawn through a shipped RmlUi document,
|
|
Singe/subtitle.rml with Singe/subtitle.rcss, both of which a game or a
|
|
theme may replace, over the picture and following it wherever the scale,
|
|
a shift, a bezel cutout or a rotation put it. A malformed file answers
|
|
false rather than dying. overlayBanner puts a message of its own up the
|
|
same way for about thirty frames.
|
|
|
|
- The disc picture gains three controls beside vldpSetMonochrome:
|
|
vldpFlash puts one white picture up the way a real player flashes when
|
|
it is searched, vldpSetBlend smooths the picture down its rows to take
|
|
the edges off an interlaced transfer, and vldpSetLuma brightens or
|
|
darkens it in eighths from half to one and a half without washing the
|
|
colour out. All three work on the decoded frame, cost nothing while
|
|
they are off, and follow a frame file from one segment to the next --
|
|
which vldpSetMonochrome now does too, and did not before.
|
|
|
|
- Sprites. spriteRotateFrame turns one frame of a sheet and
|
|
spriteDrawRotatedFrame draws it centred on a point, which is what
|
|
spriteDrawFrame will not do since it ignores rotation and scale by
|
|
design; spriteDrawGrid draws any rectangle of a sprite at its own size,
|
|
which Singe had no way to do at all; spriteGetFrames counts a sheet's
|
|
frames; and spriteLoadData and soundLoadData build a sprite or a sound
|
|
from bytes held in a Lua string, for a game that unpacks its own assets.
|
|
setOverlayOpacity fades the whole overlay and controllerHowMany counts
|
|
the open gamepads.
|
|
|
|
- Music has its own volume. The music family (musicLoad, musicPlay,
|
|
musicStop with a real fade, musicPause, musicResume, musicIsPlaying,
|
|
musicSetVolume, musicUnload) is now in the engine rather than shimmed
|
|
over the sound family, and carries a gain of its own on Hypseus's 0 to
|
|
128 scale: musicSetVolume moves no sound effect and soundSetVolume
|
|
moves no music. Music starts as loud as the sound effects, at whatever
|
|
--volume_nonvldp set. Ten more Hypseus names -- hypseusGetWidth,
|
|
hypseusGetHeight, takeScreenshot, mouseHowManyReal and the six
|
|
spriteAnim ones -- are aliases in Framework.singe.
|
|
|
|
- trace.txt now opens with a header a bug report can be built on: the
|
|
version, the whole command line, the operating system, the processor,
|
|
the renderer and GPU backend that were actually chosen, how video will
|
|
be decoded, which audio decoders the build carries, the SDL versions
|
|
built and linked against, the settings that were applied and the game. It sits between two rules so it can be
|
|
pasted whole.
|
|
|
|
- lfs.dir, lfs.attributes and lfs.symlinkattributes see inside a packed
|
|
game (the union of its loose directory, its data overlay and the
|
|
database), and lfs.mkdir and lfs.rmdir act on its data overlay, so
|
|
frameworks that list or create their own directories work packed.
|
|
|
|
- Physics. Any scene node can carry a rigid body on Jolt Physics:
|
|
static, kinematic (follows its node and pushes) or dynamic (drives its
|
|
node), shaped as a box, sphere, capsule, cylinder, a convex hull of the
|
|
node's mesh or the mesh's triangles; mass, friction, bounce,
|
|
velocities, forces and impulses; triggers; hinge, ball and slider
|
|
joints with limits; a raycast; onCollision and onTrigger callbacks.
|
|
The world steps at a fixed 60 Hz between animation and rendering and
|
|
pauses with the game. physicsSet2D keeps bodies in a plane for 2D
|
|
games, which need no GPU (nodes now exist on every machine, so a
|
|
Raspberry Pi 3 gets 2D physics without 3D). New calls: body*,
|
|
joint*, physics*, and the
|
|
BODY_*, SHAPE_* and JOINT_* constants; see the Physics chapter. Needs
|
|
SSE4.1 on x86 (2008 and later); the engine's first C++ (one wrapper
|
|
file and the library), statically linked, no runtime to install.
|
|
|
|
- Soft bodies. softNew makes cloth or a pressure body from a node's
|
|
mesh (particles from its welded vertices, Jolt's stretch, shear and
|
|
bend constraints, pressure for balloons) or a rope from the node to a
|
|
point with a tube mesh the engine makes; they collide with everything
|
|
rigid and the mesh is rewritten every frame. softPin holds particles
|
|
in place or to nodes; stiffness, mass, damping and pressure are
|
|
tunable. meshPlane takes subdivisions; nodeSetMaterial changes a
|
|
material without the mesh. New calls: soft*, and the SOFT_*
|
|
constants; see Soft bodies in the Physics chapter.
|
|
|
|
- Ragdolls. ragdollNew derives capsules and swing-twist joints from a
|
|
skinned model's skeleton; ragdollActivate hands the skeleton to
|
|
physics from its current pose and the mesh follows the bones as it
|
|
falls; motors (ragdollSetStrength) pull it back toward that pose;
|
|
ragdollApplyImpulse shoves a bone; ragdollDeactivate returns it to
|
|
animation. New calls: ragdoll*; see Ragdolls in the Physics chapter.
|
|
|
|
- Water. bodySetWater fills a static body with water: dynamic bodies
|
|
inside float, sink and drift by Jolt's buoyancy against its surface,
|
|
with drag and a current (bodySetCurrent, bodySetBuoyancy per body);
|
|
players swim in it (playerIsSwimming, playerSetSwim, three-axis
|
|
playerMove); a buoyant hull with vehicleNew(node, VEHICLE_BOAT) gets
|
|
a propeller and a rudder (vehicleSetThrust, vehicleSetRudder).
|
|
|
|
- Vehicles. A dynamic body becomes a car, a motorcycle or a tank on
|
|
Jolt's vehicle constraint: wheels are nodes the engine poses (spin,
|
|
steer, suspension), the script drives with throttle, steering, brake
|
|
and hand brake, and engine, gears, suspension, steering, brakes and
|
|
anti-roll bars are tunable. Motorcycles lean and balance with a spring
|
|
scaled to the chassis; tanks pivot on the spot. New calls: vehicle*,
|
|
and the VEHICLE_* constants; see Vehicles in the Physics chapter.
|
|
|
|
- Point lights inside geometry. A bulb in a closed room, under a shade
|
|
or inside a cabinet now shadows correctly: cube faces render depth
|
|
two-sided so closed meshes cast from inside, every shadow tap picks its
|
|
own cube face so the seams along the cube edges are gone, the depth
|
|
range fits the casters round the light instead of the scene centre,
|
|
the bias grows with distance and grazing angle, and each face draws
|
|
only what it can see. nodeSetShadow excuses a mesh from casting.
|
|
|
|
- Character controller. playerNew puts Jolt's CharacterVirtual on a
|
|
node, as a capsule or any convex bodyNew shape: it walks where
|
|
playerMove says, climbs steps, slides along walls, refuses steep
|
|
slopes, falls, jumps from the ground, rides kinematic platforms and
|
|
shoves light dynamic bodies; onCollision and onTrigger report it like
|
|
a body. Works in 2D worlds as a platformer character, with tolerances
|
|
scaled to the shape. New calls: player*; see Players in the Physics
|
|
chapter.
|
|
|
|
- Linear lighting. The scene is lit in linear light on a 16-bit float
|
|
target and brought to the screen by a post pass with an exposure
|
|
control (sceneSetExposure, in stops) and a tone curve (sceneSetTonemap:
|
|
TONEMAP_NEUTRAL, TONEMAP_ACES, TONEMAP_NONE). Colours given as 0-255
|
|
are sRGB and decoded on the way in; textures are sampled as sRGB;
|
|
bright light compresses instead of clipping. Specular highlights are
|
|
GGX with Fresnel, so roughness and metallic behave as they do in glTF
|
|
viewers. Existing scenes look a little different: the same numbers
|
|
now mean what they mean everywhere else.
|
|
|
|
- GUI. Menus, HUDs and option pages can be RmlUi documents: HTML-like
|
|
RML styled by CSS-like RCSS, with layout, text wrapping, nine-slice
|
|
panels from the game's own artwork, transitions, animations, form
|
|
controls and a focus model a gamepad can drive. guiNew(width, height)
|
|
makes a GUI rendered to its own texture every frame; guiLoad puts a
|
|
document in it (names through the vfs like spriteLoad, so packed games
|
|
work), with guiShow, guiHide and guiClose; guiDraw composites it over
|
|
the overlay in overlay coordinates (through the Sinden border and the
|
|
overscan scale like the overlay itself), and materialSetGui shows it
|
|
on a material in the scene, where sceneProbeGui gives the GUI and
|
|
pixel under an overlay point. guiSetInput routes the mouse and light
|
|
gun, keys, typed text (SDL text input runs while a field has focus)
|
|
and a pad's switches (directions as arrows, ACTION_1 as Return,
|
|
ACTION_2 as Escape) to a GUI; what an element uses never reaches
|
|
onInputPressed or onKeyPressed, and the rest falls through as before.
|
|
guiSetHandler calls a Lua function for an element's click, change or
|
|
submit; guiGetValue and guiSetValue read and write a control's value
|
|
or an element's inner RML; guiLoadFont adds a face for every GUI.
|
|
RmlUi's own Lua API runs on the game's state: rmlui.contexts["gui0"]
|
|
and its documents, a document's <script> blocks and on* attributes.
|
|
Singe/gui.rcss is the shipped theme (.panel, .list, .muted and the
|
|
styled form controls) and Singe/FreeSansBold.ttf the fallback face
|
|
(family FreeSans, bold). RmlUi (MIT) is added to LICENSES. Test
|
|
scenes scene36 to scene39 cover a document over the disc, a GUI on a
|
|
material, input and handlers, and the pointer on a 3D surface. See
|
|
the GUI chapter.
|
|
|
|
- GUI effects. Every RCSS effect now draws: rounded and transformed
|
|
elements clip their children to their shape (stencil clip masks),
|
|
filter: opacity() fades a group as one, mask-image cuts by an image's
|
|
alpha, box-shadow casts hard and soft shadows, filter and
|
|
backdrop-filter take blur, drop-shadow, brightness, contrast, invert,
|
|
grayscale, sepia, hue-rotate and saturate, and the linear-gradient,
|
|
radial-gradient and conic-gradient decorators draw (the theme's
|
|
panels, buttons and progress bars and the menu's list use them).
|
|
The custom shader decorator is refused, gradients carry at most
|
|
sixteen stops, effects nest at most eight layers deep. guiGetStats()
|
|
returns what the GUIs cost to render in the last frame (draw calls,
|
|
layers, filter passes, mask writes, layer bytes, passes). Test scenes
|
|
scene40 to scene43 cover clip masks, layers, filters and gradients.
|
|
|
|
- The Pi decodes video through its V4L2 memory-to-memory device (the
|
|
h264_v4l2m2m family of decoders) when the codec has one, falling back
|
|
to software when the device is missing, as on a Pi 5 for H.264. The
|
|
trace reports which decoder is in use and the format the device hands
|
|
back; --softwarevideo turns it off like the other hardware decoders.
|
|
|
|
- The Menu.sh and Menu.bat the engine writes now look beside themselves
|
|
for a file whose name starts with Singe and run the first one, the way
|
|
the launchers BatMaker writes already did, instead of naming the
|
|
executable. The executable's name carries its version, so a launcher
|
|
that named it stopped working at the next release, and the thing that
|
|
would have rewritten it was the launcher itself. With --gamedir the
|
|
executable is elsewhere and its full path is still written.
|
|
|
|
- Third-party libraries brought up to their current releases: ffmpeg
|
|
9.0.1 (from a 2023 development snapshot; libpostproc is gone with it),
|
|
OpenSSL 3.5.8 LTS (from a 2023 development snapshot), Lua 5.4.9, zlib
|
|
1.3.2, zstd 1.5.7, uthash 2.4.0, lsqlite3 0.9.7, luafilesystem 1.9.0,
|
|
copas 4.12.0 (its new copas.future module is embedded) and arg_parser
|
|
1.21. sqlite3.lversion() reports the real lsqlite3 version instead
|
|
of "unknown", and os.tmpname() returns a usable name again (the local
|
|
mkstemp edit never filled in its template). LICENSES now records the
|
|
version of every vendored library.
|
|
|
|
- The bundled menu is an RmlUi document (Singe/Menu.rml styled by
|
|
Singe/menu.rcss, filled and driven by Singe/Menu.singe): every game in
|
|
a scrolling list on the left, up and down to select, left and right to
|
|
page, Start or any button to play, and a mouse works too (click a row
|
|
to select it, click it again or the Start button to play, wheel to
|
|
scroll the description). The audio delay calibration screen is a page
|
|
of the same document. The previous overlay-drawn menu is kept for one
|
|
release as Singe/MenuClassic.singe for a machine without a GPU device,
|
|
where guiNew fails; name it in Menu.sh or Menu.bat to use it.
|
|
|
|
- Physics debug drawing. physicsSetDebug(DEBUG_*) draws the bodies in
|
|
wireframe (coloured by motion state), joints and their limits, contact
|
|
points and velocities as lines over the scene each frame, static shapes
|
|
too with DEBUG_STATIC; lineDraw draws a script's own world-space lines
|
|
for a frame, navDraw the baked navigation mesh.
|
|
|
|
- Compressed textures. KTX2 files (Basis Universal, from
|
|
util/compressTextures.py) stay compressed on the GPU in the block
|
|
format the machine has (BC7, ASTC, BC3, or plain RGBA where nothing
|
|
else exists), a quarter of the memory of PNGs or less. A model whose
|
|
file uses KHR_texture_basisu loads them on its own; materialSetTexture
|
|
and the map calls take a .ktx2 name in place of a sprite; sceneGetStats
|
|
reports the texture memory in use. The Sponza test scene went from
|
|
393 MB of textures to 99 MB.
|
|
|
|
- Hot reload. singe --reload (-R) watches the game's loose script files
|
|
and runs the game again from its script when one changes; F5 does the
|
|
same, and singeReload() from a script. The engine (window, GPU, disc,
|
|
controllers) stays; everything the script made is remade by the script.
|
|
|
|
- Terrain. meshHeightmap makes rolling ground from a greyscale image,
|
|
materialSetTiling repeats a texture across it, a SHAPE_MESH body on it
|
|
becomes a Jolt height field, and terrainGetHeight reads the ground
|
|
height at a point.
|
|
|
|
- Sprites and text in the scene. nodeSetSprite shows a loaded sprite on
|
|
a node as a lit quad (nodeSetSpriteFrame picks a frame), nodeSetText a
|
|
line of text in the selected font, and nodeSetBillboard turns any node
|
|
to face the camera squarely or about its own up axis (BILLBOARD_*).
|
|
|
|
- Navigation. navNew, navAddNode and navBuild bake a walkable mesh from
|
|
the scene's geometry on Recast; navPath, navNearest, navRaycast and
|
|
navRandomPoint query it on Detour; agents (navAgentNew, navAgentMoveTo,
|
|
navAgentStop, navAgentGetVelocity, navAgentIsArrived, navAgentSetPlayer)
|
|
walk it on DetourCrowd without walking through each other, moving their
|
|
nodes or driving a player controller, with onNavArrived when they get
|
|
there; navSave and navLoad keep a baked mesh. Recast Navigation is
|
|
vendored under thirdparty/recastnavigation (zlib licence).
|
|
|
|
- Bloom and views. sceneSetBloom makes everything brighter than a
|
|
threshold glow, blurred over a half-size chain before the tone curve.
|
|
viewNew renders a second camera to a texture every frame (viewSetCamera
|
|
points it) that materialSetView shows on any surface: monitors,
|
|
mirrors, portals. Each view turns billboards to its own camera and
|
|
draws transparency back to front from it.
|
|
|
|
- Particle extras. Trails (emitterSetTrail: a fading ribbon behind
|
|
each particle, 2D and 3D), collisions with a floor or, by ray cast,
|
|
the physics world (emitterSetCollide, COLLIDE_*), lit particles shaded
|
|
by the lights and sky (emitterSetLit) and soft particles that fade
|
|
where they meet geometry (emitterSetSoftness).
|
|
|
|
- Culling and instancing. Meshes outside the camera's view are skipped,
|
|
and copies of one mesh with one material draw as a single instanced
|
|
call (their matrices in a per-frame buffer the vertex shaders read), in
|
|
the shadow passes too. sceneGetStats reports the draws collected, the
|
|
draws in view and the draw calls made.
|
|
|
|
- Cascaded shadows. A directional light's shadow is split into up to
|
|
four maps along the view (sceneSetShadowCascades, three by default),
|
|
each fitted to a slice of the camera's view out to
|
|
sceneSetShadowDistance and snapped to its texels, so shadows in a
|
|
large level are sharp up close and steady as the camera moves.
|
|
|
|
- Sky, environment light and fog. sceneSetSky wraps the scene in an
|
|
equirectangular panorama (Radiance .hdr, PNG or JPEG) drawn behind
|
|
everything and lighting it: its diffuse light replaces the ambient and
|
|
its reflections show on metals and glossy surfaces, blurred by
|
|
roughness (sceneSetEnvironment switches that off, sceneSetSkyIntensity
|
|
scales it). sceneSetFog fades meshes and particles toward a colour
|
|
between two distances.
|
|
|
|
- Positional sound. A playing channel can follow a scene node
|
|
(soundSetNode) or sit at a point (soundSetPosition) and is then panned
|
|
and faded by distance (soundSetRange) as heard from the camera or a
|
|
chosen node (soundSetListener); soundGetPosition reports where it lies;
|
|
soundSetPan pans a channel for 2D games; soundPlay takes a loop count.
|
|
|
|
- Animation blending. animationPlay takes a fade in seconds and
|
|
crossfades from the clip that was playing, or from the pose the nodes
|
|
are in when nothing was (so a ragdoll eases back up); up to four clips
|
|
play on layers above the base (animationPlayLayer), each with a weight
|
|
(animationSetLayerWeight) and an optional mask limiting it to part of
|
|
the skeleton (animationSetLayerMask); animationStop takes a layer.
|
|
|
|
- Material maps. A material can carry a normal map, an occlusion map, a
|
|
metallic-roughness map (glTF's packing) and an emissive map beside its
|
|
base texture, and a loaded model brings its own. Every mesh has
|
|
tangents: a model's when the file carries them, computed from the UVs
|
|
otherwise. New calls: materialSetNormalMap, materialSetOcclusionMap,
|
|
materialSetMetallicRoughnessMap, materialSetEmissiveMap.
|
|
|
|
- Mipmaps. Every scene texture carries a full mipmap chain generated on
|
|
the GPU at load and is sampled with anisotropic filtering, so textures
|
|
seen small or at a grazing angle no longer sparkle or moire.
|
|
materialSetFilter(material, FILTER_NEAREST) keeps pixel art crisp.
|
|
|
|
- Particles. Emitters spawn, move and age particles from a recipe (rate,
|
|
life, speed, direction and spread, gravity, drag, size, colour and spin
|
|
over life, a birth radius, a pool cap) in the overlay and in the 3D
|
|
scene alike. A 2D emitter draws on the GPU above or beneath the
|
|
overlay when the script asks (emitterDraw); a 3D emitter sits on a node
|
|
and draws camera-facing billboards after every mesh, depth tested, in
|
|
world units. Any loaded sprite is the picture (a random frame per
|
|
particle from an animated one); without one, a soft disc. Normal or
|
|
additive blending. New calls: emitter*, and the PARTICLE_* constants;
|
|
see the Particles chapter.
|
|
|
|
- 3D scenes. A game can draw a 3D scene between the disc video and the
|
|
overlay: primitive meshes and script-built geometry, materials with
|
|
colour, textures from sprites, the disc or a loaded video, metallic and
|
|
roughness, up to eight lights each able to cast shadows (cube maps for
|
|
point lights), any node as the camera, and glTF 2.0
|
|
models (.glb, self-contained) with node animation, skinning and morph
|
|
targets, placed any number of times. Everything is a node in one tree. New calls:
|
|
scene*, node*, mesh*, material*, light*, camera*, model*, animation*,
|
|
and the LIGHT_* constants; see the 3D Scenes chapter of the manual.
|
|
Needs a GPU with Vulkan, Direct3D 12 or Metal; the Raspberry Pi 4 is
|
|
the minimum Pi (the Pi 3 has no Vulkan driver). 2D games run as
|
|
before without one. The renderer now runs on
|
|
SDL's GPU device where there is one.
|
|
|
|
- A game can be one file: singe --pack DIRECTORY GAME.game writes the game's
|
|
scripts, art, sounds, fonts, and video into an SQLite database, and
|
|
singe GAME.game (or the menu, which lists every .game file beside the game
|
|
directories) runs it. Nothing in a game has to change to be packed:
|
|
every name a script uses (dofile, require, io.open and friends,
|
|
spriteLoad, soundLoad, fontLoad, videoLoad, framefiles, controls.cfg)
|
|
goes through one lookup that tries a loose directory named like the
|
|
database, then the game's data directory, then the database, so
|
|
packed games can be modded by dropping files beside them and files a
|
|
game writes land under data/<game>/files. Inside a database names
|
|
are matched without regard to case and a leading own-directory prefix
|
|
("DLe/Cfg/x") is ignored. singe --unpack GAME.game DIRECTORY restores
|
|
the files and singe --patch GAME.game SOURCE replaces files from a
|
|
directory or a patch database in one transaction. --entry=N picks
|
|
which games.dat entry a .game launch runs. Loose games are
|
|
untouched by all of this. Scripts get SQLite for their own data with
|
|
require("sqlite3"). The archive installer (.game, .tool, and .patch
|
|
archives dropped beside the binary) is gone along with libarchive; a
|
|
game is a .game file or a directory, copied into place.
|
|
|
|
- Video is decoded by libavcodec directly; FFMS2 is gone. Seeking is still
|
|
frame exact, indexing is faster, and the index files in the data
|
|
directory use a new format (old ones are rebuilt automatically).
|
|
Multi-channel audio is downmixed to stereo instead of refused. Indexing
|
|
is a few seconds at most, so the indexing screen and its artwork are gone.
|
|
Video is
|
|
decoded by the platform's hardware decoder when there is one (VA-API or
|
|
VDPAU on Linux, D3D11VA on Windows, VideoToolbox on macOS) and falls
|
|
back to software otherwise; --softwarevideo forces software.
|
|
|
|
- Singe is built on SDL3 (SDL 3.4, SDL3_image, SDL3_mixer 3.2, SDL3_ttf)
|
|
instead of SDL2. Nothing changes for scripts: the same functions,
|
|
constants, scancodes, and controls.cfg names. SDL2_gfx is gone; sprite
|
|
and video rotation and scaling are done by the engine itself.
|
|
|
|
- The sprite handle is now the FIRST argument of spriteDraw(), spriteLoop(),
|
|
spriteQuality(), spriteRotate(), spriteRotateAndScale(), spriteScale(), and
|
|
spriteSetFrame(), matching the video*() functions. Move the last argument
|
|
of each call to the front. Games that cannot be edited can set
|
|
SINGE_LEGACY_SPRITE_ARGS = true before loading the framework, or add
|
|
LEGACY_SPRITE_ARGS = true to their games.dat entry. See "Migrating from
|
|
Singe 2.10" in the manual.
|
|
|
|
- Singe is no longer laserdisc only. A games.dat entry with a VIDEO is a
|
|
laserdisc game; one with the line missing or blank runs without a
|
|
disc, on a canvas sized by CANVAS_X/CANVAS_Y (default 720x480), the
|
|
disc functions become harmless, and discGetState() returns the new
|
|
DISC_EJECTED. On the command line --disc (implied by --framefile)
|
|
declares the disc, since a video found beside the script no longer
|
|
does; --canvas=WxH sizes the canvas. Existing games.dat files need no
|
|
change. Scripts can test SINGE_DISC.
|
|
|
|
- mouseSetEnabled(bool) and singeSetPauseKeyEnabled(bool) added. The old
|
|
mouseEnable/mouseDisable and singeEnablePauseKey/singeDisablePauseKey are
|
|
now aliases defined in Framework.singe.
|
|
|
|
- Handles, counts, and frame numbers are returned as Lua integers.
|
|
videoIsPlaying() returns a boolean. mouseSetMode() returns nothing.
|
|
|
|
- onControllerMoved() receives the controller index (0 to 3), not SDL's
|
|
instance ID. onMouseMoved() in MANY_MOUSE mode receives real relative
|
|
motion in its third and fourth arguments.
|
|
|
|
- Runtime errors inside callbacks now end the game with a traceback, the way
|
|
argument errors always have.
|
|
|
|
- Held keys no longer auto-repeat in MODE_NORMAL.
|
|
|
|
- The engine parks the disc on frame 1 before the script runs; the framework
|
|
no longer seeks when it is loaded, so threaded games keep their own
|
|
startup positioning.
|
|
|
|
- The pause key now pauses the whole game. The engine freezes the script
|
|
(no callbacks, no singeMain), holds sound completions, releases and later
|
|
re-presses held inputs, and draws a PAUSED indicator. It acts on the key
|
|
press, and SWITCH_PAUSE reaches the script only when the key is disabled
|
|
with singeSetPauseKeyEnabled(false). singeSetPauseFlag() still pauses
|
|
media without freezing the script. Gamepad and mouse buttons mapped to
|
|
pause, quit, screenshot, or grab now work in MODE_FULL too; keyboard
|
|
mappings stay raw there.
|
|
|
|
- Video decoding runs on its own thread per video, so a discSearch() far
|
|
from a keyframe no longer stalls the game loop; the previous frame stays
|
|
on screen until the new one is ready. The laserdisc and framefile videos
|
|
are handed to the GPU as YUV and converted there instead of on the CPU,
|
|
and each video reports its keyframe spacing to the program trace when
|
|
loaded, with a warning there when seeks may be slow.
|
|
|
|
- Constants that used to be duplicated in Framework.singe (SWITCH_*,
|
|
FONT_QUALITY_*, MODE_*, MOUSE_*, OVERLAY_*, RENDER_*, SOUND_ERROR_*) are
|
|
now defined by the engine. New: DISC_STOPPED/DISC_PLAYING/DISC_PAUSED
|
|
for discGetState(), SINGE_VERSION_MAJOR/MINOR/STRING, and the SINGE_*
|
|
input code layout that Framework.singe builds GAMEPAD_N and MOUSE_N from.
|
|
|
|
- Audio and video are kept in sync from the audio clock. The engine
|
|
measures the audio device's queue at startup instead of guessing, uses
|
|
small mixer buffers, and honours a container whose first video frame is
|
|
timestamped after its first audio sample. For delay the engine cannot
|
|
see (receivers, Bluetooth, DACs) there is singeSetAudioDelay(ms) /
|
|
singeGetAudioDelay(), the games.dat key AUDIO_DELAY, and
|
|
--audiodelay=MS; positive values mean the audio is heard late.
|
|
|
|
- The menu has an audio delay calibration screen for the machine's own
|
|
delay (DAC, receiver, Bluetooth, display). Press the service key on
|
|
the game list; a click plays once a second and the screen flashes.
|
|
Adjust with the stick until the flash and the click coincide, then
|
|
press button 1 to save. The value is kept in audio.cfg in the data
|
|
root and applied to every game on the machine, from the menu or the
|
|
command line, on top of any per-game AUDIO_DELAY. Scripts can read and
|
|
set it with singeGetAudioCalibration() / singeSetAudioCalibration(),
|
|
read the measured device queue with singeGetAudioLatency(), and now
|
|
have singeGetTicks() for a wall clock in milliseconds (os.clock() is
|
|
processor time and drifts).
|
|
- Inside a packed game a relative name with a ".." component resolves
|
|
nowhere: io.open returns nil and "NAME reaches outside the game",
|
|
io.lines, io.input and io.output raise it, lfs.mkdir and lfs.rmdir
|
|
return nil with it, and asset loads report the file as missing. The
|
|
packer already refused such names; the lookup now enforces the rule at
|
|
run time. lfs.dir on a packed directory lists its entries sorted
|
|
without regard to case.
|
|
|
|
- One rule for the data directory on every launch (command line,
|
|
games.dat, scriptPush and scriptExecute): with --datadir, or for a
|
|
packed game, a directory named for the game under the base; otherwise
|
|
the game's own directory. scriptPush of a loose game without --datadir
|
|
used to write under the current directory.
|
|
|
|
- Color components handed to lightSetColor, materialSetColor (alpha
|
|
too), materialSetEmissive, lineDraw and navDraw are clamped to 0..255,
|
|
as colorForeground and colorBackground always were; they used to wrap.
|
|
|
|
- materialDelete on the private material of a sprite or text node is an
|
|
error ("belongs to a sprite node; clear the sprite instead") instead
|
|
of freeing a slot the node still used.
|
|
|
|
- lightSetCone raises the outer angle to at least the inner angle.
|
|
|
|
- Packed games: the data directory is always <base>/<database name>/
|
|
<script directory>/. An archive whose script sat in a subdirectory
|
|
(Game/Main.singe) wrote to <base>/Game/, shared with every archive of
|
|
that layout; its saves now belong under <base>/<name>/Game/ and must be
|
|
moved by hand. Archives whose script sits at the root are unaffected.
|
|
|
|
- Packed games: a path naming the .game file itself (Games/DLe.game,
|
|
with or without a trailing separator) addresses the database root, as
|
|
"." does from inside: lfs.dir lists it and lfs.attributes reports a
|
|
directory, while io.open on it returns nil and lfs.mkdir and lfs.rmdir
|
|
refuse it. Engine-owned names (Singe/ and the data directory) may use
|
|
".." as long as they stay below that prefix; any other ".." inside a
|
|
packed game is still refused.
|
|
|
|
- lfs.attributes inside a packed game answers as the real lfs does: the
|
|
whole table (mode, size, modification and the rest) with one argument,
|
|
one field by name (an unknown name raises "invalid attribute name"),
|
|
or a table of the caller's filled in; a missing file returns nil, a
|
|
message and the error number.
|
|
|
|
- sceneProject returns false with 0, 0, 0 before the scene has rendered
|
|
a frame, instead of inf and nan.
|
|
|
|
- softNew removes the node's existing soft body before it looks at the
|
|
mesh, so a call that fails leaves the node with none; softNew on a
|
|
node carrying a rope fails with "needs a mesh" (the tube was the
|
|
engine's) instead of reading freed memory.
|
|
|
|
- navAgentNew keeps the radius and height at 0.01 or more, as navNew
|
|
does, so onNavArrived fires for an agent given a radius of zero.
|
|
|
|
- Three Hypseus names the bytecode-only games turned out to need.
|
|
controllerSetPadding(boolean) and controllerGetPadding() are accepted;
|
|
the getter always answers 0, because Hypseus's offset marks a switch
|
|
code as having come from a gamepad and Singe's mapped switches carry no
|
|
such mark. videoGetVolume() with no argument answers the laserdisc's
|
|
volume on Hypseus's 0 to 64 scale, which its games double into
|
|
musicSetVolume. require("md5") gives the PUC-Rio md5 library Hypseus
|
|
bundles, all five of sum, hexsum, exor, crypt and decrypt, so a game
|
|
written against it behaves the same here; note that library names its
|
|
sumhexa hexsum.
|
|
|
|
- The ARM64 build carries the V4L2 stateless decoders: AV1, H.264,
|
|
HEVC, MPEG-2, VP8 and VP9. These reach the hardware on the boards
|
|
whose kernel drivers use the V4L2 Request API rather than the older
|
|
memory-to-memory one -- Allwinner's Cedrus, Rockchip's rkvdec and
|
|
hantro, the Pi 5's rpivid, i.MX8's hantro -- none of which the engine
|
|
could use before. Singe tries them first on ARM, then Rockchip's MPP,
|
|
then stateful V4L2, and decodes in software when a board offers none.
|
|
Desktop platforms are untouched. The support is Jonas Karlman's
|
|
V4L2 Request patchset for FFmpeg, carried in thirdparty/ffmpeg; it
|
|
brings a runtime dependency on libudev, which these boards all have.
|
|
|
|
- The Raspberry Pi build target is now linux-aarch64 and its binary is
|
|
Singe-v3.00-Linux-aarch64. Nothing in it was ever Pi specific: the
|
|
V4L2 decoder it builds talks to any memory-to-memory device, so the
|
|
same binary serves Amlogic, Exynos, Qualcomm and other ARM64 Linux
|
|
boards whose kernel offers one. The build system now decides by
|
|
architecture, and by whether a sysroot is in use, rather than by a
|
|
platform name that stood for both. cmake/zig/piPackages.cmake is
|
|
cmake/zig/arm64Packages.cmake, and the command is
|
|
./build-all.sh linux aarch64.
|
|
|
|
- src/main.c now includes windows.h where it reads the processor name
|
|
out of the registry, which the Windows build had been failing without.
|
|
|
|
- A hardware decoded frame stays NV12 the whole way to the texture
|
|
instead of being converted to planar YUV on the CPU first. The picture
|
|
is identical: the GPU does the same conversion when it samples either
|
|
way. Measured on an RX 7900 XTX at 720x480 the difference was inside
|
|
the noise, so this is for the small ARM machines where a per-frame
|
|
conversion is worth more than it is on a desktop; it has not been
|
|
measured on one.
|
|
|
|
- Rockchip's rkmpp decoders are built and used when the Rockchip Media
|
|
Process Platform library is present, and ignored everywhere else. The
|
|
superbuild probes for rockchip_mpp with pkg-config rather than asking
|
|
for --enable-rkmpp unconditionally, which would fail every configure on
|
|
a machine without it. Untested: no Rockchip hardware here.
|
|
|
|
- The manual now says plainly that the Hypseus API is for compatibility
|
|
and not for new games, in the Lua API Reference introduction and again
|
|
at the head of the Hypseus Compatibility section. Every Hypseus name,
|
|
and every Hypseus argument form on a function that otherwise takes
|
|
Singe's own, says so in its own entry.
|
|
|
|
- videoSetVolume(volume), with one argument instead of three, sets the
|
|
laserdisc's volume on Hypseus's 0 to 64 scale, the twin of the bare
|
|
videoGetVolume(). A level outside that range is ignored rather than
|
|
fatal, as in Hypseus.
|
|
|
|
- drawTransparent(boolean), getUserString(), mouseGetPadding() and
|
|
spriteBlend(boolean, sprite) are registered, completing the set of
|
|
names Hypseus's engine exposes. All four are compatibility answers:
|
|
Singe carries transparency in the alpha channel rather than a colour
|
|
key, sends no statistics and so has no machine ID to report, adds no
|
|
offset to mouse input codes, and never has blending switched off.
|
|
|
|
- allowSocketCall() and setOverlayFullAlpha() are registered so a game
|
|
that calls them does not stop on a missing function. The first always
|
|
answers false: Hypseus reports whether the user let it send data to a
|
|
stats server, and Singe sends nothing anywhere. The second is accepted
|
|
and ignored: it asks for an overlay that blends its alpha channel
|
|
rather than thresholding it, which Singe's overlay has always done.
|
|
|
|
- The other Lua modules Hypseus exposes are here too, whether or not a
|
|
game asks for them: require("bit") is LuaBitOp, require("random") a
|
|
Mersenne Twister generator, require("cjson") Lua CJSON and
|
|
require("lpeg") LPeg. Singe already had the rest of Hypseus's list
|
|
(lfs, socket, rs232) and opens every standard Lua library besides.
|
|
|
|
- materialSetCutoff(material, cutoff) discards the texels whose base
|
|
colour alpha falls below the cutoff, in the lit pass and in the shadow
|
|
pass alike, so cutout foliage casts cutout shadows. What survives the
|
|
cutoff is drawn fully opaque, which is what glTF means by MASK as
|
|
against BLEND: the sampled alpha is not passed on, so nothing behind
|
|
the scene shows through a leaf. glTF's MASK alpha mode now sets the
|
|
cutoff from the file's alphaCutoff instead of warning that the material
|
|
would be drawn opaque; BLEND and OPAQUE are unchanged. Materials that
|
|
never set a cutoff keep drawing every texel.
|
|
|
|
- --deterministic[=MS] runs the engine on a virtual clock stepped MS
|
|
milliseconds (default 15) once a frame instead of on real time, steps
|
|
the disc one video frame a frame with it, and seeds Lua's generator
|
|
and the engine's from the same number, so a screenshot taken at a
|
|
fixed frame number is byte identical between runs. It is a testing
|
|
facility: audio and pacing are meaningless in it. Settable as
|
|
deterministic in settings.cfg. With the option absent nothing
|
|
changes.
|
|
|
|
- Every image format the bundled FFmpeg can decode now loads, not only
|
|
the ones SDL_image reads: TIFF, OpenEXR, JPEG 2000, DDS, PSD, DPX,
|
|
and AVIF and HEIC through a vendored dav1d. spriteLoad,
|
|
spriteLoadData, spriteLoadFrames, meshHeightmap, --bezel artwork,
|
|
glTF embedded textures and the GUI's images all take the same path:
|
|
SDL_image first, FFmpeg for whatever it turns down. sceneSetSky
|
|
gains the same fallback and keeps a floating point source's range, so
|
|
an OpenEXR sky is lit correctly rather than clipped.
|
|
|
|
- AV1 video plays. FFmpeg's own AV1 decoder is hardware only and said
|
|
"Your platform doesn't support hardware accelerated AV1 decoding" on
|
|
any machine without an AV1 capable GPU; the vendored dav1d is the
|
|
software decoder, and it is what brings AVIF with it.
|
|
|
|
- Every audio format the bundled FFmpeg can decode now loads as a sound
|
|
or as music: AAC, ALAC, AC-3, WMA, APE, TTA, Speex and AMR among
|
|
them. An AAC track used to play when it was the audio of a video and
|
|
fail when it was a sound effect. Chiptunes arrive with a vendored
|
|
game-music-emu (NSF, NSFE, SPC, VGM, GBS, AY, GYM, HES, KSS and
|
|
SAP), and the tracker
|
|
formats libxmp was already reading -- MOD, S3M, XM, IT and fifty-odd
|
|
others -- are now documented rather than merely present.
|
|
|
|
- MIDI files play, synthesised with TinySoundFont. Singe ships no
|
|
sound bank, because a good one is tens of megabytes for a format
|
|
almost no game uses: name one with --soundfont FILE, put one at
|
|
Singe/soundfont.sf2 so a packed game carries its own, or let it find
|
|
the one your distribution installed. The trace header says which was
|
|
used. SDL_mixer's own MIDI decoder wants a GUS patch set almost
|
|
nobody has, so a .mid failed to load while the trace claimed a MIDI
|
|
decoder was there.
|
|
|
|
- MIDI ports, in and out, for a cabinet driving a real sound module or
|
|
reading a keyboard or a control surface as an input device:
|
|
midiInputCount, midiInputName, midiOpenInput, midiCloseInput,
|
|
midiIsInputOpen and the same five for output; midiSend for any
|
|
message at all; midiNoteOn, midiNoteOff, midiProgramChange,
|
|
midiControlChange and midiPitchBend; midiRescan for a device plugged
|
|
in while the game runs; and an onMidiMessage(status, data1, data2,
|
|
bytes) callback. Channels are 1 to 16. ALSA's sequencer on Linux,
|
|
CoreMIDI on macOS, the multimedia MIDI calls on Windows. On Linux
|
|
the library is opened at run time, as SDL opens the same one for
|
|
audio, so a machine without it simply has no ports.
|
|
|
|
- spriteLoad(name, width, height) rasterises a vector picture -- an SVG
|
|
-- to fit that box, keeping its proportions, instead of taking the
|
|
file's own size, which for an icon is usually 16 or 24 pixels. Every
|
|
other format ignores the size.
|
|
|
|
- --deinterlace off|auto|on, automatic by default, for a laserdisc rip
|
|
that kept its interlaced fields and combs on a progressive display.
|
|
Automatic touches only the frames a file marks interlaced, so a
|
|
progressive disc pays nothing; on is for a file whose flags are
|
|
wrong. Settable as deinterlace in settings.cfg.
|
|
|
|
- Fonts gain what FreeType was built without: WOFF and WOFF2, colour
|
|
bitmap glyphs, colour vector glyphs, and complex script shaping
|
|
through HarfBuzz, so Arabic, Hebrew and the Indic scripts join and
|
|
order correctly instead of coming out as unjoined letters.
|
|
|
|
- A framefile opens its segments as the disc reaches them instead of
|
|
opening every one at startup. A long framefile -- typing-md2 has 213
|
|
segments -- held a demuxer open for each, which cost hundreds of file
|
|
descriptors and could push a game past the 1024 that glibc's select
|
|
refuses to look past, aborting it inside a fortify check. At most
|
|
four segments are open now: the one playing, the one after it (opened
|
|
ahead so the changeover costs nothing), and room for a seek to move
|
|
between two. Measured on a twelve-segment game, 75 open descriptors
|
|
became 24. A framefile naming a file that does not exist still fails
|
|
at startup, as it always has.
|
|
|
|
- Subtitles carried inside the video file: discGetSubtitleTracks(),
|
|
discGetSubtitleLanguage(track) and srtLoadTrack(track), which reads
|
|
one track out of the disc's own container and loads it exactly as
|
|
srtLoad loads a .srt beside the game. SubRip, WebVTT, ASS and MOV
|
|
text are read, with ASS override tags stripped; picture subtitles
|
|
(VobSub, PGS) are counted so the numbering matches other players, but
|
|
cannot be read, because there are no words in them.
|
|
|
|
|
|
|
|
Fixes
|
|
-----
|
|
|
|
- A lone .m2v played with --framefile now finds the .ogg beside it, as a
|
|
framefile's segments always have. A Daphne era elementary stream
|
|
carries no audio of its own, so lair.m2v played silently unless it was
|
|
listed in a framefile or --altaudio was given. Any other container
|
|
keeps using the audio inside it.
|
|
|
|
- A single-frame image saved as a 1-bit PNG (many overlay assets from
|
|
the 2.x era) loads again; spriteLoad reported "Blit combination not
|
|
supported" on it. Images with 16 bits per channel or floating point
|
|
pixels (16-bit PNGs among them) load as sprites, animation frames and
|
|
particle textures; they ended the program.
|
|
|
|
- lfs.dir(".") and other names for the root work inside a packed game;
|
|
they were refused as if the directory did not exist.
|
|
|
|
- --sindengun and --scalefactor are listed in --help and the manual;
|
|
they were hidden because mouse positions ignored the Sinden border and
|
|
the overscan shrink. Positions now map into the picture actually
|
|
drawn, so a light gun lands where it points inside the border. The
|
|
border is drawn the right way round (black ring outside, white ring
|
|
inside, the game in the middle); one too wide for the video ends the
|
|
program with a message instead of a zero-sized game; and light guns
|
|
and tablets reporting absolute positions through ManyMouse are mapped
|
|
into the picture like the mouse.
|
|
|
|
- The manual's 3D Scenes, Physics and Particles chapters are rewritten as
|
|
sections in a learning order with worked examples, every reference
|
|
entry now shows each form the call accepts with a realistic example,
|
|
and prose spelling is American throughout. Manual.pdf and Manual.html
|
|
are rendered into .builddir/ by the build and by build-docs.sh alike,
|
|
and the binary embeds the PDF from there.
|
|
|
|
- controllerGetButton() never worked; it rejected every framework button
|
|
code.
|
|
|
|
- The five argument form of spriteDraw() always failed.
|
|
|
|
- spriteSetFrame() on a still image crashed.
|
|
|
|
- Unloading the selected font left later fontPrint() calls using freed
|
|
memory.
|
|
|
|
- overlayCircle() drew around the origin instead of the requested center.
|
|
|
|
- overlaySetResolution() broke mouse coordinates and vldpGetPixel() because
|
|
the overlay scale was computed with integer division.
|
|
|
|
- mouseGetPosition() always returned 0,0 and controllerGetAxis() read the
|
|
wrong controller because the axis cache was indexed two different ways.
|
|
|
|
- Analog stick release events fired with a stale input code. Controllers
|
|
that were unplugged and replugged shifted their codes.
|
|
|
|
- The -o/--audio option was lost when the configuration was copied.
|
|
|
|
- --option=value forms of numeric options, and options after the script
|
|
name, were not accepted.
|
|
|
|
- Running "singe game.singe" from the game's own directory wrote its data
|
|
to the filesystem root.
|
|
|
|
- Framefile playback never advanced past the first segment, and switching
|
|
segments dropped the selected audio track.
|
|
|
|
- Video timing accumulated main loop latency, drifting behind the audio.
|
|
Audio is now the master clock: the picture follows the samples the sound
|
|
device has actually consumed, so the two cannot drift apart and the
|
|
mixer's buffering latency is accounted for. Silent videos follow the wall
|
|
clock. The first frame after a seek was shown one update late.
|
|
|
|
- Video without an audio track crashed on the first update.
|
|
|
|
- The video audio stream was shared with the mixer thread without a lock.
|
|
|
|
- onSoundCompleted was called from the audio thread. It is now delivered
|
|
from the game loop.
|
|
|
|
- Screenshots read the frame buffer through the window surface, which SDL
|
|
forbids alongside a renderer, and could corrupt memory on shutdown.
|
|
|
|
- .patch archives crashed the installer. Archives without an explicit top
|
|
level directory entry were rejected. Extraction now refuses paths that
|
|
escape the installation directory.
|
|
|
|
- The Sinden gun arguments could overflow the configuration structure.
|
|
|
|
- Support files in the Singe directory are refreshed when the installed
|
|
copy differs from the running build. Upgrading the binary used to keep
|
|
the old Framework.singe and Menu.singe forever.
|
|
|
|
- The menu background video is encoded with a keyframe every second, so
|
|
the menu's jump to the game list is instant.
|
|
|
|
- Framefiles whose first line is a relative directory produced garbage
|
|
video paths (the directory string was freed before use); a lone "." now
|
|
means the framefile's own directory.
|
|
|
|
- A crash prints a backtrace to the console (Linux and macOS) so a report
|
|
can say where it happened; run with --program and send trace.txt too.
|
|
|
|
- Mouse positions are delivered in the video's coordinates again (SDL3
|
|
stopped scaling them for us), so crosshairs follow the pointer.
|
|
|
|
- os.clock() in scripts now returns wall time rather than processor time.
|
|
Nearly every existing game times its input debounces and prompts with
|
|
it; once the GPU decodes the video the engine mostly sleeps, and those
|
|
timers ran seconds slow. A game that wanted processor time is not
|
|
known to exist. This diverges from stock Lua and from other Singe
|
|
runtimes; new code that must be portable should use singeGetTicks().
|
|
|
|
- A key or button still held when a game exits no longer counts as a
|
|
press in the script that follows. Confirming Exit in a game with
|
|
button 1 used to relaunch it from the menu.
|
|
|
|
- Audio ran late by the device queue depth on most Linux audio servers:
|
|
the clock assumed one mixer buffer of latency where PulseAudio and
|
|
PipeWire hold several. See the audio sync entry above.
|
|
|
|
- Windows builds now carry the icon and version resource, with the version
|
|
number encoded correctly.
|
|
|
|
- The build no longer depends on lyx, upx, or xxd; embedded resources, the
|
|
version header, and the manual are generated by CMake. The manual is
|
|
embedded again and extracted as Singe/Manual.pdf.
|
|
|
|
- One command builds Singe from a clean checkout on a Debian based
|
|
system: ./build-all.sh <platform> <arch> installs the host packages,
|
|
then CMake fetches zig (the compiler for every target), the Pi's
|
|
platform packages and the macOS SDK into .builddir/toolchains and
|
|
builds every library and Singe itself. The results are a Linux binary
|
|
that runs on glibc 2.28 or newer, a Windows 10 or newer binary, a
|
|
64-bit Raspberry Pi OS (bullseye or newer) binary and a macOS 13 or
|
|
newer universal (Apple silicon and Intel) binary. The toolchains repository is no longer
|
|
needed. See INSTALL.
|
|
- On a plain video disc, discSearch, discSkipToFrame, discSkipForward,
|
|
discSkipBackward, discStepForward and discStepBackward clamp at the
|
|
first and last frame instead of wrapping around the disc.
|
|
discStepForward and discStepBackward are ignored while the disc is
|
|
stopped, like the skips; they used to move the frame behind the blue
|
|
screen. On a framefile the skips apply to the frame number
|
|
discGetFrame reports, so a skip in any segment after the first lands
|
|
where it should.
|
|
|
|
- Every collision and trigger event of a frame reaches onCollision and
|
|
onTrigger; only the first 64 used to, the rest were dropped.
|
|
onTrigger fires once per body and trigger pair even when a mesh or
|
|
compound shape touches in several places. playerJump is granted
|
|
whenever the player is on the ground.
|
|
|
|
- playerSetEnabled(node, false) removes the player's body from the
|
|
world and reports it leaving every trigger it stood in; enabling puts
|
|
it back. navPath ends a partial path at the last reachable point.
|
|
navLoad rejects data that is not a Detour mesh, or a file shorter
|
|
than the mesh its header describes ("the navigation mesh file is
|
|
truncated or damaged"). The world holds at most 4096 bodies; bodyNew
|
|
raises an error when it is full.
|
|
|
|
- A body, player, ragdoll, soft body or vehicle whose node was deleted
|
|
is released at the start of the next frame, disabled, inactive, static
|
|
or paused or not; a disabled player or an inactive ragdoll on a deleted
|
|
node kept its slot (16 players, 8 ragdolls) forever. playerExists,
|
|
softExists, ragdollExists, vehicleExists and every setter answer false
|
|
for a node handle that was deleted and reused instead of driving the
|
|
previous owner's record. bodySetTrigger(node, false) no longer
|
|
swallows the leave of a trigger the node is standing in.
|
|
physicsSetDebug's mask is cleared by a reload.
|
|
|
|
- Sprite nodes are no longer culled early at the edge of the view, and
|
|
shadow fitting sees their real size. Soft particles fade correctly in
|
|
a scene with no meshes instead of against stale depth. Point-light
|
|
shadows update when a caster's mesh is rewritten (soft bodies), given
|
|
morph targets, or swapped with nodeSetMesh at an unchanged transform;
|
|
the six cached faces could freeze in the old shape. Cascaded sun
|
|
shadows appear in render-to-texture views (viewNew) instead of
|
|
vanishing; a view takes the finest cascade that covers each point.
|
|
Debug lines no longer pile up while the scene layer is enabled but has
|
|
no target.
|
|
|
|
- modelLoad refuses a skin with more than 128 joints or a skinned mesh
|
|
naming a joint numbered 128 or more (the shader used to read past the
|
|
matrices); a joint beyond the skins that use the mesh is traced and the
|
|
load goes on, so a mesh shared by skins of different sizes loads. A
|
|
texture that cannot be decoded, or a missing KTX2 image, is traced and
|
|
the load goes on without it instead of leaving modelLastError set.
|
|
KHR_materials_emissive_strength is applied. Sparse accessors load,
|
|
animation keyframes included, so Blender shape keys are no longer
|
|
silently zero; a sampler that cannot be read is traced and skipped,
|
|
and a rotation channel with a malformed output no longer produces NaN
|
|
transforms. Alpha MASK materials, textures on TEXCOORD_1 and up, and
|
|
KHR_texture_transform are traced as unsupported.
|
|
|
|
- After a script reload, or any stall longer than a tenth of a second,
|
|
model animations advance by at most a tenth of a second instead of the
|
|
whole gap; a non-looping clip used to be finished before its first
|
|
frame. A 3D emitter whose node is deleted is removed even when the
|
|
handle is reused by a later nodeNew; it used to jump to the new node.
|
|
|
|
- Video index files are named <name>-<hash>.index, the hash from the
|
|
video's full name, so two videos with one base name in a game stop
|
|
rebuilding each other's index. Old <name>.index files are ignored and
|
|
can be deleted.
|
|
|
|
- The packer reports every forbidden file in a directory rather than
|
|
stopping at the first. --unpack refuses a database whose stored names
|
|
would land outside the target directory. Launching a .game database
|
|
from the command line no longer crashes.
|
|
|
|
- --stretch hands the script mouse positions in the game's coordinates
|
|
at any window size. --scalefactor with --sindengun shrinks the picture
|
|
inside the Sinden border instead of being cancelled by it. A negative
|
|
--audio track number is ignored. With no --datadir the controls.cfg
|
|
beside the script is read once, not twice.
|
|
|
|
- A reload (F5, --reload, singeReload) ignores keys and buttons held
|
|
across it until they are released, resets the effect channels and their
|
|
pending onSoundCompleted queue, the held switches and the 2D particle
|
|
textures, restores the effects volume, mouse mode, mouse enabled state
|
|
and mouse capture, parks the disc on frame 1 paused, and no longer
|
|
calls into the closed Lua state when it happens while paused, so the
|
|
new script starts exactly as a fresh launch does. Under --reload a
|
|
script that fails to compile stays watched, so saving the fix triggers
|
|
the reload.
|
|
|
|
- Quitting with a stick past its dead zone no longer crashes; the
|
|
release reaches onInputReleased after onShutdown, before the state
|
|
closes. Plugging in a gamepad no longer resets the positions
|
|
mouseGetPosition reports, and a controls.cfg mapping that lists the
|
|
same code twice fires its switch once.
|
|
|
|
- materialSetVideo(material) on a framefile disc keeps showing the disc
|
|
after a segment change.
|
|
|
|
- In a packed game the controls.cfg at the root of the data directory
|
|
loads again, io.open(".") returns nil with a message instead of a
|
|
handle whose first read fails, and a directory listing where a loose
|
|
file and a packed one differ only in case shows the loose spelling.
|
|
|
|
- Error messages: an invalid view, navigation mesh or navigation agent
|
|
handle reports "No <thing> N." like every other handle; navAgentNew,
|
|
vehicleSetGears and viewNew state their limits from the engine's
|
|
constants; fontLoad and soundLoad of a missing file say "Unable to open
|
|
NAME" instead of the library's "invalid parameter"; navDraw reports its
|
|
argument count, and running out of memory in it is an error rather than
|
|
a silent blank; scriptExecute and scriptPush that cannot create the new
|
|
data directory abort the script with a message instead of exiting.
|
|
materialNew, sceneGetSize and sceneGetStats no longer reject extra
|
|
arguments.
|
|
|
|
|
|
|
|
SINGE 2.10
|
|
==========
|
|
|
|
Released March 26th, 2024
|
|
|
|
|
|
New Features
|
|
------------
|
|
|
|
- spriteDraw() now has two more forms. In addition to being able to draw
|
|
regular sprites and stretched sprites it can now draw both using the
|
|
sprite's center as the anchor instead of the upper left. This is highly
|
|
useful when dealing with rotated sprites.
|
|
|
|
- Animated sprites! Both animated GIF and WEBP images are supported.
|
|
|
|
- Sprite anti-aliasing, scaling and rotation! Optional separate X & Y scaling.
|
|
|
|
- Video anti-aliasing, scaling and rotation! Optional separate X & Y scaling.
|
|
|
|
- Multiple audio track support for videos. New command line options to select
|
|
which default audio track you want (-o or --audio). For games with multiple
|
|
languages, you can specify AUDIO_TRACK in the games.dat as well.
|
|
|
|
- New keyboard methods to make handling input easier, especially multiple
|
|
keypresses for diagonal movement.
|
|
|
|
- controllerGetButton() added.
|
|
|
|
- SINGE_DEAD_ZONE global variable now available based on the DEAD_ZONE
|
|
controller configuration option.
|
|
|
|
- SINGE_FRAMEWORK_VERSION variable added.
|
|
|
|
- DIR variable added providing directory to the running script.
|
|
|
|
- All new Lua library handling. Previously Singe "injected" libraries
|
|
directly into the script context. Now there is a proper Lua module search
|
|
handler. Scripts can properly require() modules from the following:
|
|
- Lua Standard Library
|
|
- Lua Auxiliary Library
|
|
- LuaFileSystem
|
|
- LuaSocket
|
|
- LuaSec
|
|
- LuaRS232
|
|
- timerwheel
|
|
- binaryheap
|
|
- Copas
|
|
- json
|
|
|
|
- New command line option: -p (or --program). This is similar to the
|
|
existing -t (or --trace) option. Where "trace" displays and logs script
|
|
execution, "program" displays and logs Singe internals. This should make
|
|
troubleshooting difficult setups and odd script crashes easier.
|
|
|
|
- Auto-installer for the new GAME, TOOL, and PATCH archive formats to make
|
|
managing games easier.
|
|
|
|
- ZeroBrane Studio (https://studio.zerobrane.com) integration for live
|
|
debugging of running Singe scripts! A full IDE for developing games!
|
|
|
|
|
|
Fixes
|
|
-----
|
|
|
|
- USB controllers now work in games launched from the included menu.
|
|
|
|
- Default controller DEAD_ZONE increased.
|
|
|
|
- Framework.singe was missing keyboard mode constants. MODE_NORMAL and
|
|
MODE_FULL are now included.
|
|
|
|
- SINGE 2.00 added two new input events but never had them in the framework.
|
|
SWITCH_BUTTON4 and SWITCH_TILT are now available.
|
|
|
|
- Taking screenshots could sometimes crash Singe.
|
|
|
|
- PNG files no longer generate warnings on the console.
|
|
|
|
- Non-VLDP videos now honor command line volume settings.
|
|
|
|
- Menu system now performs "natural sorting" of game titles instead of brain
|
|
dead "computer sorting". It should make a lot more sense paging through
|
|
games now.
|
|
|
|
- Building Singe depended on a lot of undocumented software cobbled together
|
|
from other projects. This has been vastly improved and should be usable by
|
|
actual humans now. Humans running Linux anyway.
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
|
|
SINGE 2.00
|
|
==========
|
|
|
|
Released October 25th, 2020
|
|
|
|
|
|
New Features
|
|
------------
|
|
|
|
- Complete rewrite! No more dependency on Daphne.
|
|
|
|
- Support for almost every (non-patent encumbered) video and audio format
|
|
that can be played by FFMPEG.
|
|
|
|
- High definition video.
|
|
|
|
- 32 bit color.
|
|
|
|
- Additional videos can be played on top the laserdisc video.
|
|
|
|
- Scaled sprites.
|
|
|
|
- Graphics overlay resolution no longer dependent on video resolution.
|
|
|
|
- Analog controllers.
|
|
|
|
- LuaSocket and LuaFileSystem added to runtime.
|
|
|
|
- Ability to chain scripts together for menus or launchers.
|
|
|
|
- Specify if you want gun games to display on-screen crosshairs or not.
|
|
|
|
- Menu for launching games included.
|
|
|
|
- Many updates to Framework.singe to support the new APIs.
|
|
|
|
- New APIs:
|
|
- controllerGetAxis
|
|
- discGetHeight
|
|
- discGetWidth
|
|
- fontUnload
|
|
- mouseGetPosition
|
|
- mouseSetCaptured
|
|
- overlayBox
|
|
- overlayCircle
|
|
- overlayEllipse
|
|
- overlayLine
|
|
- overlayPlot
|
|
- overlaySetResolution
|
|
- scriptExecute
|
|
- scriptPush
|
|
- singeWantsCrosshairs
|
|
- soundUnload
|
|
- spriteGetHeight
|
|
- spriteGetWidth
|
|
- spriteUnload
|
|
- videoDraw
|
|
- videoGetFrame
|
|
- videoGetFrameCount
|
|
- videoGetHeight
|
|
- videoGetVolume
|
|
- videoGetWidth
|
|
- videoIsPlaying
|
|
- videoLoad
|
|
- videoPause
|
|
- videoPlay
|
|
- videoSeek
|
|
- videoSetVolume
|
|
- videoUnload
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
|
|
SINGE 1.14 - 1.19
|
|
=================
|
|
|
|
Released ?
|
|
|
|
|
|
Notes
|
|
-----
|
|
|
|
- Unofficial fan builds.
|
|
- Stand-alone releases.
|
|
|
|
|
|
New Features
|
|
------------
|
|
|
|
- New API functions:
|
|
- discPauseAtFrame
|
|
- mouseEnable
|
|
- mouseDisable
|
|
- mouseHowMany
|
|
- mouseSetMode
|
|
- singeDisablePauseKey
|
|
- singeEnablePauseKey
|
|
- singeGetScriptPath
|
|
- singeSetGameName
|
|
- soundFullStop
|
|
- soundGetVolume
|
|
- soundIsPlaying
|
|
- soundPause
|
|
- soundResume
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
|
|
SINGE 1.10 - 1.14
|
|
=================
|
|
|
|
Released ?
|
|
|
|
|
|
Notes
|
|
-----
|
|
|
|
- Unofficial fan builds.
|
|
- DLL releases for Daphne.
|
|
|
|
|
|
New Features
|
|
------------
|
|
|
|
- New API functions:
|
|
- discGetState
|
|
- keyboardGetMode
|
|
- keyboardSetMode
|
|
- singeGetPauseFlag
|
|
- singeSetPauseFlag
|
|
- singeQuit
|
|
- singeVersion
|
|
- soundStop
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
|
|
SINGE 1.00
|
|
==========
|
|
|
|
Released 2006
|
|
|
|
|
|
Notes
|
|
-----
|
|
|
|
- Original add-on for Daphne.
|
|
- Shipped with the ActionMax "emulator" and games.
|
|
|