diff --git a/src/engine.clj b/src/engine.clj index 3701efa..93c7763 100644 --- a/src/engine.clj +++ b/src/engine.clj @@ -10,6 +10,12 @@ (defonce texture-cache (atom {})) ;; (reset! texture-cache {}) +(defn load-texture-once [id path] + (or (@texture-cache path) + (let [t (assoc (rl/load-texture path) :name id)] + (swap! texture-cache assoc path t) + t))) + (defn load-texture-once [id path] (or (@texture-cache path) (let [t (assoc (rl/load-texture path) :name id)] diff --git a/src/rl.clj b/src/rl.clj index 6c6d71a..f6839e1 100644 --- a/src/rl.clj +++ b/src/rl.clj @@ -265,8 +265,11 @@ ;; Called once at startup, so the map-taking form is fine. (defcfn load-texture-raw* "LoadTexture" [::mem/c-string] ::texture) +(defcfn load-image-raw* "LoadImage" [::mem/c-string] ::image) +(defcfn image-from-image-raw* "ImageFromImage" [::image ::rectangle] ::image) (defcfn load-texture-from-image "LoadTextureFromImage" [::image] ::texture) (defcfn unload-texture! "UnloadTexture" [::texture] ::mem/void) +(defcfn unload-image! "UnloadImage" [::image] ::mem/void) (defcfn set-texture-filter! "SetTextureFilter" [::texture ::mem/int] ::mem/void) (defcfn get-font-default "GetFontDefault" [] ::font) @@ -285,6 +288,10 @@ (ffi/make-downcall "MeasureTextEx" [::font ::mem/c-string ::mem/float ::mem/float] ::vector-2)) +(def ^:private get-image-color-raw!* + (ffi/make-downcall "GetImageColor" + [::image ::mem/int ::mem/int] ::color)) + ;;; --------------------------------------------------- macros (defmacro with-drawing! @@ -349,6 +356,9 @@ (defn vec2-seg [x y] (mem/serialize {:x (float x) :y (float y)} ::vector-2 arena)) +(defn image-seg [img] + (mem/serialize img ::image arena)) + (defn texture-seg [tex] (mem/serialize tex ::texture arena)) @@ -356,6 +366,22 @@ (let [tex (load-texture-raw* path)] (assoc tex :seg (texture-seg tex)))) +(defn load-image [path] + (let [img (load-image-raw* path)] + (assoc img :seg (image-seg img)))) + +(defn image-from-image [img {:keys [x y width height]}] + (let [cropped (image-from-image-raw* + img + {:x (float x) :y (float y) + :width (float width) :height (float height)})] + (assoc cropped :seg (image-seg cropped)))) + +(defn get-image-color!* [img x y] + (mem/deserialize + (get-image-color-raw!* arena (:seg img) (int x) (int y)) + ::color)) + (defn n-patch-info-seg [{:keys [source left top right bottom layout]}] (mem/serialize {:source source :left (int left) :top (int top) :right (int right) :bottom (int bottom) :layout (int layout)} diff --git a/src/sprite_atlas.clj b/src/sprite_atlas.clj index c459910..afefe8c 100644 --- a/src/sprite_atlas.clj +++ b/src/sprite_atlas.clj @@ -6,7 +6,9 @@ [engine :as e] [rl :as rl] [watch :as w]) - (:import (java.awt FileDialog Frame))) + (:import + (java.io File) + (java.awt FileDialog Frame))) (def screen-width 1280) (def screen-height 630) @@ -14,20 +16,18 @@ ;; (def screen-height 490) (def cell-size 16) -(defn sprite-atlas-path [] - (let [asset-path "./source-assets/Sprout Lands Premium/" - level-deco "Objects/Mushrooms, Flowers, Stones.png"] - (str asset-path level-deco))) +(def sprite-atlas-path "./source-assets/Sprout Lands Premium/Objects/Mushrooms, Flowers, Stones.png") (defn zoom-to-fit [{:keys [width height]}] (min (/ screen-width (double width)) (/ screen-height (double height)))) (defn init-sprite-atlas [config] - (let [atlas (e/load-texture-once :main-atlas (sprite-atlas-path))] + (let [atlas (e/load-texture-once :main-atlas sprite-atlas-path)] {:zoom (zoom-to-fit atlas) :selected-cells #{} :atlas atlas + :atlas-img (rl/load-image "./source-assets/Sprout Lands Premium/Objects/Mushrooms, Flowers, Stones.png") :atlas-pos [0 0] :drag-delta [0 0]})) @@ -38,6 +38,19 @@ end-y (max ay by)] {:x start-x :y start-y :w (- end-x start-x) :h (- end-y start-y)})) +(defn auto-select-tiles [{:keys [atlas-img atlas]}] + ;; TODO: This should come from game, so should cell-size + (let [rows (quot (:height atlas) cell-size) + cols (quot (:width atlas) cell-size)] + (into #{} + (for [r (range rows) c (range cols) + :when (let [rect {:x (* c cell-size) :y (* r cell-size) :width cell-size :height cell-size} + sub-img (rl/image-from-image atlas-img rect) + all-zero (all-zero? sub-img)] + (rl/unload-image! sub-img) + (not all-zero))] + [r c])))) + (defn handle-input [_ game] (update-as-> [g game] (when (rl/key-pressed? rl/key-r) @@ -45,23 +58,30 @@ ;; TODO: We want to add auto sprite sheet gen, so first detect non-empty, and we populate with that ;; then we have a selection box that allows grouping (when (rl/key-pressed? rl/key-enter) - (let [d (doto (FileDialog. (Frame.) "Open" FileDialog/LOAD) + (let [d (doto (FileDialog. (Frame.) "Save" FileDialog/SAVE) ;; TODO: Maybe add a check if the directory exists? (.setDirectory (str (System/getProperty "user.dir") "/assets")) + (.setFile (change-ext (.getName (File. sprite-atlas-path)) "edn")) (.setVisible true))] (when-let [f (.getFile d)] - (println (str (.getDirectory d) f))) + (let [base-path (System/getProperty "user.dir") + full-path (str (.getDirectory d) f)] + (spit full-path (pr-str {:texture-path (str (.relativize (.toPath (io/file base-path)) + (.toPath (io/file full-path)))) + :selected-cells (:selected-cells g)})))) game) #_(this should be to write the sprite sheet to some path)) (when (rl/key-pressed? rl/key-zero) (assoc g :zoom (zoom-to-fit (:atlas g)))) + (when (rl/key-pressed? rl/key-a) + (assoc g :selected-cells (auto-select-tiles g))) + (when (rl/mouse-button-pressed? rl/mouse-button-left) (cond-> (assoc g :drag-start-pos (rl/get-mouse-position) :current-mouse-pos (rl/get-mouse-position) :drag-mode :selecting) - (not (rl/key-down? rl/key-left-shift)) (assoc :selected-cells #{}))) (when (rl/mouse-button-released? rl/mouse-button-left) @@ -76,6 +96,9 @@ [ax ay] (:atlas-pos g)] [(+ ax dx) (+ ay dy)])) (dissoc :drag-start-pos :current-mouse-pos :drag-mode :drag-delta))) + ;; TODO: We need to delete tiles + #_(when (rl/mouse-button-pressed? rl/mouse-button-middle) + (disj )) (let [move (rl/get-mouse-wheel-move)] (when (not (zero? move)) (let [new-scale (max 1.0 ((if (pos? move) * /) (:zoom g) 1.2)) @@ -112,15 +135,15 @@ h (+ (- y0 start-y) (- y1 y0)) row-count (inc (quot h curr-cell-size)) col-count (inc (quot w curr-cell-size))] - (w/spy :info [row-count col-count]) (when (> (* w h) 256) - (set (for [r (range row-count) - c (range col-count)] - [(+ start-row r) (+ start-col c)]))))) - + (into (:selected-cells g) + (for [r (range row-count) + c (range col-count)] + [(+ start-row r) (+ start-col c)]))))) :dragging (assoc g :drag-delta (let [{mx :x my :y} (rl/get-mouse-position) {:keys [x y]} (:drag-start-pos g)] [(- mx x) (- my y)])))))) +(into #{[1 2]} [[1 2] [2 3]] ) (defn draw-sprite-atlas [_ {:keys [atlas atlas-pos drag-delta zoom drag-mode drag-start-pos current-mouse-pos selected-cells]}] (rl/clear-background!* rl/black) @@ -171,7 +194,8 @@ :atlas-pos (:atlas-pos game) #_#_:drag-delta (:drag-delta game)}) -(defn unload-sprite-atlas [game]) +(defn unload-sprite-atlas [game] + (rl/unload-image! (:atlas-img game))) (defn -main [& _args] (engine/run-game! diff --git a/src/tilemap_blob.clj b/src/tilemap_blob.clj index 46f0e5e..c47e19e 100644 --- a/src/tilemap_blob.clj +++ b/src/tilemap_blob.clj @@ -89,7 +89,7 @@ {} tiles) -(comment +(comment ;; #C0D470 ;; #A4C263 ;; #78A158 diff --git a/src/util.clj b/src/util.clj index 24f5ec4..6af4408 100644 --- a/src/util.clj +++ b/src/util.clj @@ -1,11 +1,24 @@ (ns util (:require - [clojure.pprint :as pprint])) + [clojure.pprint :as pprint]) + (:import (java.lang.foreign MemorySegment ValueLayout))) (defn spy ([x] (pprint/pprint x) x) ([tag x] (pprint/pprint [tag x]) x)) +(defn change-ext [path ext] + (str (clojure.string/replace path #"\.[^./\\]*$" "") "." ext)) + +(defn all-zero? [{:keys [data width height]}] + (let [n (* 4 (long width) (long height)) + px (.reinterpret ^MemorySegment data n)] + (loop [i 0] + (cond + (>= i n) true + (zero? (.get px ValueLayout/JAVA_INT i)) (recur (+ i 4)) + :else false)))) + (defmacro do-grid [[r rows c cols] & body] `(dotimes [~r ~rows] (dotimes [~c ~cols]