Skip to content
Matija Kecman edited this page Aug 28, 2024 · 112 revisions

Getting Started

Download the relevant .zip asset from the Releases page, unzip the file and run the Prizm executable. There is no installation process.

Video Tutorials

Note: If your browser doesn't support webms you can visit Video Tutorials (.mp4 format) instead, that page duplicates this section but uses mp4s instead.

Here are a few videos demonstrating Prizm's main features, the videos cover most of what is described in the Features section. The timeline scrubbing feature of .webm files uploaded to GitHub make them convenient to browse. The videos were recorded using version 0.9.1, which is now old, but the UI should not have changed that much (I will remake the videos if it does!).

Basic Features

This video covers: loading files, camera controls, changing settings on single items, changing settings on multiple selected items, inspecting many files in various ways.

Prism-0.9.1-Demo1.webm

Console Commands and Clipping

This video inspects a file logged by buggy mesh boolean code. It covers the useful "ball clipping" feature, console commands for finding open boundaries, perturbing vertices and locating triangles by index.

Prism-0.9.1-Demo3.webm

Labelling and Annotations

This video covers: annotations (string data) associated with OBJ files/elements/vertices, how annotations can be visualized/stylized in the viewport, how console commands can be executed when a file is loaded, how label format can be tweaked e.g., to inspect precise coordinate data.

Prism-0.9.1-Demo2.webm

Window Manipulation and Hot-Reloading

This video covers: reloading/hot-reloading files loaded in Prizm, quickly moving/resizing the Prizm window without pixel perfect cursor positioning on the window border/menu bar, adjusting window opacity and changing the Prizm logo color to help you identify datasets loaded in different instances of Prizm.

Prism-0.9.1-Demo4.webm

There is a minor bug with the text indicating the mouse/keyboard presses in this video: when resizing/repositioning the Prizm window the text is "Alt" but it should be "Alt LMB" since these operations are triggered by dragging LMB clicks while holding Alt.

Features Overview

TLDR;

  • Load OBJ geometry from files/folders, with an option to monitor and hot-reload changes.
  • Find and visualize bugs in awkwardly shaped meshes or amongst multiple meshes using a UI that helps you work fast.
  • Attach text annotations to various geometric elements and view them within Prizm.
  • Use console commands to process loaded geometry to help you tackle more complicated bugs.
  • Use a C++/Python API, configured and accessed via the UI, to log OBJ files from anywhere in your program.
  • The C++ API supports working with the Unreal geometry library and I am planning similar support for other tools/libraries.
  • Prizm runs Linux and Windows and is distributed under the MIT license.
  • For real-time support you can join the Discord.
  • Features planned for future releases can be found here.

Loading Data

  • Files and directories can be loaded into Prizm using drag and drop or by passing arguments to the Prizm executable via the command-line (the wildcard * is supported and matches any sequence of characters).
  • Prizm can work with large (250MB+) files representing scanned geometry which cause some other viewers to crash or load extremely slowly.
  • Loading directories will open a popup where you can select which files in the directory should be loaded. When a directory is loaded it will (by default) auto-load any newly created or modified file, this enables a handy workflow where Prizm monitors and loads files in the directory where your C++ program writes files.
  • Loaded items are added under an appropriate folder name to the item list window in the top left. The checkbox to the left of the filename indicates visibility.
  • Files are given a color by hashing the fully pathed filename which helps differentiate files and make them easier to track across sessions.
  • Files which have changed on disk since they were loaded are marked with an asterisk and changed visible items can be reloaded with F5.
  • Only OBJ files are supported, see here for more details. We attempt to handle errors in the OBJ file gracefully while reporting errors/warnings in the Console.

Camera Controls

Camera interactions should be fast and require minimal fiddling to look at what you want.

  • Use RMB to orbit the camera, MMB to pan the camera and scroll to zoom the camera "into the cursor".
  • If you prefer the more common, but IMO less convenient behaviour where zooming moves toward the current fixed orbit position, you can use Shift scroll instead.
  • The camera orbit position can be set using Ctrl RMB and will be located at the intersection of a view ray and the scene, or a nearby vertex if the ray misses.
  • Pressing f will toggle the camera between focussing on the selected items and all the visible items.
  • The camera UI in the top right has buttons to set the camera to look along the given world axes, adjust the near/far clipping planes and rotate the camera around the selected alternative rotation axis.

Inspecting Data

When debugging an algorithm it’s often helpful/necessary to inspect many different shapes at the same time, Prizm implements features which aim to make this as easy as possible.

  • You can sweep drag LMB over item visibility checkboxes to quickly turn them on/off. By holding Ctrl LMB while you do this only the item under the cursor will be visible and by holding Shift LMB the camera will also update to keep the focussed item on the screen.
  • Items can be selected by clicking LMB on the item in the viewport, or the item name in the item list. When an item is selected various pieces of state can be modified/inspected in the "Details" section.
  • Ctrl a selects/deselects all loaded items, Shift a selects/deselects all visible items and Alt a complements the current item selection. When multiple items are selected any state changes made via the "Details" section will be applied to each item in turn.
  • The ribbon buttons at the bottom centre of the the screen make cyclic edits to all currently selected items. Each button also has a corresponding shortcut, for example pressing l will cycle the line thickness rendering. The ribbon button behaviours are explained in their tooltips.
  • Hovering the name of a visible item in the item list will cause it to flash white and render above other items to make it easy to find in the viewport.

Clipping

It often happens that only a small region of a given geometry is interesting to look at for the purposes of debugging. Prizm has some features related to clipping which make it quick and easy to focus on a subset of a shape:

  • Clipping balls. By holding Shift and clicking with LMB on an item a clipping ball centered at the view ray/item intersection is created and the radius is set by the subsequent mouse motion. After setting a ball it is helpful to press f to focus the camera on the clipped result. It is also handy to create a new ball centered on a part of the shape now revealed by the previous clip. When a clipping ball is created while multiple items are selected, that ball is applied to all selected items.
  • Clipping slabs. These are less convenient than clipping balls since they can only be edited via the UI. Up to three clipping slabs can be created, which are defined as planes with a range of distances from the plane in which geometry will be visible. The plane normals are initialized to the X/Y/Z axis directions and the distance ranges are initialized to fully enclose the item bounding box. The ranges and normals can be edited under "Details > Clipping".
  • If you get into a situation where the entire shape is accidentally clipped you can use c keybind to toggle the clipping shapes, and then use f to re-focus the camera.

Console Commands

Prizm has a console which displays information/error messages and allows users to run commands the configure/process loaded OBJ files.

  • The console can be toggled with ` (backtick) and closed with Esc.
  • Console commands are invoked with their name followed by space separated arguments, there are no parentheses delimiting arguments or commas separating them. Items are referenced by integers shown in the item list prefixed with a # but note that the # is not written in command arguments.
  • The console supports tab-completion for command names and when the name is unique pressing tab will display the command documentation.
  • If you have access to the Jai compiler you can add console commands by writing a regular Jai procedures and adding a @RegisterCommand note. Any comments immediately above the function definition will be extracted and displayed as documentation in the console. See commands.jai for examples.
  • The console will appear automatically when an error message is reported (e.g., if a file cannot be loaded) but otherwise it needs to be toggled manually. When writing commands, try to make sure they report useful error messages.

Labelling and Annotations

Many algorithms require careful manipulation of indices and positions, Prizm can help you find bugs in such code by visualizing indices/coordinates as text in the viewport. In other cases it can be helpful to associate and visualize text next to point/segment/triangle elements, in Prizm these are called annotations and they are specified in the OBJ file via OBJ comments. See Demo.obj for an example of an annotated OBJ file.

  • OBJ comments placed on the same line as vertex/segment/triangle declarations (i.e., lines starting with v, l, or f directives) create annotations. Note: Extending the OBJ format in this way is not entirely new e.g., see this ZBrush example.
  • OBJ comments placed at the start of the file are parsed into a block/header annotation which is added to the tooltip that appears when the filename is hovered. This is useful to record a comment explaining the geometry stored in a particular file. This behaviour is off by default but can be enabled via "File > Preferences > Item List > Show Header Annotation Tooltips".
  • Element index labels can be toggled by pressing i or by clicking the corresponding ribbon button.
  • Vertex index/positions labels can be cycled by pressing x or by clicking the corresponding ribbon button.
  • Annotations can be toggled by pressing a or by clicking the corresponding ribbon button.
  • All viewport labels and annotations can be cleared by pressing z.
  • Annotations can be viewed in a table via "Details > Annotations", the "Focus" button will position the camera orbit on the annotated vertex/element centroid and will zoom in on it. When you write a OBJ file you can add annotations to a specific vertex/element where you detect a problem, and then use the focus buttons to quickly locate that annotation in Prizm.

Command Annotations

When users load files in Prizm often the first thing they do is configure various rendering settings. This configuration is usually done via the UI but most things can also be configured via console commands (e.g., use set_edges_width to enable triangle edge rendering), this is particularly useful in combination with Prizm's command annotations feature. Command annotations allow you execute console commands immediately after an the OBJ file has been loaded, they are specified in the OBJ file on lines that look like this: #! command_name arg1 arg2 (this syntax is inspired by the "hashbang" character sequence used to introduce an interpreter directive on Unix). A workflow which has been handy in some cases is to write console commands that do processing specific to the problem your are currently debugging, and then call them automatically via command annotations written to your debug files. For more details on command annotations, see the command and documentation functions in the Prizm.h, or prizm.py files.

OBJ file authoring APIs

Prizm has a header-only C++ API, Prizm.h, and a Python API, prizm.py, for writing OBJ files with Prism-specific extensions (e.g., vertex/element annotations, command annotations). The API is conveniently accessed and configured via the UI under "Prism > Tools > API". The "Copy #include lines" button in the "C++" section copies the required #includes to your clipboard. There is a similar button, "Python > Copy import lines", which copies the Python import lines that you can paste into your program. Absolute paths are used so that if you're using Prizm with a project with a CI/CD pipeline this will fail if you accidently check in your debugging code (the pipeline should fail to compile on your remote build machine because it won't be able to find the Prism API headers).

  • The API has functions to write geometry in plain OBJ format (based on http://paulbourke.net/dataformats/obj/) as well as functions to write Prism-specific extensions e.g., annotations and command annotations. See the documentation() function for more info.
  • The C++ API can be configured to be usable with small standalone projects in which case the it will only depend on the C++ STL (future work could remove this).
  • The API optionally supports working with Unreal data types by including Prizm_Unreal.h. Unreal module dependencies are optional e.g., the API functions using types/headers from the GeometryCore module can be excluded if you want to use the API in a module which doesn't depend on GeometryCore, this is achieved via macros which are automatically configured via the UI (although it is clear how this works by glancing at the file).

Tips/Tricks

  • If you don't have any OBJ files lying around you can try loading the examples in the Prizm/shapes/ folder. Alternatively you load preset shapes which are baked into the executable using "Tools > Create".
  • Use Alt LMB to click/drag the center of the window (anywhere within middle 50%) to move the Prism window.
  • Use Alt LMB to click/drag near the edge of the window (anywhere within the 25% or a border) to resize the Prizm window.
  • Start Prizm passing the --AlwaysOnTop option on the command line and use the unfocussed window opacity option located under "View > Window Settings" to implement a useful workflow where you are able to step through in your debugger while (re)loading any generated OBJ files. You could also put your debugger and Prizm side-by-side in your window manager but the opacity trick is useful for small screens e.g., when working on a laptop.
  • You can click on the Prism icon in the top left of the window menu bar to cycle through various colors, this can be useful if you have multiple instances of Prizm running with different data loaded in each one. You can also set the color via the command line using the --Red, --Green, --Blue or --Black options.
  • You can use Prizm as a simple modeling tool as follows: Make an OBJ file with a single triangle in it, load it in Prizm and ensure the auto-reload checkbox is enabled under "Details > Display > Selection", and then you can made edits to your OBJ file and see the new changes in Prizm each time you save the file. Prizm gracefully handles broken OBJ files and will attempt to give you useful error messages. This workflow can be useful when you're creating test cases for your code.

Supported OBJ Features

The text below summarizes subset of the OBJ format which is supported and explains the parts which are omitted. Prizm's load_obj function, which implements this, should be easy to follow.

The following features are supported:

Syntax Description Comments
v vx vy [vz] 2D [3D] Geometric Vertex
v vx vy [vz] r g b Colored 2D [3D] Geometric Vertex Color interpolated over elements
vn nx ny [nz] 2D [3D] Geometric Normal Annotions not supported
p vi [...] Point Element [Point Cloud]
l vi vj [...] Line Element [Polyline]
f vi vj vk [...] Triangle Element [Triangle Fan]
p vi//ni [...] Oriented Point Element [Point Cloud]
l vi//ni vj//nj [...] Oriented Line Element [Polyline]
f vi//ni vj//nj vk//nk [...] Oriented Triangle Element [Triangle Fan]

Notes:

  • vx,vy,vz are floating point coordinate values.
    • If vz is omitted it is set to 0
    • Prizm will replace any inf/nan values with the corresponding component of "Invalid Point", which defaults to (0,0,0). "Invalid Point" can be customized under "File > Preferences > Item List" (this is not the best location for this option).
  • r,g,b are floating point color components in the range [0, 1].
  • nx,ny,nz are floating point normal component values.
    • If nz is ommitted it is set to 0 (Note: Actually in this case the parser fails, this needs fixing..!)
    • Prizm will replace any inf/nan values with 0
  • vi,vj,vk are 1-based, non-zero integers referencing v-directives.
    • Negative indices refer to the v-directives immediately previous to the current element.
    • Invalid/out of range references will be updated to reference a new vertex positioned at the "Invalid Point" position.
  • ni,nj,nk are 1-based, non-zero integers referencing vn-directives.
    • Negative indices refer to the vn-directives immediately previous to the current element.
    • Invalid/out of range references will be ignored and elements using them will store zeroed normals.
  • If any syntax is malformed Prizm will ignore that line and try to continue loading the shape
  • When visualizing normals in Prizm there is an option to scale the normals, and the scaling can be applied after normalizing the data from the file (default), or without normalization (requires unchecking the checkbox under e.g., "Details > Display > Triangles > Normals Style"). Disabling normalization is useful if the normal lengths encode something you are interested in.

The following features are planned:

Syntax Description Comments
vt tu tv [tw] Texture Vertex Annotations will not be supported
f vi/ti/ni vj/tj/nj vk/tk/nk Triangle Element with UVs and with normals
f vi/ti vj/ti vk/tk Triangle Element with UVs and without normals
g Element Grouping Annotation semantics TBD
o Object Name Annotation semantics TBD
usemap Texture Map Specification

Notes:

  • tu,tv,tw are texture map UV values (tw is a 3D texture coordinate).
  • ti,tj,tk are 1-based, non-zero integers referencing v-directives.
    • Negative indices refer to the vt-directives immediately previous to the current element.
    • Invalid/out of range references will be ignored and elements using them will store zeroed UVs.
  • Annotations added to group/object name lines have so far undefined semantics (should we ignore them, apply them to all elements, handle them some other way?)

All features which are not supported or planned will likely never be supported, the Prizm OBJ parser will just ignore these lines and print a warning. Prizm assumes users are working with simplex geometry, not smooth primitives so free-form curve/surface statements are unsupported (these are not very useful for debugging), also the OBJ format rendering/display configuration features are replaced with Prizm's own features, which we configure using command annotations (see below).

Hacking/Contributing

Architecture

This section will be filled out in more detail when Jai is publically available and the Prizm architecture stabilizes. In the meantime, it's worth mentioning that Prizm is using Dear ImGui for its UI and SDL for platform operations, this won't change since these libraries are quite familiar to potential users and it should be as easy as possible to modify the Prizm source code.

Note: I also tried using raylib for the platform operations, but as of March 2024, it seems not to support borderless windows as well as SDL (it seems to be quite fiddly to do this right). I also experimented with writing an entirely custom platform layer but it seems annoying to properly test this on Linux because a good implementation would need to support both X11 and Wayland.

Coding Style

The coding style is non-dogmatic but sticking to the following conventions would be appreciated.

  • Use the following naming convention: function_names, Structs_And_Types, MacroNames, COMPILE_TIME_CONSTANTS (including enum names)
  • Use a single space between an identifier and the :: or : and don't use backslashes in identifiers. This convention makes searching the code easier. If explicit types are used use single spaces ie : Type =.
  • To improve readability avoid using := for assignments unless the type can be trivially inferred from the context (unfortunately there is a lot of code that doesn't do this, but it can be gradually fixed). Note that with explicit types its a bit harder to search for declarations (unless you also know the type).
  • When you implement a feature adding a comment explaining the user-facing motivation for it next to the implementation is useful, so that we are not tempted to remove an obscure looking feature because we forgot why it was useful. Consider starting such comments with "Feature documentation:".
  • Consider using these naming convention: _model/_world/_screen postfix for geometry in model/world/screen space, g_ prefix for global variables and t_ prefix for variables in temporary storage (e.g., functions returning strings in temporary storage). Consider using a _t postfix for functions returning results in temporary storage.
  • Consider using a prefix underscore for _auto_generated_functions and _Auto_Generated_Structs.
  • Consider using naming conventions like init_Type/new_Type (allocate and init)/make_Type(return a copy) to make it easier to find the function? Or perhaps a type_init, type_deinit, type_operation is better since its more consistent with array_add etc.
  • Consider using naming conventions like thing_kind for enum variables, thing_type for Type variables and thing for everything else
  • init(thing : *Thing)/deinit(thing : *Thing) functions should work on stack instances as well as values on the heap and hence they shouldn't call free?
  • When you rename a function, particularly a console command, search all .jai and OBJ files for the name so you can fix all the references, we think good, unambiguous user documentation is important!
  • Don't indent line with the case keyword in a switch statement (if ==), but do indent the case body, this makes the switch statement indent in a similar way to if-else statements.
  • Consider writing long comments on a single line, this way the user can control how much vertical space long comments occupy (by wrapping lines, or not).

Prizm should not encourage the user to generate OBJ files that do not load in other viewers, so any extensions should work via OBJ comments, perhaps as a post processing step invoked via a command annotation.

Release Process

This tool is intended to be hackable and compiled from source as you use it. So it doesn't really make sense to release binaries, however, this is what we do while Jai is in closed beta:

  1. Update changelog.jai with the release date. Update EXPECTED_COMPILER_VERSION_INFO

  2. Compile using: jai first.jai - release. Do the following:

    • Test all the files in the shapes folder load correctly: Prizm.exe shapes/*obj
    • Test a large OBJ file (>200MB) file.
    • (Recompile with jai first.jai - verydebug) Test there are no memory leaks by executing various workflows (load files, enable/disable annotations, run commands...)
  3. Compile using: jai first.jai - shipping this will build the release executable, set the icon (windows only) and create the .zip file with the correct name.

    • Unzip the zip and launch the Prizm executable.
    • Check the size of the zip, if its suddenly huge you may have included some temp files e.g., delete the .mypy_cache folder if you run the mypy to check the Python API
    • Test all the files in the shapes/ folder load correctly.
    • Test the basic C++ API works by calling the Prizm::documentation.
  4. Use the GitHub UI to complete the release:

    • Upload the release zip for windows and linux (currently you need to run step 2. on both operating systems).
    • Copy the new section in the changelog to the description, name the tag vX.Y.Z
    • Press the release button and run git pull in your repos to get the generated tag.
  5. [optional]. Post a release message to the Prizm/GPWW Discord, Handmade Network, Twitter? Start a thread with a changelog summary and screenshot if relevant, post the zip in a follow up message and post the full changelog for the new version in another follow up message.