Skip to content
Permalink
Browse files

Fix particle system, make a nice fireworks workbench.

  • Loading branch information...
Shinmera committed Aug 2, 2019
1 parent f79249c commit 9162e871bd9a470c21dfabfd8b52207b3746a18c
Showing with 77 additions and 51 deletions.
  1. +1 −1 controller.lisp
  2. +36 −29 particle.lisp
  3. +8 −0 toolkit.lisp
  4. +32 −21 workbench.lisp
@@ -71,7 +71,7 @@
for i from 0 below (length observers)
for (title . func) = (aref observers i)
when func
do (restart-case (format stream "~%~a:~16t~a" title (funcall func ev))
do (restart-case (format stream "~%~a:~12t~a" title (funcall func ev))
(remove-observer ()
:report "Remove the offending observer."
(setf (aref observers i) NIL))))
@@ -13,13 +13,12 @@
for binding in (bindings vao)
when (listp binding)
maximize (getf (rest binding) :index i)))
(data (make-array (* (+ 3 3 1) max-particles) :element-type 'single-float
(data (make-array (* (+ 1 3 3) max-particles) :element-type 'single-float
:initial-element 0.0f0))
(vbo (make-instance 'vertex-buffer :buffer-data data :data-usage :stream-draw)))
(push (list vbo :index (+ 1 idx) :offset (* 0 4) :size 3 :stride (* 7 4) :instancing 1) (bindings vao))
(push (list vbo :index (+ 2 idx) :offset (* 3 4) :size 3 :stride (* 7 4) :instancing 1) (bindings vao))
(push (list vbo :index (+ 3 idx) :offset (* 6 4) :size 1 :stride (* 7 4) :instancing 1) (bindings vao))
;; location: vec3, velocity: vec3, lifetime: float
(push (list vbo :index (+ 1 idx) :offset (* 0 4) :size 2 :stride (* 8 4) :instancing 1) (bindings vao))
(push (list vbo :index (+ 2 idx) :offset (* 2 4) :size 3 :stride (* 8 4) :instancing 1) (bindings vao))
(push (list vbo :index (+ 3 idx) :offset (* 5 4) :size 3 :stride (* 8 4) :instancing 1) (bindings vao))
(values vao (+ 1 idx))))

(define-shader-subject particle-emitter (bakable)
@@ -43,40 +42,48 @@
(gl:bind-vertex-array (gl-name vao))
(%gl:draw-elements-instanced (vertex-form vao) (size vao) :unsigned-int 0 (live-particles emitter))))

(defgeneric initial-particle-state (emitter tick location velocity)) ; => lifetime
(defgeneric update-particle-state (emitter tick location velocity lifetime)) ; => lifetime
(defgeneric initial-particle-state (emitter tick location velocity lifetime))
(defgeneric update-particle-state (emitter tick location velocity lifetime))
(defgeneric new-particle-count (emitter tick)) ; => N

(define-handler (particle-emitter tick) (ev)
(declare (optimize speed))
(let* ((vbo (vertex-buffer particle-emitter))
(data (buffer-data vbo))
(location (vec 0 0 0))
(velocity (vec 0 0 0))
(lifetime 0.0f0)
(lifetime (vec 0 0))
(write-offset 0))
(declare (type (simple-array single-float (*)) data))
(declare (type (unsigned-byte 32) write-offset))
(labels ((read-particle (offset)
(vsetf location (aref data (+ 0 offset)) (aref data (+ 1 offset)) (aref data (+ 2 offset)))
(vsetf velocity (aref data (+ 3 offset)) (aref data (+ 4 offset)) (aref data (+ 5 offset))))
(setf (vx3 location) (aref data (+ 2 offset)))
(setf (vy3 location) (aref data (+ 3 offset)))
(setf (vz3 location) (aref data (+ 4 offset)))
(setf (vx3 velocity) (aref data (+ 5 offset)))
(setf (vy3 velocity) (aref data (+ 6 offset)))
(setf (vz3 velocity) (aref data (+ 7 offset))))
(write-particle (offset)
(setf (aref data (+ 0 offset)) (vx3 location))
(setf (aref data (+ 1 offset)) (vy3 location))
(setf (aref data (+ 2 offset)) (vz3 location))
(setf (aref data (+ 3 offset)) (vx3 velocity))
(setf (aref data (+ 4 offset)) (vy3 velocity))
(setf (aref data (+ 5 offset)) (vz3 velocity))
(setf (aref data (+ 6 offset)) (coerce lifetime 'single-float))))
(loop for read-offset from 0 below (* 7 (live-particles particle-emitter)) by 7
do (setf lifetime (aref data (+ 6 read-offset)))
(when (< 0 lifetime)
(setf (aref data (+ 0 offset)) (vx2 lifetime))
(setf (aref data (+ 1 offset)) (vy2 lifetime))
(setf (aref data (+ 2 offset)) (vx3 location))
(setf (aref data (+ 3 offset)) (vy3 location))
(setf (aref data (+ 4 offset)) (vz3 location))
(setf (aref data (+ 5 offset)) (vx3 velocity))
(setf (aref data (+ 6 offset)) (vy3 velocity))
(setf (aref data (+ 7 offset)) (vz3 velocity))
(incf write-offset 8)))
(loop for read-offset from 0 below (* 8 (live-particles particle-emitter)) by 8
do (setf (vx2 lifetime) (aref data (+ 0 read-offset)))
(setf (vy2 lifetime) (aref data (+ 1 read-offset)))
(when (< (vx2 lifetime) (vy2 lifetime))
(read-particle read-offset)
(setf lifetime (update-particle-state particle-emitter ev location velocity lifetime))
(write-particle write-offset)
(incf write-offset 7)))
(update-particle-state particle-emitter ev location velocity lifetime)
(when (< (vx2 lifetime) (vy2 lifetime))
(write-particle write-offset))))
(loop repeat (new-particle-count particle-emitter ev)
while (< write-offset (length data))
do (setf lifetime (initial-particle-state particle-emitter ev location velocity))
(write-particle write-offset)
(incf write-offset 7))
(setf (live-particles particle-emitter) (/ write-offset 7))
(with-pointer-to-vector-data (ptr data)
(update-buffer-data/ptr vbo ptr write-offset)))))
do (initial-particle-state particle-emitter ev location velocity lifetime)
(write-particle write-offset))
(setf (live-particles particle-emitter) (/ write-offset 8))
(update-buffer-data vbo data))))
@@ -31,6 +31,14 @@
(error (err) (declare (ignore err))
:unavailable)))

(defun polar->cartesian (vec)
(etypecase vec
(vec2 (vec2 (* (vx2 vec) (cos (vy2 vec)))
(* (vx2 vec) (sin (vy2 vec)))))
(vec3 (vec3 (* (vx3 vec) (cos (vy3 vec)) (sin (vz3 vec)))
(* (vx3 vec) (sin (vy3 vec)) (sin (vz3 vec)))
(* (vx3 vec) (cos (vz3 vec)))))))

(defmethod apply-class-changes ((class standard-class)))

(defmethod apply-class-changes :before ((class standard-class))
@@ -6,10 +6,6 @@
(define-pool workbench
:base 'trial)

(define-asset (workbench particles) mesh
(make-particle-storage (make-cube 5) :vertex-attributes '(location))
:data-usage :stream-draw)

(define-asset (workbench grid) mesh
(make-line-grid 10 200 200))

@@ -19,23 +15,33 @@

(define-shader-subject fireworks (particle-emitter)
()
(:default-initargs :vertex-array (asset 'workbench 'particles)))
(:default-initargs :name :fireworks
:vertex-array (make-particle-storage (make-sphere 2)
:max-particles 16384
:vertex-attributes '(location))))

(defmethod initial-particle-state ((fireworks fireworks) tick loc vel)
(defmethod initial-particle-state ((fireworks fireworks) tick loc vel life)
(vsetf loc 0 0 0)
(vsetf vel (- (random 1.0) 0.5) 3.0 (- (random 1.0) 0.5))
(nv* vel 2)
(+ 1.0 (random 1.0)))
(flet ((hash (x)
(sxhash x)))
(let ((dir (polar->cartesian (vec2 (/ (hash (fc tick)) (ash 2 60)) (mod (hash (fc tick)) 100)))))
(vsetf vel (vx dir) (+ 2.5 (mod (hash (fc tick)) 2)) (vy dir))))
(vsetf life 0 (+ 3.0 (random 1.0))))

(defmethod update-particle-state ((fireworks fireworks) tick loc vel life)
(nv+ loc vel)
;;(decf (vy3 vel) 0.1)
(- life (dt tick)))
(decf (vy3 vel) 0.005)
(when (< (vy loc) 0)
(setf (vy vel) (- (vy vel)))
(setf (vy loc) 0))
(when (< (abs (- (vx life) 2.5)) 0.05)
(let ((dir (polar->cartesian (vec3 (+ 1.5 (random 0.125)) (random (* 2 PI)) (random (* 2 PI))))))
(vsetf vel (vx dir) (vy dir) (vz dir))))
(incf (vx2 life) (dt tick)))

(defmethod new-particle-count ((fireworks fireworks) tick)
(if (= 0 (mod (fc tick) 60))
100000
0))
(if (= 0 (mod (fc tick) (* 10 1)))
128 0))

(defmethod paint :before ((fireworks fireworks) (pass shader-pass))
(let ((program (shader-program-for-pass pass fireworks)))
@@ -45,40 +51,45 @@

(define-class-shader (fireworks :vertex-shader)
"layout (location = 0) in vec3 vtx_location;
layout (location = 1) in vec3 location;
layout (location = 2) in vec3 velocity;
layout (location = 3) in float lifetime;
layout (location = 1) in vec2 lifetime;
layout (location = 2) in vec3 location;
layout (location = 3) in vec3 velocity;
uniform mat4 model_matrix;
uniform mat4 view_matrix;
uniform mat4 projection_matrix;
out PARTICLE_DATA{
vec2 lifetime;
vec3 location;
vec3 velocity;
float lifetime;
} particle_out;
void main(){
vec3 position = vtx_location + location;
gl_Position = projection_matrix * view_matrix * model_matrix * vec4(position, 1.0f);
particle_out.lifetime = lifetime;
particle_out.location = location;
particle_out.velocity = velocity;
particle_out.lifetime = lifetime;
}")

(define-class-shader (fireworks :fragment-shader)
"out vec4 color;
in PARTICLE_DATA{
vec2 lifetime;
vec3 location;
vec3 velocity;
float lifetime;
} particle;
void main(){
color = vec4(clamp(particle.lifetime, 0, 1));
if(particle.lifetime.x <= 2.5)
color = vec4(1);
else{
float lt = particle.lifetime.y-particle.lifetime.x;
color = vec4(lt*2, lt, 0, 1);
}
}")

(progn

0 comments on commit 9162e87

Please sign in to comment.
You can’t perform that action at this time.