(ns cortex.movement "Give simulated creatures defined in special blender files the power to move around in a simulated environment." {:author "Robert McIntyre"} (:use (cortex world util sense body)) (:import java.awt.image.BufferedImage) (:import com.jme3.scene.Node) (:import com.jme3.math.Vector3f) (:import com.jme3.bullet.control.RigidBodyControl)) (in-ns 'cortex.movement) (def ^{:doc "Return the children of the creature's \"muscles\" node." :arglists '([creature])} muscles (sense-nodes "muscles")) (defn muscle-profile-image "Get the muscle-profile image from the node's blender meta-data." [#^Node muscle] (if-let [image (meta-data muscle "muscle")] (load-image image))) (defn muscle-strength "Return the strength of this muscle, or 1 if it is not defined." [#^Node muscle] (if-let [strength (meta-data muscle "strength")] strength 1)) (defn motor-pool "Return a vector where each entry is the strength of the \"motor neuron\" at that part in the muscle." [#^Node muscle] (let [profile (muscle-profile-image muscle)] (vec (let [width (.getWidth profile)] (for [x (range width)] (- 255 (bit-and 0x0000FF (.getRGB profile x 0)))))))) (in-ns 'cortex.movement) (defn movement-kernel "Returns a function which when called with a integer value inside a running simulation will cause movement in the creature according to the muscle's position and strength profile. Each function returns the amount of force applied / max force." [#^Node creature #^Node muscle] (let [target (closest-node creature muscle) axis (.mult (.getWorldRotation muscle) Vector3f/UNIT_Y) strength (muscle-strength muscle) pool (motor-pool muscle) pool-integral (reductions + pool) forces (vec (map #(float (* strength (/ % (last pool-integral)))) pool-integral)) control (.getControl target RigidBodyControl)] ;;(println-repl (.getName target) axis) (fn [n] (let [pool-index (max 0 (min n (dec (count pool)))) force (forces pool-index)] (.applyTorque control (.mult axis force)) (float (/ force strength)))))) (defn movement! "Endow the creature with the power of movement. Returns a sequence of functions, each of which accept an integer value and will activate their corresponding muscle." [#^Node creature] (for [muscle (muscles creature)] (movement-kernel creature muscle))) (defn movement-display-kernel "Display muscle exertion data as a bar filling up with red." [exertion] (let [height 20 width 300 image (BufferedImage. width height BufferedImage/TYPE_INT_RGB) fill (min (int (* width exertion)) width)] (dorun (for [x (range fill) y (range height)] (.setRGB image x y 0xFF0000))) image)) (defn view-movement "Creates a function which accepts a list of muscle-exertion data and displays each element of the list to the screen." [] (view-sense movement-display-kernel))