(defn
torus
[{:keys
[radius
thickness
radial-subdivisions
body-subdivisions
start-angle
end-angle],
:or {start-angle 0, end-angle (* 2 (math PI))}}]
(let
[angle-range
(- end-angle start-angle)
radial-parts
(inc radial-subdivisions)
body-parts
(inc body-subdivisions)
num-vertices
(* radial-parts body-parts)]
(->
(fn
[m slice]
(let
[v
(/ slice body-subdivisions)
slice-angle
(* v (math PI) 2)
slice-sin
(math sin slice-angle)
ring-radius
(+ radius (* slice-sin thickness))
ny
(math cos slice-angle)
y
(* ny thickness)]
(reduce
(fn
[m ring]
(let
[u
(/ ring radial-subdivisions)
ring-angle
(+ start-angle (* u angle-range))
x-sin
(math sin ring-angle)
z-cos
(math cos ring-angle)
x
(* x-sin ring-radius)
z
(* z-cos ring-radius)
nx
(* x-sin slice-sin)
nz
(* z-cos slice-sin)]
(->
m
(update
:positions
(fn
[positions]
(-> positions (conj! x) (conj! y) (conj! z))))
(update
:normals
(fn [normals] (-> normals (conj! nx) (conj! ny) (conj! nz))))
(update
:texcoords
(fn [texcoords] (-> texcoords (conj! u) (conj! (- 1 v))))))))
m
(range radial-parts))))
(reduce
{:positions (transient []),
:normals (transient []),
:texcoords (transient [])}
(range body-parts))
(assoc
:indices
(reduce
(fn
[indices slice]
(reduce
(fn
[indices ring]
(let
[next-ring-index (inc ring) next-slice-index (inc slice)]
(->
indices
(conj! (+ (* radial-parts slice) ring))
(conj! (+ (* radial-parts next-slice-index) ring))
(conj! (+ (* radial-parts slice) next-ring-index))
(conj! (+ (* radial-parts next-slice-index) ring))
(conj! (+ (* radial-parts next-slice-index) next-ring-index))
(conj! (+ (* radial-parts slice) next-ring-index)))))
indices
(range radial-subdivisions)))
(transient [])
(range body-subdivisions)))
(update :positions persistent!)
(update :normals persistent!)
(update :texcoords persistent!)
(update :indices persistent!))))