commit 1c7a26136b79467e0307299a634de539e4809299 Author: Jeremy Penner Date: Thu Nov 21 09:09:02 2013 -0500 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cc2ff39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/target +/lib +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +.lein-deps-sum +.lein-failures +.lein-plugins +.lein-repl-history diff --git a/README.md b/README.md new file mode 100644 index 0000000..b1e0fbe --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# cko2dae + +FIXME: description + +## Installation + +Download from http://example.com/FIXME. + +## Usage + +FIXME: explanation + + $ java -jar cko2dae-0.1.0-standalone.jar [args] + +## Options + +FIXME: listing of options this app accepts. + +## Examples + +... + +### Bugs + +... + +### Any Other Sections +### That You Think +### Might be Useful + +## License + +Copyright © 2013 FIXME + +Distributed under the Eclipse Public License, the same as Clojure. diff --git a/doc/intro.md b/doc/intro.md new file mode 100644 index 0000000..e5d0a54 --- /dev/null +++ b/doc/intro.md @@ -0,0 +1,3 @@ +# Introduction to cko2dae + +TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/) diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..1ebdec7 --- /dev/null +++ b/project.clj @@ -0,0 +1,8 @@ +(defproject cko2dae "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :dependencies [[org.clojure/clojure "1.5.1"] + [org.clojure/data.xml "0.0.7"]] + :main cko2dae.core) diff --git a/src/cko2dae/core.clj b/src/cko2dae/core.clj new file mode 100644 index 0000000..9c59c6e --- /dev/null +++ b/src/cko2dae/core.clj @@ -0,0 +1,149 @@ +(ns cko2dae.core + (:require clojure.string + [clojure.data.xml :as xml] + fs.core) + (:import java.text.SimpleDateFormat + java.util.Calendar + java.util.TimeZone) + (:gen-class)) + +(defrecord vec3 [x y z]) +(defrecord mat [colour]) +(defrecord poly [rgvec3 mat]) +(defrecord cube [vec3 mat]) + +(defn rgpoly-quad [v1 v2 v3 v4 mat] + [(->poly [v1 v2 v3] mat) (->poly [v3 v2 v4] mat)]) + +(defn rgpoly-cube [vec3 mat] + (let [{:keys [x y z]} vec3 + x_ (+ 1 x), y_ (+ 1 y), z_ (+ 1 z) + qFront (rgpoly-quad (->vec3 x y z) (->vec3 x y_ z) (->vec3 x_ y z) (->vec3 x_ y_ z) mat) + qBack (rgpoly-quad (->vec3 x y z_) (->vec3 x_ y z_) (->vec3 x y_ z_) (->vec3 x_ y_ z_) mat) + qTop (rgpoly-quad (->vec3 x y_ z) (->vec3 x y_ z_) (->vec3 x_ y_ z) (->vec3 x_ y_ z_) mat) + qBot (rgpoly-quad (->vec3 x y z) (->vec3 x_ y z) (->vec3 x y z_) (->vec3 x_ y z_) mat) + qLeft (rgpoly-quad (->vec3 x y z) (->vec3 x y z_) (->vec3 x y_ z) (->vec3 x y_ z_) mat) + qRight (rgpoly-quad (->vec3 x_ y z) (->vec3 x_ y_ z) (->vec3 x_ y z_) (->vec3 x_ y_ z_) mat)] + (concat qTop qBot qFront qBack qLeft qRight))) + +(defn dedup [rgval] + (loop [i 0 + rgval rgval + mpval_i {} + rgval_result []] + (if (seq rgval) + (let [val (first rgval)] + (if (nil? (get mpval_i val)) + (recur (inc i) (next rgval) (assoc mpval_i val i) (conj rgval_result val)) + (recur i (next rgval) mpval_i rgval_result))) + [mpval_i rgval_result]))) + +(defn dae-vec3-array [rgvec3 id x y z] + (let [rgfloat (apply concat (map (fn [{:keys [x y z]}] [x y z]) rgvec3))] + [:source {:id id} + [:float_array {:id (str id "_array") :count (count rgfloat)} + (clojure.string/join " " rgfloat)] + [:technique_common {} + [:accessor {:source (str "#" id "_array") :count (count rgvec3) :stride 3} + [:param {:name x :type "float"}] + [:param {:name y :type "float"}] + [:param {:name z :type "float"}]]]])) + +(defn dae-geom-from-rgpoly [rgpoly id name] + (let [rgtri (apply concat (map :rgvec3 rgpoly)) + [mpvec3_i rgvec3] (dedup rgtri) + points_id (str id "_points") + vertices_id (str id "_vertices")] + [:geometry {:id id :name name} + [:mesh {} + (dae-vec3-array rgvec3 points_id "X" "Y" "Z") + ;todo: normals, textures + [:vertices {:id vertices_id} + [:input {:semantic "POSITION" :source (str "#" points_id)}]] + [:triangles {:count (count rgpoly)} + [:input {:semantic "VERTEX" :source (str "#" vertices_id) :offset 0}] + [:p {} (clojure.string/join " " (map #(get mpvec3_i %) rgtri))]]]])) + +(defn now + "Returns current ISO 8601 compliant date." + [] + (let [f (SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ss'Z'")] + (.setTimeZone f (TimeZone/getTimeZone "GMT")) + (.format f (.getTime (Calendar/getInstance))))) + +(defn dae-from-desc [libs scene] + (-> [:COLLADA {:xmlns "http://www.collada.org/2005/11/COLLADASchema" :version "1.4.1"} + [:asset + [:created {} (now)] + [:modified {} (now)] + [:up_axis {} "Y_UP"]]] + (into + (for [[lib nodes] libs] + (into [(keyword (str "library_" (name lib))) {}] nodes))) + (conj [:scene {} scene]))) + +(defn dae-from-rgpoly [rgpoly name] + (dae-from-desc {:geometries [(dae-geom-from-rgpoly rgpoly (str "id_" name) name)] + :visual_scenes [[:visual_scene {:id (str "id_" name "_scene") :name (str name "_scene")} + [:node {:id (str name "_node") :name name :type "NODE"} + [:instance_geometry {:url (str "#id_" name)}]]]]} + [:instance_visual_scene {:url (str "#id_" name "_scene")}])) + +(defn parse-number + "Reads a number from a string. Returns nil if not a number." + [s] + (if (re-find #"^-?\d+\.?\d*$" s) + (read-string s))) + +(defn rgcube-from-cko [cko] + (let [[header version count & rgstcube] (clojure.string/split-lines cko)] + + ; todo check header, version, count + (for [stcube rgstcube] + (let [[x y z mat] (clojure.string/split stcube (re-pattern " +"))] + (->cube (->vec3 (parse-number x) (parse-number y) (parse-number z)) (->mat mat)))))) + +(defn rgpoly-from-rgcube [rgcube] + (apply concat (for [{:keys [vec3 mat]} rgcube] (rgpoly-cube vec3 mat)))) + +(defn name-from-filename [filename] + (first (fs.core/split-ext filename))) + +(defn filename-with-ext [filename ext] + (let [extPrev (second (fs.core/split-ext filename)) + filenameStripped (subs filename 0 (- (count filename) (count extPrev)))] + (str filenameStripped ext))) + +(defn convert-cko-to-dae [filenameIn] + (let [filenameOut (filename-with-ext filenameIn ".dae") + name (name-from-filename filenameIn)] + (-> (slurp filenameIn) + rgcube-from-cko + rgpoly-from-rgcube + (write-dae name filenameOut)))) + +(defn write-dae [rgpoly name filename] + (let [xml (-> rgpoly + (dae-from-rgpoly name) + xml/sexp-as-element + xml/emit-str)] + (spit filename xml))) + +(defn write-test-cube [] + (write-dae (rgpoly-cube (->vec3 0 0 0) nil) "cube" "C:/dev/unity/cube.dae")) + +(defn -main + "I don't do a whole lot ... yet." + [& args] + (let [xml (-> + ;(slurp "C:/dev/unity/w.cko") + ;rgcube-from-cko + ;rgpoly-from-rgcube + (rgpoly-cube (->vec3 0 0 0) nil) + (dae-from-rgpoly "cube") + xml/sexp-as-element + xml/emit-str)] + (spit "C:/dev/unity/cube.dae" xml))) + +;(convert-cko-to-dae "C:/dev/unity/genie-hand.cko") +;(write-test-cube)