Skip to content

v0.1.3

Choose a tag to compare

@github-actions github-actions released this 11 Sep 15:54
· 367 commits to main since this release

A timeline for cutscenes; a glTF character of your own in the hero's place - auto-rigged,
weight-painted in Crystal, drawn by the client with smooth weights or with its own
animations; a weapon fitted in its hand; a free camera and a modding guide. The notes:

  • A timeline for cutscenes. The Cutscene component plays a Timeline - tracks of clips
    on a time axis, as Unity's Timeline lays one out - edited in a dock under Crystal's 3D
    view: object tracks walk, glide, turn, pose, show, fade and scale the scene's objects (or
    the hero), a camera track glides the camera and hands it back, dialogue lines hold the
    playhead until dismissed, screen fades and flashes, sounds and music, flags, warps,
    battles, items and signals to code. Clips are dragged and resized on the lanes, the ruler
    scrubs with a live preview in the 3D view (objects move, the camera clip moves the view),
    Space plays; every edit autosaves and undoes. It starts on talking to the object, on
    entering the map, or from code, with If flags, Once and Then flags; B skips it; the
    hero stands still while the player can still advance the lines. Play in OpenFF starts
    the client on the map with the cutscene playing (--cutscene=PATH). The whole thing is
    plain data in the scene file, so a cutscene can also be written by hand or generated.
  • A weapon on a character, in the model viewer. The viewer's on a character toggle
    puts one of the party's models (j101…) under a viewed weapon or shield, plays its battle
    motions (b_b01: the idle, the swings) through the transport, and poses the weapon from the
    hand joint each frame as the game does - R_te/L_te for a weapon, the forearm for a
    shield, then the game's grip turn and offset (btl.BattlePlayer.haveWeapon). For a glTF an
    item definition names, the fit fields (scale, rotation, offset - what the client applies in
    battle) move the model live and save to the definition, so an export is fitted to the attack
    animations by eye; the Look card's On a character… opens the viewer that way. The game's
    own w### models take the toggle too, for comparison. Off, the viewer shows the model alone
    as before. Under it: /api/model/joint (a node's matrix per frame of a motion, from the same
    SBC walk the viewer's poses come from), a second model and an attach matrix in the viewer,
    and a transport that can be pointed at another model. The Look card now shows for a shield
    (armour with a model of its own) as well as a weapon.
  • The modding guide. Guide\index.html in the release (the repository's Docs/Guide):
    short HTML tutorials with pictures - getting started, the map editor, a map of your own,
    items and weapons, cutscenes, models/sounds/fonts, C# code, Steam mods - and a reference
    of every component, timeline clip, file, setting and shortcut. Crystal opens it with
    Help ▸ Guide (a Help menu is new: the guide's pages, the API reference, the samples).
  • Sample projects. The three sample mods ship in the release (Samples\; the Showcase
    also as a ready mod in mods\), and File ▸ Sample projects… copies one into a project
    of your own - laid out as any project, the C# under code/ with a csproj against the
    client's engine - to read, change and Run in OpenFF.
  • The project panel's two sides. The game's libraries and the mod's own folders were one
    list, too tall for the panel with the mod's rows below the fold. A Mod tab now sits
    beside FF3 / FF4: the tree shows one side at a time and each tab remembers the library
    last browsed on it. The Behaviours section of the inspector no longer asks for C# code
    first: the built-in behaviours attach on a project without any (they always could in the
    client; the editor hid them).
  • Game.Camera.MoveTo did nothing. In the game's free camera mode a set position is
    rebuilt every frame from the target, an angle pair and a distance, so the position the
    API wrote was overwritten before it was seen and the camera parked 16 units behind its
    target. MoveTo/LookAt now set the angles and the distance from the line target -> position,
    so a camera glide lands where it is told.
  • Game.Hero.Freeze(keepInput: true) holds the hero still with the pad left on, for a
    cutscene's lines; the plain Freeze() still takes the pad too.
  • Export .glb: a real skeleton, all motions, and no project needed. The export's skin
    used to be one joint per matrix instance, all at the origin, animated with delta matrices:
    playback was right, but in Blender the bones were dots scattered around the character and
    no use for weighting a mesh. The skin is now the model's own node tree - bones parented
    and placed as the game's SBC builds them, inverse binds from the same walk - and vertices
    carry the game's weights, including the DS's envelope blends (several bones per vertex at
    the knees, elbows and hips), checked against the viewer's matrices to 1e-2 over every
    frame. + all motions exports every motion of the pack on the transport as separate
    animations of one file. With a project open the file goes into the project's exports/
    as before, now with the textures as PNGs beside it; with no project - which used to
    write somewhere unasked and then say "no project is open" - the browser's Save As takes
    it where you like (/api/model/glb).
  • A character remade from Blender. The way back in for a mesh modelled over the export:
    the model viewer's Remake from glTF… writes a skinned glTF over a model of the game's
    (j101, a monster) in the game's own format, keeping its skeleton, envelope blends and
    the SBC that builds them, so every .ncap motion plays on the new mesh - in the OpenFF
    client and the Steam game alike. Vertices go through the game's matrix stack slots by
    nearest weights (one bone, or one of the blends the original had); a node drawn without a
    slot gets its shape inline as the original does; runs are split at the game's 20,480-vertex
    buffer and a model over its 32,768 is refused with the number to aim for. Undo remake
    reverts; crystal mdl-reskin file.glb j101 does the same at the command line. Checked by
    round trip: the game's j101 exported, re-exported by Blender 5.1 and written back lands
    on the original to 1/4096 through the idle and a swing; a 10,320-triangle subdivision of
    it fights a goblin in the client. The static mdl-import observes the same limits now.
  • On OpenFF, the glTF itself - smooth weights and all. A model definition
    (defs/models/j101.json: { "model": "j101", "gltf": "assets/luneth-hd.glb" }, which
    Remake writes on an OpenFF project) has the client draw the file in the game model's place
    wherever it is drawn - field, battle, menus. The game's model still loads and animates;
    the stand-in walks its SBC for the frame's joint matrices (shapes masked off, a NODEDESC
    callback, the battle's own hand-joint callback still called) and skins the glTF through
    them on the CPU with the weights as Blender painted them, the file's inverse binds times
    the game's node matrices. Vertex colours are the game's own for the part - the scene's
    light times the original material's diffuse and ambient plus emission - so it is as bright
    as the native model beside it (measured 131.4 against 131.8) and dims with the scene.
    Undo remake removes the definition with the overrides. The OpenFF client's DrawModel
    buffers, the Steam port's fixed 20,480 vertices a run / 32,768 a model / 256 shapes, now
    grow to fit, so a dense game-format model draws too; on an OpenFF target the writers note
    a model over the Steam limit rather than refuse it.
  • A mesh with no rig, rigged to the character. Remake with a plain glTF binds it to the
    model's skeleton first (AutoRig, crystal mdl-autorig): fitted to the character's height
    and feet, weighted from the nearest point of the original mesh's surface - the game's own
    skinning, blends included - after the original is posed like the file (a modelled
    character stands arms-down, the game's bind is a T; matched by proportions, the file
    taken back into the bind pose through its new weights), then smoothed. The rigged copy
    is written beside the file as <name>-rigged.glb.
  • The viewer skins a glTF with the game's motions. A skinned glTF whose joints are a
    game model's bones (a remade export, an auto-rigged mesh) gets the model's transport:
    its packs and motions, skinned in the browser from the model's node matrices per frame
    (/api/model/rig-pose). Orbit and scrub to check the weights.
  • Weight painting in the viewer. weights on a skinned file of the project's: one
    bone's weight as a heat map with the skeleton over it, a brush (add / erase / smooth,
    radius, strength, mirror to the L_/R_ counterpart) on the mesh as posed, Alt+click to pick
    the bone under the cursor, undo, and Save weights writing the four-bone weights into the
    .glb in place and remaking the game model from it. The auto-rig's second pass (in the
    bind pose, where the arms stand clear of the body) and its outlier vote take most of the
    need away; the brush is for the rest. The brush is a ring on the surface under the cursor
    and paints that surface - out along the mesh's edges to the radius, not a ball about the
    point - so a sleeve is painted without the chest beneath it. The hierarchy lists a skinned file's bones as a
    folding tree (click to pick the paint bone); bones and wireframe toggles draw the
    skeleton and the mesh's edges over the shaded model, weights mode or not, and the heat
    map can be turned off to paint on the textured model.
  • The auto-rig knows a chin from a chest. Two more steps for a mesh of other
    proportions: each arm limb is turned about its joint onto the game's posed bone before the
    carry into the bind pose (the matched pose is alike as a whole, not limb by limb, and the
    difference would otherwise stay as a permanent bend of the forearm off its bone); and the
    file is cut by its own shape into head (above the narrowest slice under the head), arms
    (outside the torso's width in a slice) and the rest, each taking weights only from the
    like part of the original. On the chibi that took mune off the face, kubi off the eyes,
    hara off the sleeves and R_sakotu off the hair in one go. Legs and a skirt are regions
    too (a tunic follows the hips, never the legs), and the cuts - neck, hips, arm floor, torso
    width, skirt - are sliders on the unrigged file with the found values shown and the frames
    drawn on the model, saved beside the file and used by every rig of it after.
  • A fitted skeleton. The game's skeleton with its joints moved to where the file has them
    (markers first - chin, wrists, elbows, knees, groin, placed by clicking the model - the
    mesh's own rings for the rest), weights by distance to those bones within the regions, the
    file carried into the T-pose about them; "fitted": true in the definition and the client
    retargets the game's motions onto it, every bone its own name. Limbs of the file's own
    lengths bend where the file bends. OpenFF only.
  • A model's own animations. A glTF look with animation clips plays them in the game's
    motions' place, for the motions the definition names ("clips": roles like idle, walk, run,
    attack, damage, magic, victory, or a motion's id or pack name), in step with the game's
    motion or at their own pace; the rest stay the game's. Game.Hero.PlayClip / Npc.PlayClip
    play a clip from C#. Crystal's transport plays a file's own clips, and own clips under the
    viewer edits the map.
  • Weights you can reason about. assign paints a part onto a bone outright; the vertex
    under the brush reads out its bones and weights; all bones colours the mesh by heaviest
    bone with the hierarchy's rows in the same colours. Weights are kept as Blender keeps them
    • each bone's its own, no sum held to one while painting - so erase takes only that
      bone's weight off and nothing is handed to a parent or a stray bone; a vertex may carry
      nothing (it stays as the file has it; the readout says so) and the sum is made one on save.
  • The model view's toolbox. The tools are icon buttons floating at the view's left -
    look, bones, wireframe, heat map, all bones, the four brushes, the auto-rig's cuts and
    markers, own clips - and the active tool's properties sit in the inspector as a card, so
    the view never resizes as a tool comes and goes; the readout of the vertex under the cursor
    floats over the view's bottom right. The middle button (or Shift with the right) drags the
    view, in the model viewer as in the map editor. With a bone picked in the hierarchy, Up and
    Down step to the next bone - from the 3D view too, so the bone under the brush is swapped
    without leaving it. Every tool has a key on its button (Q look, B, W, H, C, 1-4 the
    brushes, X, M, L), [ ] and { } step the brush, Ctrl+Z and Ctrl+S undo and save; painting,
    Ctrl held erases and Shift smooths for the stroke, the right button dragged sets radius
    (sideways) and strength (up) with the ring and the numbers following, Alt + drag orbits
    and Alt + click picks the bone. Mirror is off unless ticked; G cycles the backdrop
    (dark, grey, light, sand).
  • Normals, an importer's way. A .glb asset in the project panel has a Normals card in
    the inspector: from the file, or calculated from the triangles at a smoothing angle (across
    the seams where the file split a vertex), applied into the file with the file's own kept
    inside it for an exact Revert. For a model exported faceted.
  • A file's kind, and a weapon in your character's hand. A .glb asset's Kind card in
    the inspector flags it as a character, prop, weapon, shield, map or battle map when its name
    and shape do not say (written into the file). A weapon's on a character preview now
    offers the project's own characters as the body - a rigged export, an auto-rigged file -
    skinned by the viewer and the weapon hung from its own hand joint, so equipment is fitted
    on the character it will be worn by.
  • Which model a file plays as, in plain sight. A character file's In play card in the
    inspector says which game model the client draws it in place of (the whole binding is one
    file, defs/models/<model>.json, a click away), and switches or clears it by hand; the
    Models list tags both tiles (→ j101 on the file, ← luneth-chibi-rigged on the model).
  • A free camera in the client. F7 on the debug overlay (F1): the mouse looks, WASD fly,
    E/Q up and down, Shift and Ctrl the pace, the wheel too, R back to the game's eye, F7 or
    Esc off. The game runs on with the player's input held off - a close look at what is drawn.
    A drive's camera x y z yaw pitch places it for a screenshot.
  • Textures at any size in OpenFF. The DS format's 64 × 64 (or 8 to 1024) is the Steam
    game's ceiling, not the client's: on an OpenFF project Replace with a PNG… keeps a larger
    picture at its own size as textures/<name>.png, exported with the mod, and the client
    draws that in the texture's place - filtered smooth, mapped as before - while the package
    keeps the downsized copy for Steam.
  • One character in a glTF: the Look. Picking a glTF as a game character's model in the
    map editor no longer goes nowhere - a .hich row can only name a game model - but puts a
    Look behaviour on that one character (Path, Fitted): the game's model still loads,
    walks and talks, the client draws the file in its place, skinned by that character's own
    motions. Npc.SetLook / Game.Hero.SetLook do the same from C#. A definition
    (defs/models) stays the way to dress every instance of a model.
  • Save weights writes the geometry too. The bind pose re-carried through the painted
    weights - what the viewer showed all along - now goes into the file with them, so the
    preview and the game stop showing sleeves stretched to where the first auto-rig left them.
  • The retarget carries a node's move. The game's motions move nodes as well as turn them
    (trans in a hop, a knockback, the fall); a retargeted glTF now follows those moves as the
    viewer always did - a felled character lies on the ground rather than at standing height,
    a sleeve is not stretched to where its shoulder had been. Drive verbs motion <id> [loop] [all] [end] and hp <member> <hp> for looking at a pose in play.
  • Another rig's model, retargeted by the client. A definition may name a glTF rigged
    another way (Mixamo, Tripo, Rigify): bones matched by a table of the usual names and the
    definition's "bones", the file scaled and stood on its feet with its up and facing found
    from the rig, limb bones turned into the game's bind directions, the game's motion applied
    as world rotations down the file's own hierarchy. scale, rotation, offset override
    the fit. Proportions still tell; the auto-rig is the better road for a chibi.
  • glTF textures get mipmaps in the client (they had none: a 2048-square texture on a
    character a hundred pixels tall shimmered), capped at 2048 a side; JPEG textures decode
    for the game-format remake too. Then taken off again by default: an atlas of many small
    islands packed tight (a generated model's - 1189 on the chibi) cannot be mipmapped without
    mixing two islands into one texel, and those were the grey lines along the hair and face
    from afar, gone up close. The client now draws level 0 alone (at most 2048 a side, the
    islands' rims repainted from their insides, as Crystal's viewer does); --gltf-mips=auto
    builds a chain from the UVs' coverage where they leave room, full regardless.