GithubHelp home page GithubHelp logo

doughamil / threeagent Goto Github PK

View Code? Open in Web Editor NEW
131.0 7.0 10.0 1.24 MB

ClojureScript library for building Three.js apps in a reagent-like fashion

License: MIT License

Clojure 96.42% HTML 0.54% Dockerfile 0.76% JavaScript 2.28%
clojurescript-library reagent threejs clojurescript clojure

threeagent's Introduction

threeagent

CircleCI cljdoc badge

ClojureScript library for building Three.js apps in a reagent-like fashion

Installation

Clojars Project

threeagent depends on THREE.js, so it will need to be added as a dependency to your project as well.

For shadow-cljs, install THREE.js using npm:

npm install --save three

For lein-cljsbuild, add an npm-deps entry on THREE.js:

:cljsbuild {:builds [{...
                      :compiler {...
                                 :install-deps true
                                 :npm-deps {:three "0.100.0"}}}]}

Example

(ns my-app.core
  (:require [threeagent.core :as th]))
  
;; Use reactive atom for storing state
(defonce state (th/atom {:ticks 0}))

;; Tick every second
(.setInterval js/window #(swap! state update :ticks inc) 1000)

;; Form-1 component example
(defn color-box [color size]
  [:box {:dims [size size size]
         :material {:color color}}])
         
;; Form-2 component example
(defn growing-sphere []
  (let [s (atom 0)]
    (.setInterval js/window #(swap! s inc) 5000)
    (fn []
      [:sphere {:radius @s}])))

;; Root component render function
(defn root []
  [:object {:position [1.0 0 -4.0]
            :rotation [0 (.sin js/Math (:ticks @state)) 0]} ; Rotate on Y axis based on :ticks
    [:ambient-light {:intensity 0.8}]
    [color-box "red" 1.0] ; Don't forget to use square brackets!
    [growing-sphere]])
           
           
;; Initialize and begin rendering threeagent scene
(defonce scene (th/render root (.-body js/document)))

Documentation

The latest documentation is available here

Development

Running Tests

Use shadow-cljs to watch unit tests

npx shadow-cljs watch test

Navigate to the HTTP server that shadow-cljs creates to view the test results

threeagent's People

Contributors

dependabot[bot] avatar doughamil avatar kaosko avatar yogthos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

threeagent's Issues

Just black boxes!

Just been playing with threeagent for the last half an hour/ hour and all I can see are black boxes in the browser. I tried to setup a minimal example with the following files:

<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
    <title>A test of threejs</title>
    <style>
        body {
        padding: 0;
        margin: 0;
        overflow: none;
    }

     #root {
         width: 100%;
         height: 100%;
     }
    </style>
</head>
<body>
    <div id="app"></div>
    <canvas id="root"></canvas>
    <script src="scripts/main.js" type='text/javascript'></script>
</body>
</html>

shadow etc:

{:source-paths ["src"]

 :deps   {:aliases [:dev]}
 :nrepl {:middleware [refactor-nrepl.middleware/wrap-refactor]}
 :builds
 {:app {:target :browser
        :output-dir "public/scripts"
        :asset-path "/scripts"
        :modules {:main {:init-fn basic.main/init}}
        :devtools {:repl-pprint true
                   :http-root "public"
                   :http-port 3000}}}}

deps

{:paths ["src"]

 :aliases {:dev
           {:extra-deps {thheller/shadow-cljs {:mvn/version "RELEASE"}}
            ;; binaryage/devtools {:mvn/version "RELEASE"}
            }}
 :deps  {org.clojure/clojure       {:mvn/version "RELEASE"}
         org.clojure/clojurescript {:mvn/version "RELEASE"}
         reagent/reagent {:mvn/version "RELEASE"}
         doughamil/threeagent {:mvn/version "RELEASE"}}}

main.cljs

(ns basic.main
  (:require [threeagent.core :as th]))

;; Use reactive atom for storing state
(defonce state (th/atom {:ticks 0}))

;; Tick every second
(.setInterval js/window #(swap! state update :ticks inc) 1000)

;; Form-1 component example
(defn color-box [color size]
  [:box {:width size
         :height size
         :depth size
         :material {:color color}}])

;; Form-2 component example
(defn growing-sphere []
  (let [s (atom 0)]
    (.setInterval js/window #(swap! s inc) 5000)
    (fn []
      [:sphere {:radius @s}])))

;; Root component render function
(defn root []
  [:object {:position [1.0 0 0.0]
            :rotation [0 (js/Math.sin (:ticks @state)) 0]}
   [color-box "red" 1.0] ; Don't forget to use square brackets!
   [growing-sphere]])


;; Initialize and begin rendering threeagent scene


(defn init []
  (th/render root (.getElementById js/document "root")))

(defn ^:dev/after-reload on-code-reload [] ;; For hot-code reloading, just call the render function again
  (th/render root (.getElementById js/document "root")))

I'm not getting any errors in the console apart from:
"The character encoding of the HTML document was not declared. The document will render with garbled text in some browser configurations if the document contains characters from outside the US-ASCII range. The character encoding of the page must be declared in the document or in the transfer protocol." which of course I assume is unrelated.

I tried the example on the github page and the example in the doc but no joy. I had a flick through the source and believe that the package is automatically adding a renderer but is it just that there's a camera missing or not in the right place or something simple?

I've never used three.js before. Any help would be much appreciated :).

Thanks.

v1.0 - Doc Updates

To prepare for the v1.0 release update the documentation:

  • ISystem usage
  • IEntityType and IUpdateableEntityType usage
  • threeagent.core/render usage
  • Add doc-strings to all built-in elements
  • Context usage
  • :ref, :on-added, :on-removed usage
  • Migration guide for v1.0

v1.0 - IEntity protocol

Replace the defcomponent macro with IEntity protocol.

The IEntity protocol allows users to register custom entities with threeagent. IEntity implementations will then be passed in to the threeagent.core/render function as a map, in the same manner as ISystem. This should provide a more consistent dev experience, and allows for more flexibility in defining new types of entities.

The protocol should have these methods:

  • create - creates an instance of this entity type, which should be a ThreeJS object
  • destroy - destroys an instance of this entity type
  • update - does an in-place update of an instance of this entity type, provided the original ThreeJS object and updated parameters

All built-in defcomponent should be replaced with this protocol.

Threeagent clashes with reagent

If you attempt to render a Threeagent component as a child of a reagent component, an error is thrown. It seems like reagent attempts to turn the threeagent primitive into an html element (e.g., :box => <box />). This makes sense, so I'm wondering if there's a good way to integrate threeagent with reagent.

I thought about having reagent render the parent container/canvas, and then having threeagent manually mount/render to the canvas. I'd like to avoid that if there's a cleaner option.

I can't figure out how to make a transparent sphere

var material = new THREE.MeshPhysicalMaterial( {
color: params.color,
metalness: 0,
roughness: 0,
alphaMap: texture,
alphaTest: 0.5,
envMap: hdrCubeRenderTarget.texture,
envMapIntensity: params.envMapIntensity,
depthWrite: false,
transparency: params.transparency, // use material.transparency for glass materials
opacity: 1, // set material.opacity to 1 when material.transparency is non-zero
transparent: true
} );
Is what the three.js website says I should do.

However the stacktrace in ChromeDevTools says
image

Error when wrapping root fn

(defn root-inner []
  [:object])

(defn root-outer []
  [root-inner])

(render root-outer (.-body js/document))

:octahedron runtime error

When I try to draw an octahedron I get the following runtime error:

Uncaught TypeError: module$node_modules$three$build$three_cjs.Octahedron is not a constructor

Here's my setup:

(defn threeagent-root
  []
  [:object
   [:ambient-light {:intensity 1.50}]
   [:box {:position [2 2 -5]}]
   [:dodecahedron
    {:position [0 0 -5]
     :radius 1}]
   [:octahedron
    {:position [-1 -2 -5]
     :radius 1}]])

(defn threeagent-scene
  [root-fn]
  (let [ref (react/createRef)]
    (r/create-class
      {:display-name "threeagent"
       :reagent-render (fn [] [:canvas {:ref ref}])
       :component-did-mount (fn [_] (th/render root-fn (.-current ref)))
       :component-did-update (fn [_] (th/render root-fn (.-current ref)))})))

(defn root
  []
  [:div
   [:h4 "Embedded Threagent Scene"]
   [threeagent-scene threeagent-root]])

(defn ^:dev/after-load init
  []
  (rdom/render root (.getElementById js/document "app")))

I'm using the following:
shadow-cljs 2.25.2
threeagent 1.0.1
three 0.152.0

I've also tried with the newest version of three, 0.155.0, with the same result.

Support custom Renderers

Discussed in #45

Originally posted by hkjels December 2, 2021
I believe three.js is possible to use with react-native, but I guess the render-method of threeagent will have to be changed right?

Allow a pre-created ThreeJS Renderer instance to be provided to the threeagent.core/render opts map. When a renderer is provided, no DOM canvas should be created.

form three component?

In my explorations, I decided to ape stuff I did with Vega and implement a form 3 component that wraps the setup from the examples. The hope was to manage all of my web page via reagent (and the scene with threeagent), with the threeagent merely being a component handled outside of react. I am not a reagent genius though (mildly experienced, able to copy and extend). My solution worked okay:

(defn three-canvas [name f on-before-render]
  (r/create-class
   {:display-name (str name)
    :reagent-render (fn [] [:canvas])
    :component-did-mount
    (fn [this]
       (threehelp/render f (rdom/dom-node this) {:render-params {:antialias true :resize true}
                                                     :on-before-render on-before-render}))
    :component-did-update
    (fn [this]
      (threehelp/render f (rdom/dom-node this) {:render-params {:antialias true :resize true}
                                                     :on-before-render on-before-render}))}))

Used

(defn app [ratom]
  [:div.header {:style {:display "flex" :flex-direction "column" :width "100%" :height "100%"}}
   [:div {:id "chart-root" :style {:display "flex"}}
    [:div {:style {:flex "1" :width "100%"}}
     [v/vega-chart "ltn-plot" v/ltn-spec]]]
   [:div  {:style {:display "flex" :width "100%" :height  "auto"  :class "fullSize" :overflow "hidden"
                   :justify-content "space-between"}}
;;here....
    [three-canvas "root-scene" scene (fn [dt] (swap! ratom tick-scene))]]
   [:div.header  {:style {:display "flex" :width "100%" :height  "auto"  :class "fullSize" :overflow "hidden"
                          :justify-content "space-between"
                          :font-size "xxx-large"}}
    [:p {:id "c-day" :style {:margin "0 auto" :text-align "center" }}
     ;;no idea why this causes a slow memory leak!
     (str "C-Day:"  (int @c-day))
     ]]])

This works great, except when I have a dynamic text element (the C-Day label in the hiccup above) that is updating, I get awful performance and an eventual memory leak. If the text data doesn't change, performance is great. I am suspecting there is something going on with lifecycle methods and the like.

full janky source here https://github.com/joinr/threeagentdemo/blob/master/src/threeagentdemo/core.cljs
The threehelp ns is just a wrapper around the original threeagent.impl.scene to allow some stuff like setting up pxiel ratios and other rendering setup (maybe unnecessary now), also trying to work on responsive resizing of the canvas (pending).

Any ideas? I am revisiting the reagent docs to get more skilled on form 3 components. I noticed that in all the existing threeagent examples, you specify different divs in the index.html, and then have different reagent mounting points for the reagent scene and the ui. My attempted design tries to unify them; maybe you went with the separation for a reason.

To be able to access the ref

I'd suggest this kind of implementation (providing ref attr) for users to be able to access the THREE.js objects so they can update other attributes besides those that were given.

(defcomponent :perspective-camera [{:keys [fov aspect near far active ref]
                                    :or {fov 75.0
                                         aspect 1.0
                                         near 0.1
                                         far 2000.0
                                         active true}}]
  (let [cam (three/PerspectiveCamera. fov aspect near far)]
    (set! (.-active cam) active)
    (when ref (ref cam))
    cam))

v1.0 - Context/Provider mechanism

Provide a way to pass a context to child entities.

  • Context should be specified in hiccup
  • Context should be passed to all relevant lifecycle methods
  • Context should be a clj map, and thus is immutable.
  • When the context is changed, all children are marked for re-render

Garbage Leak?

I am working with a scene that's allocating and deallocating sprites quite a bit, although I have cached the materials use for the sprites. So I "think" the virtual scene is adding and removing nodes quite often. Over time, I am getting a pretty big memory leak (on the order of gigs, building from < 100 mb), despite having what should be more-or-less constant average memory usage. It looks like the implementation in virtual scene is not calling dispose anywhere. Reading three.js docs, it seems like this may be necessary (particularly in a reactive context where the scene graph is changing). Any ideas? I am looking at manually patching in some calls to dispose to see if it fixes my problem.

v1.0 - ISystem protocol

Provide an ISystem protocol which gives users an easier way to define custom behavior across entities. This protocol can help in cases where users need to access the threeagent-managed ThreeJS objects by reducing the clutter from on-added/on-removed hooks.

All ISystem implementations will be passed as a map to the threeagent.core/render function.

Adding fog to a scene

What is the recommended way to add fog to a scene? Can I pass the values to the renderer or is it something I should use interop to add after setting up?

v1.0 - Expose default camera

For convenience in cases where the user relies on the threeagent-created default camera, return the default camera under :default-camera key in the threeagent.core/render return value.

Controls

Hi, it's not an issue, it's just that I am not able to figure out how would I go about implementing a control like TrackballControl.
Thanks.

Maybe adopt (or document) a quil-like workflow of setup, update, draw functions

As I look at more examples, I think this is already there. It was not obvious to me that the output of render actually provides the context so that you can mess with it (adjust the camera, renderer, etc). This is akin to setup in quil (setup-scene in the tetris demo kind of gets at it https://github.com/DougHamil/threeagent-examples/blob/master/tetris/src/main/tetris/app.cljs#L72). The draw call is already there in the api more or less. I think draw/update are sort of interwoven at the moment (it seems like draw is expected to perform any updates like ticking prior to rendering). This might form a familiar model for folks coming in without three.js background, but conceptually understanding the concept of a sketch.

I am also looking at a simpler, pseudo-synchronous way to handle assets (fonts, textures, models, etc.). Basically have the setup logic load everything that's needed, then have the draw function able to make the assumption that everything is accessible from the state. It would preclude the potential mishmash of resource checks like the one for text's dependency on font presence in https://github.com/DougHamil/threeagent-examples/blob/master/tetris/src/main/tetris/app.cljs#L27

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.