Back

Portfolio

Welcome! My name is Thiz. I am a Developer, Tinkerer, and VR Enthusiast with a wide range of experience across many technological fields.

Hardware

K'nex armor

A Suit of armor and various weapons displayed on an armor rack, all made from K'nex
Mechanical Wearable

A fully-functional 6ft suit of armor made from K'nex. This was made with three primary goals:

  1. Maximize mobility, including speed and range of motion
  2. Maximize armor coverage, with as few gaps as possible
  3. Maximize durability, particularly when moving quickly or colliding with the limits of the range of motion

This suit of armor had many revisions, each improving on the mobility and durability of the last. The first versions of the arms and legs did not include hinges, instead relying on the wearer's knee to power the joint. This proved to be insufficient, as the lack of a joint resulted in the two sections sliding up or down independently of each other, and becoming disconnected more easily.

A notable challenge in the construction of this armor revolves around the limited availablity of rod locks, a part that greatly improves the strength of a connection. This required careful design in order to allocate the rod locks to where they were needed most, and alternative forms of reinforcement wherever possible.

Remote Control Tank

A K'nex tank with various electronic components integrated within
3D Printing Mechanical Robotics

A robotic tank built from of a combination of K'nex, arduino-based electronics, and some custom mechanical parts.

Plastic rods proved to be too weak to rotate the turret, so it instead rotates on a keyed aluminum shaft, integerated into the rest of the tank via custom 3D printed parts.

This tank was built with four 12V motors; One for each tread, one for rotating the turret, and one for firing the cannon. The Main chassis was built using two batteries in parallel in order to better run both treads at the same time. The turret only uses one, since rotating the turret requires less power to operate.

Due to the limitations of K'nex, there is no electrical connection between the body of the tank and the turret. Each section instead has a separate battery, controller, and wireless connection to the remote control.

VR Eye Trackers

An assembled set of EyeTrackVR Eye trackers and associated wiring layed out on a desk A set of EyeTrackVR eye trackers assembled on the inside of a Valve Index headset
3D Printing Electronics Wearable Virtual Reality

A set of custom EyeTrackVR Eye Trackers on a Valve Index.

This is the second set of eye trackers I have built based on the EyeTrackVR project. The primary focus for this project was streamlining the wiring and ribbon cables to maintain a lower profile and reduce stress on the ribbon cables. I designed clips for the microcontrollers that attach them to the side of the headset, holding the boards in range of the cameras without interrupting the headset tracking.

Tundra tracker magnetic baseplate

A hand holding one part of a magnetic baseplate, with a tundra tracker suspended below by the magnet
3D Printing Wearable Virtual Reality

These are two-piece magnetic baseplates for Tundra Trackers. This is designed to be sewn directly onto clothing, holding the trackers on via magnets. This has the following benefits:

  • These are much faster and easier to use than straps
  • The trackers are held in the exact same spot throughout and between sessions, and never needs to be recalibrated
  • This baseplate is much thinner and lighter than the standard threaded baseplate

The magnetic release was designed to allow the clothing to still be easily washed. Printing them with a higher-temperature plastic also allows them to last a long time.

Customized LucidVR Glove Actuators

A modified LucidVR Glove actuator assembly
3D Printing Mechanical Electronics Wearable Virtual Reality

This is a modified version of the Actuators from LucidVR Prototype 5 Gloves.

I split the main sensor housing into more pieces to avoid requiring supports to be built onto the part, significantly improving the quality and reducing the amount of post-processing required.

The sensor top was also converted from a friction-fit to using a screw instead (the golden-colored screw on the right) which allows for greater print tolerances and easier assembly.

Systems

Bad Eye Tracking

An animated GIF showing Bad Eye Tracking tracking with very low quality input
Computer Vision Virtual Reality

This is an alternative solution for eye tracking, designed for use with EyeTrackVR Eye Trackers. This was made to solve some issues I had with my Eye Trackers, from which it gets its name:

  • Unstable connection - cameras have varying framerates and constantly lose connection
  • Interference - camera feeds interfere with one another, sometimes briefly exchanging frames
  • Poor Lighting - Eyes were not evenly lit, which the (early in development at the time) EyeTrackVR software was unable to track

Despite all of those issues, and the constant flickering visible in the image, this is able to produce a fairly accurate and fairly stable tracking result, including eye look direction and eye openness.

Many safeguards were put in to prevent the program from failing, from automatically reconnecting if one of the camera streams froze to restarting the entire program occasionally to prevent more subtle issues from developing.

NCPF

{ "metadata": 1, "name": "nuclearcraft:cooler", "type": "legacy_block", "modules": { "nuclearcraft:underhaul_sfr:cooler": { "cooling": 60, "rules": [ { "rules": [ { "min": 1, "max": 6, "block": { "name": "nuclearcraft:underhaul_sfr:fuel_cell", "type": "module" }, "type": "between" }, { "min": 1, "max": 6, "block": { "name": "nuclearcraft:underhaul_sfr:moderator", "type": "module" }, "type": "between" } ], "type": "or" } ] }, "plannerator:display_name": { "display_name": "Water Cooler" }, "plannerator:texture": { "texture": "[...]" }, "plannerator:legacy_names": { "legacy_names": [ "Water Cooler" ] } } }
Data Structure Modularity

This is an NCPF Element with the texture data removed for brevity.

NCPF is a modular custom data format designed for my NuclearCraft Plannerator. While its primary purpose is to store the plannerator configuration and saved designs, but can contain any additional data in custom modules. Every element in NCPF can also be overwritten, modified, or added by addons.

While I primarily use JSON, NCPF can be stored in any format that supports the required structure.

Plannerator Customizable Generator

An animation depicting generation of a NuclearCraft reactor through multiple stages
The generator configuration
Machine Learning Modularity

This is the design generator from my NuclearCraft Plannerator. The animation depicts the generation of an Underhaul SFR, running with the pictured settings.

This generator runs in multiple stages; building the reactor core first, before cooling it down and refining it to the desired levels. In order to avoid getting stuck in local minima, it generates a few rough solutions very quickly, then picks the best one of those to further refine.

While the generator was originally designed for generating NuclearCraft reactors, it is fully capable of quickly generating solutions for generalized puzzles.

TreeFeller algorithm

private static HashMap<Integer, ArrayList<Block>> getBlocks(Collection<Material> materialTypes, Block startingBlock, int maxDistance, int maxBlocks, boolean diagonal, boolean playerLeaves, boolean ignoreLeafData, boolean invertLeafDirection){ //layer zero HashMap<Integer, ArrayList<Block>> results = new HashMap<>(); int total = 0; ArrayList<Block> zero = new ArrayList<>(); if(materialTypes.contains(startingBlock.getType())){ zero.add(startingBlock); } results.put(0, zero); total+=zero.size(); //all the other layers for(int i = 0; i<maxDistance; i++){ ArrayList<Block> layer = new ArrayList<>(); ArrayList<Block> lastLayer = new ArrayList<>(results.get(i)); if(i==0&&lastLayer.isEmpty()){ lastLayer.add(startingBlock); } for(Block block : lastLayer){ if(diagonal){ for(int x = -1; x<=1; x++){ for(int y = -1; y<=1; y++){ for(int z = -1; z<=1; z++){ if(x==0&&y==0&&z==0)continue;//same block Block newBlock = block.getRelative(x,y,z); if(!materialTypes.contains(newBlock.getType())){ continue; } if(lastLayer.contains(newBlock))continue;//if the new block is on the same layer, ignore if(i>0&&results.get(i-1).contains(newBlock))continue;//if the new block is on the previous layer, ignore if(layer.contains(newBlock))continue;//if the new block is on the next layer, but already processed, ignore if(newBlock.getBlockData() instanceof Leaves){ Leaves newLeaf = (Leaves)newBlock.getBlockData(); if(!playerLeaves&&newLeaf.isPersistent())continue; if(!ignoreLeafData){ if(block.getBlockData() instanceof Leaves){ Leaves oldLeaf = (Leaves)block.getBlockData(); if(invertLeafDirection){ if(newLeaf.getDistance()>=oldLeaf.getDistance())continue; }else{ if(newLeaf.getDistance()<=oldLeaf.getDistance())continue; } } } } layer.add(newBlock); } } } }else{ for(int j = 0; j<6; j++){ int x = directions[j].x, y = directions[j].y, z = directions[j].z; Block newBlock = block.getRelative(x,y,z); if(!materialTypes.contains(newBlock.getType())){ continue; } if(lastLayer.contains(newBlock))continue;//if the new block is on the same layer, ignore if(i>0&&results.get(i-1).contains(newBlock))continue;//if the new block is on the previous layer, ignore if(layer.contains(newBlock))continue;//if the new block is on the next layer, but already processed, ignore if(newBlock.getState().getBlockData() instanceof Leaves){ Leaves newLeaf = (Leaves)newBlock.getBlockData(); if(!playerLeaves&&newLeaf.isPersistent())continue; if(!ignoreLeafData){ if(block.getBlockData() instanceof Leaves){ Leaves oldLeaf = (Leaves)block.getBlockData(); if(invertLeafDirection){ if(newLeaf.getDistance()>=oldLeaf.getDistance())continue; }else{ if(newLeaf.getDistance()<=oldLeaf.getDistance())continue; } } } } layer.add(newBlock); } } } if(layer.isEmpty())break; results.put(i+1, layer); total+=layer.size(); if(total>maxBlocks)return results; } return results; }
Algorithms

This is the core algorithm for my Tree Feller, a minecraft plugin that detects and destroys an entire tree when a block is broken.

This is a very common problem to solve, and many other plugins have been created for the same purpose, but they always make critical mistakes regarding how a tree is detected. Some common problems include:

  • Failing to differentiate between one tree and the next, cutting down adjacent tree leaves or entire forests
  • Failing to detect tree branches extending diagonally out from a tree
  • Improperly detecting other structures as trees

This algorithm was created to solve all of these and more, and is flexible enough that it's useful for many other projects, whenever detecting a contiguous area of connected cells is required.

Applications

Plannerator: Design Editor

The NuclearCraft Plannerator editor window
User Interface Algorithms

This is the main editor of my NuclearCraft Plannerator, containing many helpful tools to assist design:

  • Editor tools to draw lines or fill blocks across multiple layers at once.
  • Many Symmetry options, to replicate your actions with perfect symmetry
  • Overlays to highlight invalid elements or give more details about the calculations within the design.
  • A 3D preview to show the complete structure of the design
  • Suggestors, which automatically suggest improvements to the design based on certain criteria

The editor also adapts to the configuration, allowing for full configuration via NCPF, and for extra elements to be defined as neccesary.

Plannerator: VR Editor

User Interface 3D Design Tools Virtual Reality

This is the VR editor in the NuclearCraft Plannerator. This allows for editing designs directly in 3D, and offers many of the same advanced editing tools that are offered by the standard editor.

VR is uniquely suited to this task since it does't have to split the design into separate layers. This allows the true structure to be shown and edited directly, and can accelerates the design process to up to twice as fast.

The editor is abstracted such that both editors work as if they were the same one. All of the editor tools support 2D and 3D editing. All of the modifiers support keyboard and controller-based bindings. All of the overlays and decals are drawn in 2D and 3D

Plannerator: DSSL Editor

A custom code ditor for S'tack
User Interface Programming Languages

This is a custom code editor designed for Dodd Simple S'tack Language.

This editor provides syntax highlighting, formatting, a debugger with breakpoints, and other useful editing tools. The editor runs on a custom implementation of DSSL to allow debugging, and can also download and test with the standard DSSL interpreter for performance testing.

OVRToolkit apps

A handful of VR overlays, labeled by virtual sticky notes
User Interface Virtual Reality

As I work in VR a lot, I have made a handful of custom apps for OVRToolkit, many to help improve my productivity when in VR. The following apps are listed:

  • Markers - Customizable decorative markers; This can also automatically highlight the center of your play area.
  • OSC Macros - A customizable OSC macro pad for integration with other apps, such as VRChat
  • Window Anchor - A powerful tool for linking, organizing, and controlling other overlays
  • OVRT Watchdog - A debug console for OVRToolkit, displaying log output and errors directly in VR
  • API Testing - A tool made to test every API call in OVRToolkit to verify and test functionality
  • Sticky Notes - A handy tool to write persistent notes across any application