Monday, 15 September 2025

Particle Tool Unity | code | WIP | Windsurf Coding

 

What:  Unity editor Tool 

Tools: Original written in Visual Studio. 

             Re-Written With Windsurf (It's Awesome!) 

A WIP Unity editor tool that standardizes particle presets, spawns prefabs from them, adds simple track keying, auto-captures thumbnail previews for any prefab, and includes a plain-English URP material maker for consistent rendering.


Architecture at a glance (what exists)

┌─────────────────────────────────────────────────────────────────┐
│                           FXTool (Editor)                       │
│                                                                 │
│  ┌───────────┐    ┌──────────────┐   ┌───────────────┐          │
│  │ FXPreset  │ →  │ FXPresetApplier │ → (Scene/Prefab)          │
│  └───────────┘    └──────────────┘   └───────────────┘          │
│         ↑                          ↑                             │
│         │                          │                             │
│  (listed/edited)             (invoked from)                      │
│         │                          │                             │
│  ┌──────────────┐     ┌──────────────────────┐      ┌──────────┐ │
│  │ FXToolWindow │ →→  │ FXTrackManager (API) │  →→  │ Animator │ │
│  └──────────────┘     └──────────────────────┘      └──────────┘ │
│         │                                                       │
│         ├── (calls)                                             │
│         │                                                       │
│  ┌─────────────────────┐        ┌──────────────────────┐        │
│  │ FXLibraryMaterial…  │  →→    │ FXURPMaterialFactory │        │
│  └─────────────────────┘        └──────────────────────┘        │
│                                                                 │
│   (PlayMode helper)                                             │
│        ┌─────────────────────────┐                              │
│        │ FXRuntimeThumbCapturer │  (GUID-keyed PNG cache)       │
│        └─────────────────────────┘                              │
└─────────────────────────────────────────────────────────────────┘
 

1) Folders & naming (shared foundation)

The tool enforces a predictable layout (Assets/FXTool/...) and naming helpers for roots/children, then guarantees all folders exist via EnsureAll(). This keeps presets, prefabs, thumbnails, materials, and textures tidy and script-discoverable.

public static string BuildChildName(string category, string preset, string role, int i = 0)

  => $"FX_{San(category)}_{San(preset)}_{role}_{i:D2}";

This naming scheme is used everywhere (presets to prefab names, child PS nodes, etc.).  


2) Presets (ScriptableObjects)

A preset stores identity (category/name/desc) and the key Shuriken knobs (duration, looping, lifetime, speed, size, emission, shape), plus renderer/sorting and optional flipbook info. It’s intentionally minimal but extendable.

[CreateAssetMenu(menuName="FXTool/Preset")]

public class FXPreset : ScriptableObject {

  public FXCategory category; public string presetName; public string description;

  public float duration; public bool looping; public float startLifetime, startSpeed, startSize;

  public int rateOverTime; public ParticleSystem.Burst[] bursts;

  public ParticleSystemShapeType shape; public float radius, angle;

  public Material material; public string sortingLayer; public int orderInLayer;

  public Texture2D flipbook; public int tilesX=1, tilesY=1; public float flipbookFPS=12f;

}

 ScriptableObject with callouts (“Main”, “Emission”, “Shape”, “Material/Sorting”, “Flipbook”).

 


3) Preset to Prefab 

The applier builds a rooted GameObject, adds a child with a ParticleSystem, and transfers preset values into Shuriken modules. It also wires flipbook settings when present, and can immediately save a prefab asset.

var root = new GameObject(FXNaming.BuildRootName(p.category.ToString(), p.presetName));

var psGo  = new GameObject(FXNaming.BuildChildName(p.category.ToString(), p.presetName, "PS"));

var ps    = psGo.AddComponent<ParticleSystem>();

// main

var main = ps.main;

main.duration = p.duration; main.loop = p.looping;

main.startLifetime = p.startLifetime; main.startSpeed = p.startSpeed; main.startSize = p.startSize;

// emission

ps.emission.rateOverTime = p.rateOverTime;

 

// renderer & sorting

var r = ps.GetComponent<ParticleSystemRenderer>();

r.sharedMaterial = p.material; r.sortingLayerName = p.sortingLayer; r.sortingOrder = p.orderInLayer;



4) Editor window (Library | Inspector | Tracks)

The tool window exposes three workflows:

  • Library: lists all FXPreset assets under Assets/FXTool/Presets so you can select, duplicate, or ping.
  • Inspector actions: “Create From Preset” to spawns in scene; “Save As Prefab” to writes to Assets/FXTool/Prefabs.
  • Tracks (MVP): a tiny keyframer for position on the spawned root. You set Clip name/FPS/Frame/Vector3 and it keys transform curves.

// “Key Position on Root at Frame”

var ctrl = FXTrackManager.EnsureAnimator(_lastRoot);

var clip = FXTrackManager.EnsureClip(ctrl, _clipName, Mathf.Max(2f, _frame/_fps+0.1f), _fps);

FXTrackManager.KeyTransformPosition(clip, _lastRoot.transform, _frame, _pos, _fps);


5) Tracks API (Animator + clips + transform curves)

Under the hood: if the spawned root doesn’t have an Animator, it creates one and stores a controller next to your prefabs. Then it ensures a clip by name and writes transform curves for m_LocalPosition.{x,y,z} at a given time = frame/fps.

static void AddKey(AnimationClip clip, Transform t, string prop, float v, float time) {

  var bind = EditorCurveBinding.FloatCurve(AnimationUtility.CalculateTransformPath(t, null), typeof(Transform), prop);

  var curve = AnimationUtility.GetEditorCurve(clip, bind) ?? new AnimationCurve();

  curve.AddKey(new Keyframe(time, v) { weightedMode = WeightedMode.Both });

  AnimationUtility.SetEditorCurve(clip, bind, curve);

}


6) Thumbnail capture (the big quality-of-life win!)

No need for Play Mode - just instantiate each requested prefab in front of Camera.main, warm up the particle systems, render to a square RT, reads back to Texture2D, and save as a PNG into a cache folder keyed by GUID.  

Particle pack used for testing: https://assetstore.unity.com/packages... 

instance.transform.position = cam.transform.position + cam.transform.forward * cfg.distance;

foreach (var ps in instance.GetComponentsInChildren<ParticleSystem>(true)) {

  ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);

  ps.Simulate(0f, true, true, true);

  ps.Play(true);

}

// render to RT to Texture2D to PNG in cache

The cache path is created defensively (Assets/FXTool/Cache/ParticleThumbs/...), and each PNG is named with the prefab’s GUID so the library can look up thumbnails deterministically later.

UX detail: the coroutine uses small warmup/capture delays so particles are “mid-motion” when snapped; it also shuts down Play Mode when done so the batch flow is quick.



7) URP Material Factory (plain-English, guard-railed)

A separate editor window lets you pick Usage (Sprite, Particle Billboard, Trails, UI), Blend (Alpha/Additive/Premultiplied/Multiply/Cutout), sidedness, ZWrite, and emission. It then creates/updates a URP material at a stable path (Assets/FXTool/Materials/<Category>/<Name>.mat).

On apply, it sets the URP surface/blend/keyword combo properly (including premultiplied and alpha-clip cases) and toggles common keywords. 

On masse layer switcher /  

There’s also a small “Tips” section to nudge best-practice choices (e.g., Sprite-Unlit-Default for 2D, Particles/Unlit + Additive for glows). The path utility ensures category subfolders exist.



How the pieces talk to each other

  • FXPreset is the source of truth for particle settings.
  • FXPresetApplier reads a preset and builds a scene object/prefab with those values (and flipbook, sorting).
  • FXToolWindow is the entry point: pick preset to create to (optionally) save prefab to (optionally) key position.
  • FXTrackManager owns animator/clip creation and low-level curve writing.
  • FXRuntimeThumbCapturer batch-renders thumbnails for any list of prefab paths and writes them to a cache (GUID-keyed).
  • URP Material Factory creates consistent materials that presets can reference.

 


Current limitations / “known” WIP

  • Track keying: only transform position is exposed; PS module animation is planned (Emission, Size, Speed, Color) either via safe bindings or a tiny runtime driver.
  • Thumbnail UX: batch list is passed by EditorPrefs (simple but brittle); a first-class “Library” grid with live cache state and folder filters is the next obvious polish.
  • Flipbook flow: presets carry rows/cols/FPS, but import helpers and sprite slicing UX are still to come.
  • Pooling & spawn rules: stubbed/roadmapped.

  • Visual library browser with thumbnail grid, folder dropdown, and Preview/Place actions.
  • Module animation (Emission/Size/Speed/Color) with robust 2021-friendly bindings.
  • Flipbook importer (rows/cols slicing + material hookup).
  • Pooling UI (prewarm/auto-despawn).
  • Strict structure validator and JSON import/export for presets. 

Here’s a concise script structure diagram outline for the Unity FX tool (Editor-side only). It maps responsibilities, key APIs, and dependencies between scripts so anyone can see how the pieces fit.


Top-level overview 


FXToolWindow (EditorWindow)

Role: Main entry point UI – browse presets, spawn prefabs, save prefabs, key basic motion.

  • Lists FXPreset assets from Assets/FXTool/Presets, duplicate/ping, pick active preset.

  • Buttons: Create From Preset, Save As Prefab (calls FXPresetApplier).

  • “Tracks” pane: keys position on the spawned root via FXTrackManager.

  • Depends on: FXPreset, FXPresetApplier, FXTrackManager, FXFolders.

  • Key code path (library + key):

[MenuItem("Tools/Particles/FXTool")] → ShowWindow()
DrawLibrary() → FindAssets("t:FXPreset") … select/duplicate
DrawInspectorAndTracks() → CreateFromPreset(), SaveAsPrefab()
→ FXTrackManager.EnsureAnimator/EnsureClip/KeyTransformPosition()


FXPreset (ScriptableObject)

Role: Source of truth for particle settings & identity.

  • Sections: Identity, Main, Emission, Shape, Renderer/Material/Sorting, Texture Sheet (Flipbook).

  • Referenced by FXPresetApplier to build a concrete PS.

  • Key fields: duration/looping/lifetime/speed/size, rateOverTime/bursts, shape/radius/angle, material/sorting, flipbook tiles & fps.

  • Snippet:


FXPresetApplier (static utility)

Role: Convert a FXPreset into a scene object (root + child PS) and save as prefab.

  • Builds named root & child using FXNaming; creates ParticleSystem.

  • Applies preset values to Main/Emission/Shape/Renderer; optional Texture Sheet Animation.

  • Saves to Assets/FXTool/Prefabs/<name>.prefab.

  • Snippet: create & apply + flipbook + save.


FXTrackManager (static API)

Role: Minimal “tracks” layer – ensures Animator/Clip; keys Transform.position curves.

  • Creates/assigns AnimatorController next to the prefab folder; ensures named clip.

  • Keys m_LocalPosition.{x,y,z} at time = frame/fps.

  • Extensible later for PS module animation.

  • Snippet: ensure & key.


FXRuntimeThumbCapturer (MonoBehaviour – Editor guarded)

Role: PlayMode helper that auto-captures thumbnails for any list of prefab paths.

  • Reads list from EditorPrefs (GUIDs or paths), spawns prefabs in front of Camera.main, warms PS, renders to RT, saves 256px PNG to Assets/FXTool/Cache/ParticleThumbs/<prefabGUID>.png, imports with sane settings.

  • Cleans up and stops Play Mode when done.

  • Snippet: instantiate → warm → capture → write PNG → stop.


FXLibraryMaterialTool (EditorWindow)

Role: Plain-English URP material builder for FX (independent of PS).

  • Inputs: Usage (Sprite, Billboard, Trails, UI), Blend (Alpha/Additive/Premultiplied/Multiply/Cutout), toggles (Double-Sided, ZWrite, Shadows, Emission), Base Map + Color.

  • Writes .mat to Assets/FXTool/Materials/<Category>/<Name>.mat, pings the asset.

  • Calls FXURPMaterialFactory.CreateOrUpdate(...) under the hood.

  • Snippet: UI → Create/Update call.


FXURPMaterialFactory (static)

Role: Centralizes URP surface/blend/keyword setup.

  • Applies Src/Dst blend, alpha variants, Cutout via _ALPHATEST_ON, Premultiplied via _ALPHAPREMULTIPLY_ON, toggles _SURFACE_TYPE_TRANSPARENT.

  • Called only by FXLibraryMaterialTool.

  • Snippet (keywords & blends):


FXNaming & FXFolders (static support)

Role: Consistent names and folder structure for everything.

  • FXNaming.BuildChildName(category,preset,role,i)FX_<Cat>_<Preset>_<Role>_##

  • FXFolders.EnsureAll() creates Assets/FXTool/{Presets,Prefabs,Thumbnails,Materials,Textures}.

  • Snippet: naming + ensure folders.


Dependency graph (editor)

FXToolWindow
 ├─ uses → FXPreset (list/select/duplicate)
 ├─ calls → FXPresetApplier (Create From Preset / Save As Prefab)
 └─ calls → FXTrackManager (EnsureAnimator, EnsureClip, KeyTransformPosition)

FXPresetApplier
 ├─ reads → FXPreset (values)
 ├─ uses → FXNaming (names), FXFolders (paths)
 └─ writes → Prefab asset

FXTrackManager
 └─ writes → AnimatorController + AnimationClip (curves on Transform)

FXLibraryMaterialTool
 └─ calls → FXURPMaterialFactory (CreateOrUpdate material)

FXRuntimeThumbCapturer (Play Mode, UNITY_EDITOR)
 ├─ reads → EditorPrefs capture list
 ├─ loads → Prefabs by path
 ├─ uses → Camera.main for RT capture
 └─ writes → PNG cache (GUID-keyed), reimports Texture


 

Monday, 9 December 2024

Added a Smooth Normals Slide Bar

Angle between normal of adjacent faces using the dot product:
dot(n1,n2)=∣n1∣⋅∣n2∣⋅cos⁡(θ)
dot(n1​,n2​)=∣n1​∣⋅∣n2​∣⋅cos(θ)
Dot Product and Angle - https://mathinsight.org/search/?q=dot+product
Vector Normalization - https://en.wikipedia.org/wiki/Unit_vector
OpenGL: Normals - https://learnopengl.com/Lighting/Basic-Lighting
Wikipedia: Normal - https://en.wikipedia.org/wiki/Normal_(geometry)
OpenGL : Smooth Shading - https://www.opengl.org/archives/resources/faq/technical/
Blender Manual: Auto Smooth - https://docs.blender.org/manual/en/latest/modeling/modifiers/normals/smooth_by_angle.html

glm::vec3 n1(1.0f, 0.0f, 0.0f); // Normal vector 1
glm::vec3 n2(0.0f, 1.0f, 0.0f); // Normal vector 2
float dotProduct = glm::dot(n1, n2); // Dot product
float magnitudeN1 = glm::length(n1); // Magnitudes
float magnitudeN2 = glm::length(n2);
float cosTheta = dotProduct / (magnitudeN1 * magnitudeN2);// Cosine of the angle
float angle = glm::degrees(std::acos(cosTheta));// Angle in degrees

Added Some MAYA Style Navigation Functionality

Added some "Maya Style" Navigation control:

 

# mousePressEvent = True
# mouseReleaseEvent = False
# Rotate camera
camera_rotation[0] += delta.y() * 0.2
camera_rotation[1] += delta.x() * 0.2
# Pan camera
camera_translation[0] += delta.x() * 0.01
camera_translation[1] -= delta.y() * 0.01
# Zoom camera
camera_translation[2] += delta.y() * 0.05

3D App Creation - A learning exercise

 Learning exercise - making a 3d app of some sort. Not sure what sort. c++ and python combo.

 


So far I've learnt about:
PyOpenGL = http://pyopengl.sourceforge.net/documentation/
GLFW = https://www.glfw.org/docs/latest/build_guide.html
vcpkg = automate dependency setup for GLFW and other libraries
vcpkg and CMake = https://vcpkg.io/en/getting-started.html
CMake Script FindGLFW3.cmake = https://cmake.org/cmake/help/latest/command/find_package.html
Wavefront OBJ = Details https://en.wikipedia.org/wiki/Wavefront_.obj_file
Lighting & Materials- OpenGL = https://learnopengl.com/Lighting/Basic-Lighting
Transformations - OpenGL = https://learnopengl.com/Getting-started/Transformations
Triangle Normal Calculation in OpenGL = https://learnopengl.com/Lighting/Basic-Lighting
OBJ Preprocessing for Compatibility = https://github.com/pywavefront/PyWavefront

Hardest part to solve - Exploding Vertices after obj import

Face Def / Quad Handling -
- OBJ files contain non-standard face definitions + use quads .
- Needed to convert quads to triangles = face definitions standardized.
- Had to remove unnecessary lines \ smoothing groups - simplify parsing.
Vertex Normalization -
- Needed to sort incorrect scaling or positioning due to varying coordinate extents = calculate (normalize) the center and maximum extent of the model
- needed to reposition and scaling vertices to fit within a view.
Missing Vertices -
- needed to use checks to skip invalid data = avoid crashing
Incomplete Triangles -
- The `pywavefront` library was, on my first attempt, returning only partial vertex data.
- Had to force triangles to adhered to a set length - for example the common graphics issue - avoiding malformed geometry.


Wednesday, 5 June 2024

learning exercise – animating explosions / projectiles with code and physics



Velocity: v0=v×(1+random.uniform(−rv,rv))v0=v×(1+random.uniform(−rv,rv))
Mass: mass=m×(1+random.uniform(−rm,rm))mass=m×(1+random.uniform(−rm,rm))
Launch Angle: random.uniform(0,360)random_angle=random.uniform(0,360)
Mess with time (bit slow to process but things look bad if not slowed down for aesthetics):
Δt=0.1speed_factor×(1+random.uniform(−time_randomize_bias,time_randomize_bias))Δt=speed_factor0.1×(1+random.uniform(−time_randomize_bias,time_randomize_bias))
Bias Launch Direction:.
angle_variation=(1−direction_bias)×90angle_variation=(1−direction_bias)×90 and
random_angle=random.uniform(launch_angle−angle_variation,launch_angle+angle_variation)random_angle=random.uniform(launch_angle−angle_variation,launch_angle+angle_variation).
Air Resistance: (1−air_resistance×Δt (delta t) /randomized_mass)
Blender testing – any 3d program really - :

Monday, 11 September 2023

Blender Auto rig Testing - Arm and Hand

 Blender Auto rig Testing- Still figuring some things out before I move on to the Auto Clavicle, Spine, Hips and Feet. I managed to reduce the code down to just a dictionary that sets all info for each key bone - drivers, constraints, bone rotation order, parent hierarchy, alignment to template etc) - so I have a method to refine now.

 


Sunday, 27 August 2023

Blender Arm/Leg Auto Setup Rig Module - WIP (Python Tool)

Messing with my blender auto rig (Python) tool. Making things work like they do in Maya slowly - Arm/Leg + Stretchy pull off controls + Auto in between Archs + FK/IK Switch + auto bone control curves.

 

Rotation distribution down the chain:



Friday, 10 February 2023

Blender Auto-Rig Add-on

A test for a  Blender Add-on I've been working on in what spare time I have. I've written most of the functions and classes I need to build any rig or animation tool. The fun part is being creative with them.  Early days. 



Saturday, 3 October 2020

Character controls in unity

I'm currently learning how to set up character controls in unity by writing little scripts and reading a few c# for unity books. No third person cam yet.... Just messing around at the moment but this is a lot of fun.

Wednesday, 30 September 2020

Sunday, 26 July 2020

Python Learning in Blender- Sine expression to Drive Bone Rotation


A quick python learning exercise in blender:
  1. Scripted various controls with arguments for names/limits/shape etc
  2. Scripted a bone chain definition with arguments for names/numberOfBones/transformPlacement/axis/O.O.R etc
  3. Created a script to iterated through the bones and deliver a sine expression using the controllers Y transform as input variables for the bones XYZ rotations - sin(frame*"freq+...etc")
Links:
Now - the expression needs a little work (ie: clamp(#,#,#) etc), before I can expand upon it.

Wednesday, 2 May 2018

Blender Add-on no:1 - Texture Set Manager

I wanted a quick way to load, name and update texture sets based on selected objects so I coded this in Blender.

...as well as in Maya and Max.

I'm finding it useful for quickly flicking through textures in dense scenes and for keeping all texture sets named and organized.   

Here is a flat shaded viewport screen grab of my text scene:
Render test:



 I can switch between image types( jpg,tif,png ), sizes( SM,MD,LG ) and image sets( type of 
wood, skin, metal... etc ).  

The process of making add-ons in blender is a relatively fast and simple one: