├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── code_of_conduct.md ├── env ├── dev │ ├── clj │ │ └── gampg │ │ │ └── dev.clj │ └── cljs │ │ └── gampg │ │ └── main.cljs ├── prod │ ├── clj │ │ └── gampg │ │ │ └── dev.clj │ └── cljs │ │ └── gampg │ │ └── main.cljs └── test │ ├── js │ ├── polyfill.js │ └── unit-test.js │ └── unit-test.html ├── project.clj ├── resources ├── index.html └── public │ ├── css │ ├── style.css │ └── vendor │ │ └── bootstrap │ │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ └── bootstrap.min.css │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ ├── images │ ├── crate.gif │ ├── earth-specular.gif │ ├── earth.jpg │ ├── glass.gif │ ├── hardwood.jpg │ ├── metal.jpg │ ├── moon.gif │ ├── mud.gif │ ├── nehe.gif │ ├── skybox │ │ ├── citadella │ │ │ ├── negx.jpg │ │ │ ├── negy.jpg │ │ │ ├── negz.jpg │ │ │ ├── posx.jpg │ │ │ ├── posy.jpg │ │ │ ├── posz.jpg │ │ │ └── readme.txt │ │ └── sky1 │ │ │ ├── negx.png │ │ │ ├── negy.png │ │ │ ├── negz.png │ │ │ ├── posx.png │ │ │ ├── posy.png │ │ │ └── posz.png │ └── star.gif │ ├── index.html │ ├── models │ ├── apartment_2.gltf │ ├── blue_egg_cube.gltf │ ├── chair.gltf │ ├── duck.gltf │ ├── red_egg_cube.gltf │ └── steve.gltf │ └── videos │ └── Firefox.ogv ├── src ├── clj │ └── gampg │ │ └── server.clj └── cljs │ └── gampg │ ├── core.cljs │ ├── gltf.cljs │ ├── learn_gamma │ ├── apartment.cljs │ ├── gltf.cljs │ ├── lesson_01.cljs │ ├── lesson_02.cljs │ ├── lesson_03.cljs │ ├── lesson_04.cljs │ ├── lesson_05.cljs │ ├── lesson_06.cljs │ ├── lesson_07.cljs │ ├── lesson_08.cljs │ ├── lesson_09.cljs │ ├── lesson_10.cljs │ ├── lesson_11.cljs │ ├── lesson_12.cljs │ ├── lesson_13.cljs │ ├── lesson_14.cljs │ ├── lesson_15.cljs │ ├── lesson_16.cljs │ ├── lesson_17.cljs │ ├── lesson_18.cljs │ ├── lesson_19.cljs │ ├── lesson_20.cljs │ ├── programs.cljs │ └── shadows_notes.txt │ └── utils.cljs ├── system.properties └── test ├── clj └── gampg │ └── example_test.clj └── cljs └── gampg ├── core-test.cljs └── test-runner.cljs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | /resources/public/js 11 | /out 12 | /.repl 13 | yaks 14 | .DS_Store 15 | .idea 16 | .repl* 17 | resources/public/advanced 18 | resources/public/images/apartment 19 | resources/public/models/*.dae 20 | resources/public/models/apartment_2/** 21 | resources/public/models/corner_couch 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java $JVM_OPTS -cp target/gampg.jar clojure.main -m gampg.server 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gamma Playground 2 | 3 | [![Join the chat at https://gitter.im/kovasb/gamma](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kovasb/gamma?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | Some examples of how to use [Gamma](https://github.com/kovasb/gamma) and [Gamma Driver](https://github.com/kovasb/gamma-driver). In particular, it contains the code translations of the popular [Learn WebGL Series](http://learningwebgl.com/blog/?page_id=1217) 6 | 7 | Currently lessons 1-10 are ported. Please feel free to take a shot at porting the remaining 6, it's far easier than you might think! Head over to [Issues](https://github.com/sgrove/gamma-playground/issues) to see the remaining lessons. 8 | 9 | ## Warning 10 | Please don't take the state of this code as finished - feel free to clean it up, send pull requests, and open issues. This should become a highly-polished introduction to gamma, from beginner usage to advanced. 11 | 12 | ## Further warning 13 | 14 | This code is in *heavy* flux as both usage of Gamma and Gamma Driver, and the projects themselves, are ironed out. Examples may be slightly broken or slow at any given time until all three projects settle down a bit. Feel free to open an issue if one of the examples doesn't work, with a detailed list of repro-steps. 15 | 16 | ## Development 17 | 18 | Open a terminal and type `lein repl` to start a Clojure REPL 19 | (interactive prompt). 20 | 21 | In the REPL, type 22 | 23 | ```clojure 24 | (run) 25 | (browser-repl) 26 | ``` 27 | 28 | The call to `(run)` does two things, it starts the webserver at port 29 | 10555, and also the Figwheel server which takes care of live reloading 30 | ClojureScript code and CSS. Give them some time to start. 31 | 32 | Running `(browser-repl)` starts the Weasel REPL server, and drops you 33 | into a ClojureScript REPL. Evaluating expressions here will only work 34 | once you've loaded the page, so the browser can connect to Weasel. 35 | 36 | When you see the line `Successfully compiled "resources/public/app.js" 37 | in 21.36 seconds.`, you're ready to go. Browse to 38 | `http://localhost:10555` and enjoy. 39 | 40 | **Attention: It is not longer needed to run `lein figwheel` 41 | separately. This is now taken care of behind the scenes** 42 | -------------------------------------------------------------------------------- /code_of_conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect 4 | all people who contribute through reporting issues, posting feature 5 | requests, updating documentation, submitting pull requests or patches, 6 | and other activities. 7 | 8 | We are committed to making participation in this project a 9 | harassment-free experience for everyone, regardless of level of 10 | experience, gender, gender identity and expression, sexual 11 | orientation, disability, personal appearance, body size, race, age, or 12 | religion. 13 | 14 | Examples of unacceptable behavior by participants include the use of 15 | sexual language or imagery, derogatory comments or personal attacks, 16 | trolling, public or private harassment, insults, or other 17 | unprofessional conduct. 18 | 19 | Project maintainers have the right and responsibility to remove, edit, 20 | or reject comments, commits, code, wiki edits, issues, and other 21 | contributions that are not aligned to this Code of Conduct. Project 22 | maintainers who do not follow the Code of Conduct may be removed from 23 | the project team. 24 | 25 | Instances of abusive, harassing, or otherwise unacceptable behavior 26 | may be reported by opening an issue or contacting one or more of the 27 | project maintainers. 28 | 29 | This Code of Conduct is adapted from the 30 | [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, 31 | available at 32 | [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 33 | -------------------------------------------------------------------------------- /env/dev/clj/gampg/dev.clj: -------------------------------------------------------------------------------- 1 | (ns gampg.dev 2 | (:require [environ.core :refer [env]] 3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]] 4 | [cemerick.piggieback :as piggieback] 5 | [weasel.repl.websocket :as weasel] 6 | [figwheel-sidecar.auto-builder :as fig-auto] 7 | [figwheel-sidecar.core :as fig] 8 | [clojurescript-build.auto :as auto] 9 | [clojure.java.shell :refer [sh]])) 10 | 11 | (def is-dev? (env :is-dev)) 12 | 13 | (def inject-devmode-html 14 | (comp 15 | (set-attr :class "is-dev") 16 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}])) 17 | (prepend (html [:script {:type "text/javascript" :src "/react/react.inc.js"}])) 18 | (append (html [:script {:type "text/javascript"} "goog.require('gampg.main')"])))) 19 | 20 | (defn browser-repl [] 21 | (let [repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)] 22 | (piggieback/cljs-repl repl-env) 23 | ;;(piggieback/cljs-eval repl-env '(in-ns 'gampg.core) {}) 24 | )) 25 | 26 | (defn start-figwheel [] 27 | (let [server (fig/start-server { :css-dirs ["resources/public/css"] }) 28 | config {:builds [{:id "dev" 29 | :source-paths ["env/dev/cljs" "src/cljs"] 30 | :compiler {:output-to "resources/public/js/app.js" 31 | :output-dir "resources/public/js/out" 32 | :source-map "resources/public/js/out.js.map" 33 | :source-map-timestamp true 34 | :preamble ["react/react.min.js"]}}] 35 | :figwheel-server server}] 36 | (fig-auto/autobuild* config))) 37 | -------------------------------------------------------------------------------- /env/dev/cljs/gampg/main.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.main 2 | (:require [gampg.core :as core] 3 | [figwheel.client :as figwheel :include-macros true] 4 | [cljs.core.async :refer [put!]] 5 | [weasel.repl :as weasel])) 6 | 7 | (enable-console-print!) 8 | 9 | (figwheel/watch-and-reload 10 | :websocket-url "ws://localhost:3449/figwheel-ws" 11 | :jsload-callback (fn [] 12 | (core/main))) 13 | 14 | (weasel/connect "ws://localhost:9001" :verbose true :print #{:repl :console}) 15 | 16 | (core/main) 17 | -------------------------------------------------------------------------------- /env/prod/clj/gampg/dev.clj: -------------------------------------------------------------------------------- 1 | (ns gampg.dev 2 | (:require [environ.core :refer [env]])) 3 | 4 | (if (env :is-dev) 5 | (throw (Exception. (str "Production environment code is being loaded while the dev environment is active. " 6 | "You likely have compiled class files lying around from an uberjar build. " 7 | "Remove the target/ directory and try again.")))) 8 | 9 | (def is-dev? false) 10 | (def inject-devmode-html identity) 11 | (defn browser-repl [] 12 | (throw (Exception. "Browser connected REPL is not available in prod mode"))) 13 | (defn start-figwheel [] 14 | (throw (Exception. "Figwheel is not available in prod mode"))) 15 | -------------------------------------------------------------------------------- /env/prod/cljs/gampg/main.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.main 2 | (:require [gampg.core :as core])) 3 | 4 | (core/main) 5 | -------------------------------------------------------------------------------- /env/test/js/polyfill.js: -------------------------------------------------------------------------------- 1 | if (!Function.prototype.bind) { 2 | Function.prototype.bind = function(oThis) { 3 | if (typeof this !== 'function') { 4 | // closest thing possible to the ECMAScript 5 5 | // internal IsCallable function 6 | throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); 7 | } 8 | 9 | var aArgs = Array.prototype.slice.call(arguments, 1), 10 | fToBind = this, 11 | fNOP = function() {}, 12 | fBound = function() { 13 | return fToBind.apply(this instanceof fNOP && oThis 14 | ? this 15 | : oThis, 16 | aArgs.concat(Array.prototype.slice.call(arguments))); 17 | }; 18 | 19 | fNOP.prototype = this.prototype; 20 | fBound.prototype = new fNOP(); 21 | 22 | return fBound; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /env/test/js/unit-test.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | var url = phantom.args[0]; 3 | 4 | page.onConsoleMessage = function (message) { 5 | console.log(message); 6 | }; 7 | 8 | function exit(code) { 9 | setTimeout(function(){ phantom.exit(code); }, 0); 10 | phantom.onError = function(){}; 11 | } 12 | 13 | console.log("Loading URL: " + url); 14 | 15 | page.open(url, function (status) { 16 | if (status != "success") { 17 | console.log('Failed to open ' + url); 18 | phantom.exit(1); 19 | } 20 | 21 | console.log("Running test."); 22 | 23 | var result = page.evaluate(function() { 24 | return gampg.test_runner.runner(); 25 | }); 26 | 27 | if (result != 0) { 28 | console.log("*** Test failed! ***"); 29 | exit(1); 30 | } 31 | else { 32 | console.log("Test succeeded."); 33 | exit(0); 34 | } 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /env/test/unit-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject gampg "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | 7 | :source-paths ["src/clj"] 8 | :repl-options {:timeout 200000} ;; Defaults to 30000 (30 seconds) 9 | 10 | :test-paths ["spec/clj"] 11 | 12 | :dependencies [[cljsjs/react "0.13.3-0"] 13 | [compojure "1.3.1"] 14 | [enlive "1.1.5"] 15 | [environ "1.0.0"] 16 | [fipp "0.6.2"] 17 | [instaparse "1.4.0"] 18 | [markdown-clj "0.9.66"] 19 | [org.omcljs/om "0.8.8"] 20 | [org.clojure/clojure "1.7.0-beta2"] 21 | [org.clojure/clojurescript "0.0-3291"] 22 | [org.clojure/core.async "0.1.346.0-17112a-alpha"] 23 | [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] 24 | [org.clojure/google-closure-library-third-party "0.0-20150505-021ed5b3"] 25 | [ring "1.3.2"] 26 | [ring/ring-defaults "0.1.3"] 27 | [thi.ng/geom "0.0.783" :exclusions [com.google.guava/guava 28 | com.google.javascript/closure-compiler 29 | org.clojure/clojure 30 | org.clojure/clojurescript]]] 31 | 32 | :plugins [[lein-cljsbuild "1.0.3"] 33 | [lein-environ "1.0.0"]] 34 | 35 | :min-lein-version "2.5.0" 36 | 37 | :uberjar-name "gampg.jar" 38 | 39 | :main "gampg.server" 40 | 41 | :cljsbuild {:builds {:app {:source-paths ["src/cljs" "yaks/gamma/src" "yaks/gamma-driver/src"] 42 | :compiler {:output-to "resources/public/js/app.js" 43 | :output-dir "resources/public/js/out" 44 | :source-map "resources/public/js/out.js.map" 45 | :preamble ["cljsjs/development/react.inc.js"] 46 | :optimizations :none 47 | :pretty-print true}} 48 | :advanced {:source-paths ["src/cljs" "yaks/gamma/src" "yaks/gamma-driver/src"] 49 | :compiler {:output-to "resources/public/advanced/app.js" 50 | :output-dir "resources/public/advanced/out" 51 | :source-map "resources/public/advanced/out.js.map" 52 | :preamble ["cljsjs/production/react.min.inc.js"] 53 | :optimizations :advanced 54 | :pseudo-names false 55 | :elide-asserts true 56 | :pretty-print false}}}} 57 | 58 | :profiles {:dev {:source-paths ["src/cljs" "env/dev/clj" "yaks/gamma/src" "yaks/gamma-driver/src"] 59 | :test-paths ["test/clj"] 60 | 61 | :dependencies [ ;;[figwheel "0.2.1-SNAPSHOT"] 62 | [figwheel "0.3.3"] 63 | ;;[figwheel-sidecar "0.2.1-SNAPSHOT"] 64 | [figwheel-sidecar "0.3.3"] 65 | ;;[com.cemerick/piggieback "0.1.3"] 66 | [com.cemerick/piggieback "0.2.1"] 67 | ;;[weasel "0.4.2"] 68 | [weasel "0.7.0-SNAPSHOT"] 69 | [org.clojure/tools.nrepl "0.2.10"] 70 | ] 71 | 72 | :repl-options {:init-ns gampg.server 73 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]} 74 | 75 | :plugins [ ;;[lein-figwheel "0.2.1-SNAPSHOT"] 76 | [lein-figwheel "0.3.3"] 77 | [cider/cider-nrepl "0.9.0-SNAPSHOT"] 78 | [refactor-nrepl "1.0.5"]] 79 | 80 | :figwheel {:http-server-root "public" 81 | :server-port 3449 82 | :nrepl-port 7888 83 | :css-dirs ["resources/public/css"]} 84 | 85 | :env {:is-dev true} 86 | 87 | :cljsbuild {:test-commands { "test" ["phantomjs" "env/test/js/unit-test.js" "env/test/unit-test.html"] } 88 | :builds {:app {:source-paths ["src/cljs" "env/dev/cljs" "yaks/gamma/src" "yaks/gamma-driver/src"]} 89 | :test {:source-paths ["src/cljs" "test/cljs" "yaks/gamma/src" "yaks/gamma-driver/src"] 90 | :compiler {:output-to "resources/public/js/app_test.js" 91 | :output-dir "resources/public/js/test" 92 | :source-map "resources/public/js/test.js.map" 93 | :preamble ["cljsjs/development/react.inc.js"] 94 | :optimizations :whitespace 95 | :pretty-print false}} 96 | :prod {:id "prod" 97 | :source-paths ["src/cljs" "env/dev/cljs" "yaks/gamma/src" "yaks/gamma-driver/src"] 98 | :compiler {:asset-path "/js/bin" 99 | :main gampg.core 100 | :output-to "resources/public/js/bin/main.js" 101 | :output-dir "resources/public/js/bin" 102 | :optimizations :advanced 103 | ;;:pretty-print true 104 | :preamble ["cljsjs/production/react.min.inc.js"] 105 | :externs ["cljsjs/common/react.ext.js" 106 | "resources/externs/webvr.js"]}}}}} 107 | 108 | :uberjar {:source-paths ["env/prod/clj"] 109 | :hooks [leiningen.cljsbuild] 110 | :env {:production true} 111 | :omit-source true 112 | :aot :all 113 | :cljsbuild {:builds {:app 114 | {:source-paths ["env/prod/cljs"] 115 | :compiler 116 | {:optimizations :advanced 117 | :pretty-print false}}}}}}) 118 | -------------------------------------------------------------------------------- /resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /resources/public/css/style.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | text-decoration: underline; 3 | } 4 | -------------------------------------------------------------------------------- /resources/public/css/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/css/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /resources/public/css/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/css/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /resources/public/css/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/css/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /resources/public/css/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/css/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /resources/public/css/vendor/bootstrap/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /resources/public/images/crate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/crate.gif -------------------------------------------------------------------------------- /resources/public/images/earth-specular.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/earth-specular.gif -------------------------------------------------------------------------------- /resources/public/images/earth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/earth.jpg -------------------------------------------------------------------------------- /resources/public/images/glass.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/glass.gif -------------------------------------------------------------------------------- /resources/public/images/hardwood.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/hardwood.jpg -------------------------------------------------------------------------------- /resources/public/images/metal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/metal.jpg -------------------------------------------------------------------------------- /resources/public/images/moon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/moon.gif -------------------------------------------------------------------------------- /resources/public/images/mud.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/mud.gif -------------------------------------------------------------------------------- /resources/public/images/nehe.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/nehe.gif -------------------------------------------------------------------------------- /resources/public/images/skybox/citadella/negx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/citadella/negx.jpg -------------------------------------------------------------------------------- /resources/public/images/skybox/citadella/negy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/citadella/negy.jpg -------------------------------------------------------------------------------- /resources/public/images/skybox/citadella/negz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/citadella/negz.jpg -------------------------------------------------------------------------------- /resources/public/images/skybox/citadella/posx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/citadella/posx.jpg -------------------------------------------------------------------------------- /resources/public/images/skybox/citadella/posy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/citadella/posy.jpg -------------------------------------------------------------------------------- /resources/public/images/skybox/citadella/posz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/citadella/posz.jpg -------------------------------------------------------------------------------- /resources/public/images/skybox/citadella/readme.txt: -------------------------------------------------------------------------------- 1 | Author 2 | ====== 3 | 4 | This is the work of Emil Persson, aka Humus. 5 | http://www.humus.name 6 | 7 | 8 | 9 | License 10 | ======= 11 | 12 | This work is licensed under a Creative Commons Attribution 3.0 Unported License. 13 | http://creativecommons.org/licenses/by/3.0/ 14 | -------------------------------------------------------------------------------- /resources/public/images/skybox/sky1/negx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/sky1/negx.png -------------------------------------------------------------------------------- /resources/public/images/skybox/sky1/negy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/sky1/negy.png -------------------------------------------------------------------------------- /resources/public/images/skybox/sky1/negz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/sky1/negz.png -------------------------------------------------------------------------------- /resources/public/images/skybox/sky1/posx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/sky1/posx.png -------------------------------------------------------------------------------- /resources/public/images/skybox/sky1/posy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/sky1/posy.png -------------------------------------------------------------------------------- /resources/public/images/skybox/sky1/posz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/skybox/sky1/posz.png -------------------------------------------------------------------------------- /resources/public/images/star.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/images/star.gif -------------------------------------------------------------------------------- /resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /resources/public/models/blue_egg_cube.gltf: -------------------------------------------------------------------------------- 1 | {"accessors":{"accessor_16":{"bufferView":"bufferView_42","byteOffset":72,"byteStride":0,"componentType":5123,"count":1584,"type":"SCALAR"},"accessor_18":{"bufferView":"bufferView_43","byteOffset":576,"byteStride":12,"componentType":5126,"count":266,"max":[0.6491528749465942,1.0073283910751343,1.101951003074646],"min":[-0.7716494798660278,-0.41347137093544006,-0.7308824062347412],"type":"VEC3"},"accessor_20":{"bufferView":"bufferView_43","byteOffset":3768,"byteStride":12,"componentType":5126,"count":266,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},"accessor_36":{"bufferView":"bufferView_42","byteOffset":0,"byteStride":0,"componentType":5123,"count":36,"type":"SCALAR"},"accessor_38":{"bufferView":"bufferView_43","byteOffset":0,"byteStride":12,"componentType":5126,"count":24,"max":[2.969158411026001,2.337104797363,1.2389001846313477],"min":[0.7755280137062073,-1.9679564237594604,0],"type":"VEC3"},"accessor_40":{"bufferView":"bufferView_43","byteOffset":288,"byteStride":12,"componentType":5126,"count":24,"max":[0.9751499891281128,1,1],"min":[-0.9922239780426025,-1,-1],"type":"VEC3"}},"animations":{},"asset":{"generator":"collada2gltf@","premultipliedAlpha":true,"profile":"WebGL 1.0.2","version":0.800000011920929},"bufferViews":{"bufferView_42":{"buffer":"model","byteLength":3240,"byteOffset":0,"target":34963},"bufferView_43":{"buffer":"model","byteLength":6960,"byteOffset":3240,"target":34962}},"buffers":{"model":{"byteLength":10200,"type":"arraybuffer","uri":"data:application/octet-stream;base64,AAABAAIAAQAAAAMABAAFAAYABQAEAAcACAAJAAoACQAIAAsADAANAA4ADQAMAA8AEAARABIAEQAQABMAFAAVABYAFQAUABcAAAABAAIAAAADAAEABAABAAUAAQAEAAIAAAACAAYAAAAHAAMAAQAIAAUACAABAAMACQAFAAoABQAJAAQACwACAAQAAgALAAYAAAAGAAwAAAANAAcAAwAOAAgADgADAAcABQAPAAoADwAFAAgACQAQABEAEAAJAAoAEgAEAAkABAASAAsAEwAGAAsABgATAAwAAAAMABQAAAAVAA0ABwAWAA4AFgAHAA0ACAAXAA8AFwAIAA4ADwAQAAoAEAAPABgAEQAZABoAGQARABAAEgARABsAEQASAAkAHAALABIACwAcABMAHQAMABMADAAdABQAAAAUAB4AAAAfABUADQAgABYAIAANABUADgAhABcAIQAOABYAFwAYAA8AGAAXACIAGAAZABAAGQAYACMAGgAkACUAJAAaABkAGwAaACYAGgAbABEAHAAbACcAGwAcABIAKAATABwAEwAoAB0AKQAUAB0AFAApAB4AAAAeACoAAAArAB8AFQAsACAALAAVAB8AFgAtACEALQAWACAAIQAiABcAIgAhAC4AIgAjABgAIwAiAC8AIwAkABkAJAAjADAAJAAxACUAMQAkADIAJgAlADMAJQAmABoAJwAmADQAJgAnABsAKAAnADUAJwAoABwANgAdACgAHQA2ACkANwAeACkAHgA3ACoAOAAAACoAOQArAAAAHwA6ACwAOgAfACsAIAA7AC0AOwAgACwAIQA8AC4APAAhAC0ALgAvACIALwAuAD0ALwAwACMAMAAvAD4AMAAyACQAMgAwAD8AMgBAADEAQAAyAEEAJQBCADMAQgAlADEANAAzAEMAMwA0ACYANQA0AEQANAA1ACcANgA1AEUANQA2ACgARgApADYAKQBGADcARwAqADcAKgBHADgASAAAADgASQA5AAAAOQA6ACsAOgA5AEoALABLADsASwAsADoALQBMADwATAAtADsALgBNAD0ATQAuADwAPQA+AC8APgA9AE4AMABPAD8ATwAwAD4APwBBADIAQQA/AFAAQQBRAEAAUQBBAFIAMQBTAEIAUwAxAEAAMwBUAEMAVAAzAEIARABDAFUAQwBEADQARQBEAFYARABFADUARgBFAFcARQBGADYAWAA3AEYANwBYAEcARwBIADgASABHAFkAWgAAAEgAWwBJAAAASQBKADkASgBJAFwASgBLADoASwBKAF0AOwBeAEwAXgA7AEsAPABfAE0AXwA8AEwAPQBgAE4AYAA9AE0APgBhAE8AYQA+AE4APwBiAFAAYgA/AE8AUABSAEEAUgBQAGMAZABRAFIAUQBkAGUAQABmAFMAZgBAAFEAQgBnAFQAZwBCAFMAVQBUAGgAVABVAEMAVgBVAGkAVQBWAEQAVwBWAGoAVgBXAEUAWABXAGsAVwBYAEYAWABZAEcAWQBYAGwAWQBaAEgAWgBZAG0AbgAAAFoAbwBbAAAAWwBcAEkAXABbAHAAXABdAEoAXQBcAHEASwByAF4AcgBLAF0ATABzAF8AcwBMAF4ATQB0AGAAdABNAF8ATgB1AGEAdQBOAGAATwB2AGIAdgBPAGEAUAB3AGMAdwBQAGIAYwBkAFIAZABjAHgAeQBlAGQAZQB5AHoAZQBmAFEAZgBlAHsAUwB8AGcAfABTAGYAaABnAH0AZwBoAFQAaQBoAH4AaABpAFUAagBpAH8AaQBqAFYAawBqAIAAagBrAFcAbABrAIEAawBsAFgAbABtAFkAbQBsAIIAbQBuAFoAbgBtAIMAhAAAAG4AhQBvAAAAbwBwAFsAcABvAIYAcABxAFwAcQBwAIcAXQCIAHIAiABdAHEAXgCJAHMAiQBeAHIAXwCKAHQAigBfAHMAYACLAHUAiwBgAHQAYQCMAHYAjABhAHUAYgCNAHcAjQBiAHYAdwB4AGMAeAB3AI4AeAB5AGQAeQB4AI8AkAB6AHkAegB7AGUAewB6AJEAewB8AGYAfAB7AJIAfQB8AJMAfAB9AGcAfgB9AJQAfQB+AGgAfwB+AJUAfgB/AGkAgAB/AJYAfwCAAGoAgQCAAJcAgACBAGsAggCBAJgAgQCCAGwAggCDAG0AgwCCAJkAgwCEAG4AhACDAJoAmwAAAIQAmwCFAAAAhQCGAG8AhgCFAJwAhgCHAHAAhwCGAJ0AcQCeAIgAngBxAIcAcgCfAIkAnwByAIgAcwCgAIoAoABzAIkAdAChAIsAoQB0AIoAdQCiAIwAogB1AIsAdgCjAI0AowB2AIwAjQCOAHcAjgCNAKQAjgCPAHgAjwCOAKUAkAB5AI8AkACRAHoAkQCSAHsAkgCRAKYAkgCTAHwAkwCSAKcAlACTAKgAkwCUAH0AlQCUAKkAlACVAH4AlgCVAKoAlQCWAH8AlwCWAKsAlgCXAIAAmACXAKwAlwCYAIEArQCCAJgAggCtAJkAmQCaAIMAmgCZAK4AmgCbAIQAmwCaAK8AmwCcAIUAnACbAK8AnACdAIYAnQCcALAAsQCHAJ0AhwCxAJ4AiACyAJ8AsgCIAJ4AiQCzAKAAswCJAJ8AigC0AKEAtACKAKAAiwC1AKIAtQCLAKEAjAC2AKMAtgCMAKIAowCkAI0ApACjALcApAClAI4ApQCkALgAkACPAKUAkACmAJEApgCnAJIApwCmALkApwCoAJMAqACnALoAqQCoALsAqACpAJQAqgCpALwAqQCqAJUAqwCqAL0AqgCrAJYArACrAL4AqwCsAJcAvwCYAKwAmAC/AK0AwACZAK0AmQDAAK4ArgCvAJoArwCuAMEArwCwAJwAsACvAMEAwgCdALAAnQDCALEAwwCeALEAngDDALIAnwDEALMAxACfALIAoADFALQAxQCgALMAoQDGALUAxgChALQAogDHALYAxwCiALUAtgC3AKMAtwC2AMgAtwC4AKQAuAC3AMkAkAClALgAkAC5AKYAuQC6AKcAugC5AMoAugC7AKgAuwC6AMsAvAC7AMwAuwC8AKkAvQC8AM0AvAC9AKoAvgC9AM4AvQC+AKsAzwCsAL4ArADPAL8A0ACtAL8ArQDQAMAA0QCuAMAArgDRAMEAwgDBANEAwQDCALAA0gCxAMIAsQDSAMMA0wCyAMMAsgDTAMQAswDUAMUA1ACzAMQAtADVAMYA1QC0AMUAtQDWAMcA1gC1AMYAxwDIALYAyADHANcAyADJALcAyQDIANgAkAC4AMkAkADKALkAygDLALoAywDKANkA2gC7AMsAuwDaAMwAzQDMANsAzADNALwAzgDNANwAzQDOAL0AzgDPAL4AzwDOAN0A3gC/AM8AvwDeANAA3wDAANAAwADfANEA0gDRAN8A0QDSAMIA4ADDANIAwwDgANMA1ADTAOEA0wDUAMQAxQDiANUA4gDFANQAxgDjANYA4wDGANUA1gDXAMcA1wDWAOQA1wDYAMgA2ADXAOUAkADJANgA2QDKAJAA5gDLANkAywDmANoA5wDMANoAzADnANsA3ADbAOgA2wDcAM0A3ADdAM4A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"}},"materials":{"ID12":{"instanceTechnique":{"technique":"technique0","values":{"diffuse":[0.11764699965715408,0.800000011920929,0,1]}},"name":"material_2"},"ID4":{"instanceTechnique":{"technique":"technique0","values":{"diffuse":[0,0.21568599343299866,0.9176470041275024,1]}},"name":"material_3"}},"meshes":{"ID10":{"name":"ID10","primitives":[{"attributes":{"NORMAL":"accessor_40","POSITION":"accessor_38"},"indices":"accessor_36","material":"ID12","primitive":4}]},"ID2":{"name":"ID2","primitives":[{"attributes":{"NORMAL":"accessor_20","POSITION":"accessor_18"},"indices":"accessor_16","material":"ID4","primitive":4}]}},"nodes":{"node_0":{"children":[],"matrix":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],"meshes":["ID2","ID10"],"name":"SketchUp"},"node_1":{"children":["node_0"],"matrix":[1,0,0,0,0,0,-1,0,0,1,0,0,0,0,0,1],"name":"Y_UP_Transform"}},"programs":{"program_0":{"attributes":["a_normal","a_position"],"fragmentShader":"model0FS","vertexShader":"model0VS"}},"scene":"defaultScene","scenes":{"defaultScene":{"nodes":["node_1"]}},"shaders":{"model0FS":{"type":35632,"uri":"data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0Owp2YXJ5aW5nIHZlYzMgdl9ub3JtYWw7CnVuaWZvcm0gdmVjNCB1X2RpZmZ1c2U7CnZvaWQgbWFpbih2b2lkKSB7CnZlYzMgbm9ybWFsID0gbm9ybWFsaXplKHZfbm9ybWFsKTsKdmVjNCBjb2xvciA9IHZlYzQoMC4sIDAuLCAwLiwgMC4pOwp2ZWM0IGRpZmZ1c2UgPSB2ZWM0KDAuLCAwLiwgMC4sIDEuKTsKZGlmZnVzZSA9IHVfZGlmZnVzZTsKZGlmZnVzZS54eXogKj0gbWF4KGRvdChub3JtYWwsdmVjMygwLiwwLiwxLikpLCAwLik7CmNvbG9yLnh5eiArPSBkaWZmdXNlLnh5ejsKY29sb3IgPSB2ZWM0KGNvbG9yLnJnYiAqIGRpZmZ1c2UuYSwgZGlmZnVzZS5hKTsKZ2xfRnJhZ0NvbG9yID0gY29sb3I7Cn0K"},"model0VS":{"type":35633,"uri":"data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0OwphdHRyaWJ1dGUgdmVjMyBhX3Bvc2l0aW9uOwphdHRyaWJ1dGUgdmVjMyBhX25vcm1hbDsKdmFyeWluZyB2ZWMzIHZfbm9ybWFsOwp1bmlmb3JtIG1hdDMgdV9ub3JtYWxNYXRyaXg7CnVuaWZvcm0gbWF0NCB1X21vZGVsVmlld01hdHJpeDsKdW5pZm9ybSBtYXQ0IHVfcHJvamVjdGlvbk1hdHJpeDsKdm9pZCBtYWluKHZvaWQpIHsKdmVjNCBwb3MgPSB1X21vZGVsVmlld01hdHJpeCAqIHZlYzQoYV9wb3NpdGlvbiwxLjApOwp2X25vcm1hbCA9IHVfbm9ybWFsTWF0cml4ICogYV9ub3JtYWw7CmdsX1Bvc2l0aW9uID0gdV9wcm9qZWN0aW9uTWF0cml4ICogcG9zOwp9Cg=="}},"skins":{},"techniques":{"technique0":{"parameters":{"diffuse":{"type":35666},"modelViewMatrix":{"semantic":"MODELVIEW","type":35676},"normal":{"semantic":"NORMAL","type":35665},"normalMatrix":{"semantic":"MODELVIEWINVERSETRANSPOSE","type":35675},"position":{"semantic":"POSITION","type":35665},"projectionMatrix":{"semantic":"PROJECTION","type":35676}},"pass":"defaultPass","passes":{"defaultPass":{"details":{"commonProfile":{"extras":{"doubleSided":false},"lightingModel":"Lambert","parameters":["diffuse","modelViewMatrix","normalMatrix","projectionMatrix"]},"type":"COLLADA-1.4.1/commonProfile"},"instanceProgram":{"attributes":{"a_normal":"normal","a_position":"position"},"program":"program_0","uniforms":{"u_diffuse":"diffuse","u_modelViewMatrix":"modelViewMatrix","u_normalMatrix":"normalMatrix","u_projectionMatrix":"projectionMatrix"}},"states":{"enable":[2884,2929]}}}}}} -------------------------------------------------------------------------------- /resources/public/videos/Firefox.ogv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgrove/gamma-playground/271c4c1ba45744fbd988d93faa429fe1a77f74c0/resources/public/videos/Firefox.ogv -------------------------------------------------------------------------------- /src/clj/gampg/server.clj: -------------------------------------------------------------------------------- 1 | (ns gampg.server 2 | (:require [clojure.java.io :as io] 3 | [gampg.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]] 4 | [compojure.core :refer [GET defroutes]] 5 | [compojure.route :refer [resources]] 6 | [net.cgrand.enlive-html :refer [deftemplate]] 7 | [net.cgrand.reload :refer [auto-reload]] 8 | [ring.middleware.reload :as reload] 9 | [ring.middleware.defaults :refer [wrap-defaults api-defaults]] 10 | [environ.core :refer [env]] 11 | [ring.adapter.jetty :refer [run-jetty]])) 12 | 13 | (deftemplate page (io/resource "index.html") [] 14 | [:body] (if is-dev? inject-devmode-html identity)) 15 | 16 | (defroutes routes 17 | (resources "/") 18 | (resources "/react" {:root "cljsjs/development"}) 19 | (GET "/*" req (page))) 20 | 21 | (def http-handler 22 | (if is-dev? 23 | (reload/wrap-reload (wrap-defaults #'routes api-defaults)) 24 | (wrap-defaults routes api-defaults))) 25 | 26 | (defn run-web-server [& [port]] 27 | (let [port (Integer. (or port (env :port) 10555))] 28 | (print "Starting web server on port" port ".\n") 29 | (run-jetty http-handler {:port port :join? false}))) 30 | 31 | (defn run-auto-reload [& [port]] 32 | (auto-reload *ns*) 33 | (start-figwheel)) 34 | 35 | (defn run [& [port]] 36 | (when is-dev? 37 | (run-auto-reload)) 38 | (run-web-server port)) 39 | 40 | (defn -main [& [port]] 41 | (run port)) 42 | -------------------------------------------------------------------------------- /src/cljs/gampg/core.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-always gampg.core 2 | (:require [clojure.string :as string] 3 | [fipp.clojure :as fipp-code] 4 | [fipp.edn :as fipp] 5 | [gampg.learn-gamma.lesson-01 :as lg01] 6 | [gampg.learn-gamma.lesson-02 :as lg02] 7 | [gampg.learn-gamma.lesson-03 :as lg03] 8 | [gampg.learn-gamma.lesson-04 :as lg04] 9 | [gampg.learn-gamma.lesson-05 :as lg05] 10 | [gampg.learn-gamma.lesson-06 :as lg06] 11 | [gampg.learn-gamma.lesson-07 :as lg07] 12 | [gampg.learn-gamma.lesson-08 :as lg08] 13 | [gampg.learn-gamma.lesson-09 :as lg09] 14 | [gampg.learn-gamma.lesson-10 :as lg10] 15 | [gampg.learn-gamma.lesson-11 :as lg11] 16 | [gampg.learn-gamma.lesson-12 :as lg12] 17 | [gampg.learn-gamma.lesson-13 :as lg13] 18 | [gampg.learn-gamma.lesson-14 :as lg14] 19 | [gampg.learn-gamma.lesson-15 :as lg15] 20 | [gampg.learn-gamma.lesson-16 :as lg16] 21 | [gampg.learn-gamma.lesson-17 :as lg17] 22 | [gampg.learn-gamma.lesson-18 :as lg18] 23 | [gampg.learn-gamma.lesson-19 :as lg19] 24 | [gampg.learn-gamma.lesson-20 :as lg20] 25 | [gampg.learn-gamma.apartment :as lg-apartment] 26 | [gampg.learn-gamma.apartment-vr :as lg-vr] 27 | [gampg.learn-gamma.gltf :as lg-gltf] 28 | [gampg.learn-gamma.lightmap :as lg-lightmap] 29 | [markdown.core :as md] 30 | [om.core :as om :include-macros true] 31 | [om.dom :as dom :include-macros true])) 32 | 33 | (enable-console-print!) 34 | 35 | ;; TODO: Add a menu for selecting examples 36 | (def lessons 37 | [lg01/summary 38 | lg02/summary 39 | lg03/summary 40 | lg04/summary 41 | lg05/summary 42 | lg06/summary 43 | lg07/summary 44 | lg08/summary 45 | lg09/summary 46 | lg10/summary 47 | lg11/summary 48 | lg12/summary 49 | lg13/summary 50 | lg14/summary 51 | lg15/summary 52 | lg16/summary 53 | lg17/summary 54 | lg18/summary 55 | lg19/summary 56 | lg20/summary 57 | ]) 58 | 59 | (defonce app-state 60 | (atom {:current-lesson {:index 19 61 | :exit nil 62 | :enter nil}})) 63 | 64 | #_(add-watch app-state :lg-watcher 65 | (fn [key a old-state new-state] 66 | (js/console.log "State changed! " (pr-str (keys (:scene old-state))) " -> " (pr-str (keys (:scene new-state)))))) 67 | 68 | (defn main* [] 69 | (om/root 70 | (fn [app owner] 71 | (reify 72 | om/IRender 73 | (render [_] 74 | (let [current-lesson (:current-lesson app) 75 | summary (nth lessons (:index current-lesson))] 76 | (dom/div #js{:className "row"} 77 | (dom/div #js{:className "col-xs-0 col-md-2 col-lg-2 col-xl-2"} 78 | (dom/h2 nil "Lessons") 79 | (apply dom/ul nil 80 | (map (fn [idx lesson-summary] 81 | (dom/li #js{:style #js{:overflow "hidden"}} 82 | (dom/a #js{:href "#" 83 | :onClick (fn [event] 84 | ;; TODO: Call the exit fn of the old lesson and the entry fn of the new 85 | (om/transact! app [:current-lesson] 86 | (fn [old-lesson] 87 | (when-let [exit (get-in lessons [(:index old-lesson) :exit])] 88 | (js/console.log "Exiting old lesson...") 89 | (exit app (get-in app [:live :gl]))) 90 | (let [new-lesson (get-in lessons [idx]) 91 | next-lesson (merge new-lesson 92 | {:index idx})] 93 | (js/console.log "Entering new lesson...: " (pr-str next-lesson)) 94 | ((:enter new-lesson) app-state (get-in app [:live :node])) 95 | next-lesson))) 96 | (.preventDefault event) 97 | (.stopPropagation event))} (:title lesson-summary)))) (range) lessons))) 98 | (dom/div #js{:className "col-xs-12 col-md-10 col-lg-10 col-lg-12"} 99 | (dom/div nil 100 | (dom/h2 #js{} (:title summary)) 101 | (let [debug-data (reduce merge {} (map (juxt identity #(get-in app %)) (:debug-keys summary)))] 102 | (when (seq debug-data) 103 | (dom/code nil (with-out-str (fipp/pprint debug-data {:width 80}))))) 104 | (dom/div #js{:dangerouslySetInnerHTML #js{:__html (md/md->html (or (:explanation summary) 105 | "No explanation for this lesson"))}})))))))) 106 | app-state 107 | {:target (. js/document (getElementById "app"))})) 108 | 109 | (defn main [] 110 | (let [gl-node (js/document.getElementById "gl-canvas") 111 | gl (.getContext gl-node "webgl")] 112 | (swap! app-state (fn [state] 113 | (-> state 114 | (update-in [:current-lesson] merge (nth lessons (get-in state [:current-lesson :index]))) 115 | (update-in [:live] merge {:node gl-node 116 | :gl gl})))) 117 | ((get-in @app-state [:current-lesson :enter]) app-state gl-node)) 118 | (js/console.log (clj->js @app-state)) 119 | (js/console.log "installed app") 120 | (main*)) 121 | 122 | (aset js/window "reinstallApp" 123 | (fn [] 124 | (main*))) 125 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_01.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.learn-gamma.lesson-01 2 | (:require [cljs.repl :as repl] 3 | [fipp.edn :as fipp] 4 | [gamma.api :as g] 5 | [gamma.program :as p] 6 | [gamma-driver.api :as gd] 7 | [gamma-driver.drivers.basic :as driver] 8 | [thi.ng.geom.core :as geom] 9 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 10 | 11 | (def title 12 | "1. A triangle and a square") 13 | 14 | (def u-p-matrix 15 | (g/uniform "uPMatrix" :mat4)) 16 | 17 | (def u-mv-matrix 18 | (g/uniform "uMVMatrix" :mat4)) 19 | 20 | (def a-position 21 | (g/attribute "aVertexPosition" :vec3)) 22 | 23 | (def program-source 24 | (p/program 25 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 26 | (g/* u-mv-matrix) 27 | (g/* (g/vec4 a-position 1)))} 28 | :fragment-shader {(g/gl-frag-color) (g/vec4 1 0 0 1)}})) 29 | 30 | (defn get-perspective-matrix 31 | "Be sure to 32 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 33 | the GL context 34 | 2. (set! (.-width/height canvas-node) 35 | width/height), respectively, or you may see no results, or strange 36 | results" 37 | [width height] 38 | (mat/perspective 45 (/ width height) 0.1 100)) 39 | 40 | (defn get-data [p mv vertices] 41 | {u-p-matrix p 42 | u-mv-matrix mv 43 | a-position vertices}) 44 | 45 | (defn reset-gl-canvas! [canvas-node] 46 | (let [gl (.getContext canvas-node "webgl") 47 | width (.-clientWidth canvas-node) 48 | height (.-clientHeight canvas-node)] 49 | ;; Set the width/height (in terms of GL-resolution) to actual 50 | ;; canvas-element width/height (or else you'll see blurry results) 51 | (set! (.-width canvas-node) width) 52 | (set! (.-height canvas-node) height) 53 | ;; Setup GL Canvas 54 | (.viewport gl 0 0 width height))) 55 | 56 | (def triangle-vertices 57 | {:id :triangle-vertices 58 | :data [[ 0 1 0] 59 | [-1 -1 0] 60 | [ 1 -1 0]] 61 | :immutable? true}) 62 | 63 | (def square-vertices 64 | {:id :square-vertices 65 | :data [[ 1 1 0] 66 | [-1 1 0] 67 | [ 1 -1 0] 68 | [-1 -1 0]] 69 | :immutable? true}) 70 | 71 | (defn main [_ node] 72 | (let [gl (.getContext node "webgl") 73 | w (.-clientWidth node) 74 | h (.-clientHeight node) 75 | driver (driver/basic-driver gl) 76 | program (gd/program driver program-source) 77 | p (get-perspective-matrix w h) 78 | mv (mat/matrix44)] 79 | (reset-gl-canvas! node) 80 | (.clearColor gl 0 0 0 1) 81 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 82 | (let [mv (geom/translate mv [-1.5 0 -7])] 83 | (gd/draw-arrays driver (gd/bind driver program (get-data p mv triangle-vertices)) {:draw-mode :triangles})) 84 | (let [mv (geom/translate mv [3 0 -7])] 85 | (gd/draw-arrays driver (gd/bind driver program (get-data p mv square-vertices)) {:draw-mode :triangle-strip})))) 86 | 87 | (def explanation 88 | (str 89 | " 90 | # WIP 91 | 92 | We're going to draw a simple triangle and a square to illustrate how 93 | to get simple shapes on the screen. 94 | 95 | > __Be aware that there are a lot of pitfalls in WebGL programming,__ 96 | __and you're likely to see an empty black screen _a lot_. Be ready to__ 97 | __persevere__ 98 | 99 | ## Dive in head first 100 | Let's dive right into the lesson's `main` fn: 101 | ```clojure " (with-out-str (repl/source main)) " 102 | ``` 103 | 104 | We're ignoring the first argument (we'll make use of it in later 105 | lessons), but the second is more interesting: it's the canvas node 106 | we'll be drawing to. We pull the WebGL context out of it, and create a 107 | `Gamma Driver`, which is going to do most of the heavy lifting for us. 108 | 109 | 110 | We then 111 | 1. Compile our shader (more on that further down) via `(gd/program driver program-source)` 112 | 1. Create a perspective matrix 113 | 1. A model-view matrix 114 | 1. Reset the canvas - typically you'd do this once when first starting, and then one on a canvas resize. 115 | 1. Clear the canvas so it's ready for us to draw 116 | 1. Draw the triangle by: 117 | 2. Translating the (_immutable_) mv-matrix slightly to the left and further back in the screen 118 | 2. Binding *all* the data needed to draw the triangle: In this case, the vertices, the perspective-matrix, and the mv-matrix 119 | 1. Draw the square (using the same steps as for the triangle) 120 | 121 | ## What's that `triangle-vertices` thing? When drawing things with 122 | WeblGL, you have just a few primitives - in this lesson, we'll look at 123 | buffers. Buffers are nothing more than an allocated region of memory 124 | that lives in the GPU. You store simple bytes in these buffers, and 125 | it's up to you to provide \"meaning\" to them. For example, let's say 126 | we want to draw a triangle - a triangle is made up of three points, 127 | and each point is a vector of three numbers, e.g. `[x, y, z]` 128 | representing the position. To represent a single triangle with its 129 | center at the origin of the canvas, we might do the following: 130 | 131 | ```clojure " (with-out-str (fipp/pprint (vec (flatten (get-in triangle-vertices [:data]))))) " 132 | ``` Now, usually getting this to the 133 | GPU is a incredibly (really impressively so) stateful pain in the 134 | neck. However, `GD` (Gamma Driver) will handle most of the dirty work 135 | for us. To help `GD` be efficient about things (and because we know 136 | the points of this triangle will never change - we'll use the 137 | transformation matrix to move it around), we'll tag the data with two 138 | more pieces of information: its `:id` and `:immutable? true`. Here's 139 | what our final data for the vertices looks like: 140 | 141 | ```clojure " (with-out-str (repl/source triangle-vertices)) " 142 | ``` 143 | Now we'll do the same for a square: 144 | ```clojure " (with-out-str (repl/source square-vertices)) " 145 | ``` 146 | 147 | ## Blablabla, some `Matrix` joke 148 | We just need two more bits of data: The perspective matrix and the 149 | model-view matrix. We'll outsource the generation of each to the 150 | [thi.ng.geom.core.matrix](https://github.com/thi-ng/geom/blob/master/geom-core/src/matrix.org) 151 | library. This bit of data *might* change (it depends on the field of 152 | view, and the width/height of the current canvas, which could change 153 | if the window is resized), so we won't bother marking it with `:id` 154 | or `:immutable? true`. Here's our function to generate one: 155 | ```clojure " (with-out-str (repl/source get-perspective-matrix)) " 156 | ``` 157 | 158 | For now, our `mv` (model-view matrix) will just be the identity 159 | matrix: `(mat/matrix44)`, but we'll use the `(geom/translate ...`) 160 | function to move our individual shapes around. 161 | 162 | ## Explain Shaders 163 | ## Explain `draw-arrays` 164 | ")) 165 | 166 | (def summary 167 | {:title title 168 | :enter main 169 | :explanation explanation}) 170 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_02.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.learn-gamma.lesson-02 2 | (:require [cljs.repl :as repl] 3 | [fipp.edn :as fipp] 4 | [gamma.api :as g] 5 | [gamma.program :as p] 6 | [gamma-driver.api :as gd] 7 | [gamma-driver.drivers.basic :as driver] 8 | [gampg.learn-gamma.programs :as progs] 9 | [gampg.utils :as utils] 10 | [thi.ng.geom.core :as geom] 11 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 12 | 13 | (def title 14 | "2. Adding colour") 15 | 16 | (def v-color 17 | (g/varying "vColor" :vec4 :mediump)) 18 | 19 | (def program-source 20 | (p/program 21 | {:id :lesson-02-simple 22 | :vertex-shader {(g/gl-position) (-> progs/u-p-matrix 23 | (g/* progs/u-mv-matrix) 24 | (g/* (g/vec4 progs/a-position 1))) 25 | v-color progs/a-color} 26 | :fragment-shader {(g/gl-frag-color) progs/v-color}})) 27 | 28 | (defn get-data [p mv vertices vertex-colors] 29 | {progs/u-p-matrix p 30 | progs/u-mv-matrix mv 31 | progs/a-position vertices 32 | progs/a-color vertex-colors}) 33 | 34 | (defn make-driver [gl] 35 | (driver/basic-driver gl)) 36 | 37 | (defn reset-gl-canvas! [canvas-node] 38 | (let [gl (.getContext canvas-node "webgl") 39 | width (.-clientWidth canvas-node) 40 | height (.-clientHeight canvas-node)] 41 | ;; Set the width/height (in terms of GL-resolution) to actual 42 | ;; canvas-element width/height (or else you'll see blurry results) 43 | (set! (.-width canvas-node) width) 44 | (set! (.-height canvas-node) height) 45 | ;; Setup GL Canvas 46 | (.viewport gl 0 0 width height))) 47 | 48 | (def triangle 49 | {:vertices {:data [[ 0 1 0] 50 | [-1 -1 0] 51 | [ 1 -1 0]] 52 | :immutable? true 53 | :id :triangle-vertices} 54 | :colors {:data [[1 0 0 1] 55 | [0 1 0 1] 56 | [0 0 1 1]] 57 | :immutable? true 58 | :id :triangle-colors}}) 59 | 60 | (def square 61 | {:vertices {:data [[ 1 1 0] 62 | [-1 1 0] 63 | [ 1 -1 0] 64 | [-1 -1 0]] 65 | :immutable? true 66 | :id :square-vertices} 67 | :colors {:data [[1 0 0 1] 68 | [0 1 0 1] 69 | [0 0 1 1] 70 | [1 1 1 1]] 71 | :immutable? true 72 | :id :square-colors}}) 73 | 74 | (defn main [app-state node] 75 | (let [gl (.getContext node "webgl") 76 | w (.-clientWidth node) 77 | h (.-clientHeight node) 78 | driver (make-driver gl) 79 | program program-source 80 | p (utils/get-perspective-matrix w h) 81 | mv (mat/matrix44)] 82 | (reset-gl-canvas! node) 83 | (.clearColor gl 0 0 0 1) 84 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 85 | (let [mv (geom/translate mv [-1.5 0 -7])] 86 | (gd/draw-arrays driver (gd/bind driver program (get-data p mv (:vertices triangle) (:colors triangle))) {:draw-mode :triangles})) 87 | (let [mv (geom/translate mv [3 0 -7])] 88 | (gd/draw-arrays driver (gd/bind driver program (get-data p mv (:vertices square) (:colors square))) {:draw-mode :triangle-strip})))) 89 | 90 | (def explanation 91 | (str 92 | "```clojure " (with-out-str (repl/source main)) " 93 | ``` 94 | ")) 95 | 96 | (def summary 97 | {:title title 98 | :enter main 99 | :explanation explanation}) 100 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_03.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-load gampg.learn-gamma.lesson-03 2 | (:require [gamma.api :as g] 3 | [gamma.program :as p] 4 | [gamma-driver.api :as gd] 5 | [gamma-driver.drivers.basic :as driver] 6 | [gampg.utils :as utils] 7 | [gampg.learn-gamma.lesson-02 :as lesson-02] 8 | [gampg.learn-gamma.programs :as progs] 9 | [thi.ng.geom.core :as geom] 10 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 11 | 12 | (def title 13 | "3. A bit of movement") 14 | 15 | (defn get-data [p mv vertices vertex-colors] 16 | {progs/u-p-matrix p 17 | progs/u-mv-matrix mv 18 | progs/a-position vertices 19 | progs/a-color vertex-colors}) 20 | 21 | (defn app-state [width height] 22 | {:last-rendered 0 23 | :scene {:triangle-rotation 0 24 | :square-rotation 0 25 | :triangle lesson-02/triangle 26 | :square lesson-02/square 27 | :mv (mat/matrix44) 28 | :p (utils/get-perspective-matrix width height)}}) 29 | 30 | (defn draw-scene [gl driver program] 31 | (fn [state] 32 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 33 | (let [{:keys [p mv 34 | triangle 35 | triangle-rotation 36 | square 37 | square-rotation]} (:scene state)] 38 | (let [mv (-> mv 39 | (geom/translate [-1.5 0 -7]) 40 | (geom/rotate-y triangle-rotation))] 41 | (gd/draw-arrays driver (gd/bind driver program (get-data p mv (:vertices triangle) (:colors triangle))) {:draw-mode :triangles})) 42 | (let [mv (-> mv 43 | (geom/translate [3 0 -7]) 44 | (geom/rotate-x square-rotation))] 45 | (gd/draw-arrays driver (gd/bind driver program (get-data p mv (:vertices square) (:colors square))) {:draw-mode :triangle-strip}))))) 46 | 47 | (defn animate [draw-fn step-fn current-value] 48 | (js/requestAnimationFrame 49 | (fn [time] 50 | (let [next-value (step-fn time current-value)] 51 | (draw-fn next-value) 52 | (animate draw-fn step-fn next-value))))) 53 | 54 | (defn tick 55 | "Takes the old world value and produces a new world value, suitable 56 | for rendering" 57 | [time state] 58 | ;; We get the elapsed time since the last render to compensate for 59 | ;; lag, etc. 60 | (let [time-now (.getTime (js/Date.)) 61 | elapsed (- time-now (:last-rendered state)) 62 | triangle-diff (/ (* 90 elapsed) 100000) 63 | square-diff (/ (* 75 elapsed) 100000)] 64 | (-> state 65 | (update-in [:scene :triangle-rotation] + triangle-diff) 66 | (update-in [:scene :square-rotation] + square-diff) 67 | (assoc-in [:last-rendered] time-now)))) 68 | 69 | (defn main [global-app-state node] 70 | (let [gl (.getContext node "webgl") 71 | width (.-clientWidth node) 72 | height (.-clientHeight node) 73 | driver (driver/basic-driver gl) 74 | program (gd/program driver progs/simple-color) 75 | state (app-state width height)] 76 | (utils/reset-gl-canvas! node) 77 | (.clearColor gl 0 0 0 1) 78 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 79 | (animate (draw-scene gl driver program) tick state))) 80 | 81 | (def summary 82 | {:title title 83 | :enter main}) 84 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_04.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-load gampg.learn-gamma.lesson-04 2 | (:require [gamma.api :as g] 3 | [gamma.program :as p] 4 | [gamma-driver.api :as gd] 5 | [gamma-driver.drivers.basic :as driver] 6 | [thi.ng.geom.core :as geom] 7 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 8 | 9 | (def title 10 | "4. Some Real 3D Objects") 11 | 12 | (def u-p-matrix 13 | (g/uniform "uPMatrix" :mat4)) 14 | 15 | (def u-mv-matrix 16 | (g/uniform "uMVMatrix" :mat4)) 17 | 18 | (def a-position 19 | (g/attribute "aVertexPosition" :vec3)) 20 | 21 | (def a-color 22 | (g/attribute "aVertexColor" :vec4)) 23 | 24 | (def v-color 25 | (g/varying "vColor" :vec4 :mediump)) 26 | 27 | (def program-source 28 | (p/program 29 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 30 | (g/* u-mv-matrix) 31 | (g/* (g/vec4 a-position 1))) 32 | v-color a-color} 33 | :fragment-shader {(g/gl-frag-color) v-color}})) 34 | 35 | (defn get-perspective-matrix 36 | "Be sure to 37 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 38 | the GL context 39 | 2. (set! (.-width/height canvas-node) 40 | width/height), respectively, or you may see no results, or strange 41 | results" 42 | [width height] 43 | (mat/perspective 45 (/ width height) 0.1 100)) 44 | 45 | (defn get-data [p mv vertices vertex-colors] 46 | {u-p-matrix p 47 | u-mv-matrix mv 48 | a-position vertices 49 | a-color vertex-colors}) 50 | 51 | (defn make-driver [gl] 52 | (driver/basic-driver gl)) 53 | 54 | (defn reset-gl-canvas! [canvas-node] 55 | (let [gl (.getContext canvas-node "webgl") 56 | width (.-clientWidth canvas-node) 57 | height (.-clientHeight canvas-node)] 58 | ;; Set the width/height (in terms of GL-resolution) to actual 59 | ;; canvas-element width/height (or else you'll see blurry results) 60 | (set! (.-width canvas-node) width) 61 | (set! (.-height canvas-node) height) 62 | ;; Setup GL Canvas 63 | (.viewport gl 0 0 width height))) 64 | 65 | ;; js/window.requestAnimationFrame doesn't take arguments, so we have 66 | ;; to store the state elsewhere - in this atom, for example. 67 | (defn app-state [width height] 68 | {:last-rendered 0 69 | :scene {:pyramid-vertices {:data [ ;; Front face 70 | [0.0, 1.0, 0.0,] 71 | [-1.0, -1.0, 1.0,] 72 | [1.0, -1.0, 1.0,] 73 | ;; Right face 74 | [0.0, 1.0, 0.0,] 75 | [1.0, -1.0, 1.0,] 76 | [1.0, -1.0, -1.0,] 77 | ;; Back face 78 | [0.0, 1.0, 0.0,] 79 | [1.0, -1.0, -1.0,] 80 | [-1.0, -1.0, -1.0,] 81 | ;; Left face 82 | [0.0, 1.0, 0.0,] 83 | [-1.0, -1.0, -1.0,] 84 | [-1.0, -1.0, 1.0]] 85 | :immutable? true 86 | :id :pyramid-vertices} 87 | :pyramid-colors {:data [ ;; Front face 88 | [1.0, 0.0, 0.0, 1.0,] 89 | [0.0, 1.0, 0.0, 1.0,] 90 | [0.0, 0.0, 1.0, 1.0,] 91 | ;; Right face 92 | [1.0, 0.0, 0.0, 1.0,] 93 | [0.0, 0.0, 1.0, 1.0,] 94 | [0.0, 1.0, 0.0, 1.0,] 95 | ;; Back face 96 | [1.0, 0.0, 0.0, 1.0,] 97 | [0.0, 1.0, 0.0, 1.0,] 98 | [0.0, 0.0, 1.0, 1.0,] 99 | ;; Left face 100 | [1.0, 0.0, 0.0, 1.0,] 101 | [0.0, 0.0, 1.0, 1.0,] 102 | [0.0, 1.0, 0.0, 1.0]] 103 | :immutable? true 104 | :id :pyramid-colors} 105 | :pyramid-rotation 0 106 | :cube-vertices {:data [ ;; Front face 107 | [-1.0 -1.0 1.0] 108 | [1.0 -1.0 1.0] 109 | [1.0 1.0 1.0] 110 | [-1.0 1.0 1.0] 111 | 112 | ;; Back face 113 | [-1.0 -1.0 -1.0] 114 | [-1.0 1.0 -1.0] 115 | [1.0 1.0 -1.0] 116 | [1.0 -1.0 -1.0] 117 | 118 | ;; Top face 119 | [-1.0 1.0 -1.0] 120 | [-1.0 1.0 1.0] 121 | [1.0 1.0 1.0] 122 | [1.0 1.0 -1.0] 123 | 124 | ;; Bottom face 125 | [-1.0 -1.0 -1.0] 126 | [1.0 -1.0 -1.0] 127 | [1.0 -1.0 1.0] 128 | [-1.0 -1.0 1.0] 129 | 130 | ;; Right face 131 | [1.0 -1.0 -1.0] 132 | [1.0 1.0 -1.0] 133 | [1.0 1.0 1.0] 134 | [1.0 -1.0 1.0] 135 | 136 | ;; Left face 137 | [-1.0 -1.0 -1.0] 138 | [-1.0 -1.0 1.0] 139 | [-1.0 1.0 1.0] 140 | [-1.0 1.0 -1.0]] 141 | :immutable? true 142 | :id :cube-vertices} 143 | :cube-colors {:data (vec (mapcat identity 144 | [(repeat 4 [1.0, 0.0, 0.0, 1.0]), ;; Front face 145 | (repeat 4 [1.0, 1.0, 0.0, 1.0]), ;; Back face 146 | (repeat 4 [0.0, 1.0, 0.0, 1.0]), ;; Top face 147 | 148 | (repeat 4 [1.0, 0.5, 0.5, 1.0]), ;; Bottom face 149 | (repeat 4 [1.0, 0.0, 1.0, 1.0]), ;; Right face 150 | (repeat 4 [0.0, 0.0, 1.0, 1.0]), ;; Left face 151 | ])) 152 | :immutable? true 153 | :id :cube-colors} 154 | :cube-indices {:data [0 1 2 0 2 3 ;; Front face 155 | 4 5 6 4 6 7 ;; Back face 156 | 8 9 10 8 10 11 ;; Top face 157 | 12 13 14 12 14 15 ;; Bottom face 158 | 16 17 18 16 18 19 ;; Right face 159 | 20 21 22 20 22 23 ;; 160 | ] 161 | :immutable? true 162 | :id :cube-indices} ;; Left face 163 | :cube-rotation 0 164 | :mv (mat/matrix44) 165 | :p {:data (get-perspective-matrix width height) 166 | :immutable? true 167 | :id :p}}}) 168 | 169 | (defn draw-fn [gl driver program] 170 | (fn [state] 171 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 172 | (let [{:keys [p mv 173 | pyramid-vertices pyramid-colors 174 | pyramid-rotation 175 | cube-vertices cube-colors 176 | cube-rotation cube-indices 177 | square-vertices square-colors 178 | square-rotation]} (:scene state)] 179 | (let [mv (-> mv 180 | (geom/translate [-1.5 0 -7]) 181 | (geom/rotate-y pyramid-rotation))] 182 | (gd/draw-arrays driver (gd/bind driver program (get-data p mv pyramid-vertices pyramid-colors)) {:draw-mode :triangles})) 183 | (let [mv (-> mv 184 | (geom/translate [3 0 -7]) 185 | (geom/rotate-x cube-rotation))] 186 | (gd/draw-elements driver (gd/bind driver program 187 | (assoc (get-data p mv cube-vertices cube-colors) 188 | {:tag :element-index} cube-indices)) 189 | {:draw-mode :triangles 190 | :first 0 191 | ;;Hard-coded 192 | :count 36}))))) 193 | 194 | (defn animate [draw-fn step-fn current-value] 195 | (js/requestAnimationFrame 196 | (fn [time] 197 | (let [next-value (step-fn time current-value)] 198 | (draw-fn next-value) 199 | (animate draw-fn step-fn next-value))))) 200 | 201 | (defn tick 202 | "Takes the old world value and produces a new world value, suitable 203 | for rendering" 204 | [time state] 205 | ;; We get the elapsed time since the last render to compensate for 206 | ;; lag, etc. 207 | (let [time-now (.getTime (js/Date.)) 208 | elapsed (- time-now (:last-rendered state)) 209 | pyramid-diff (/ (* 50 elapsed) 100000) 210 | cube-diff (/ (* 75 elapsed) 100000)] 211 | (-> state 212 | (update-in [:scene :pyramid-rotation] + pyramid-diff) 213 | (update-in [:scene :cube-rotation] + cube-diff) 214 | (assoc-in [:last-rendered] time-now)))) 215 | 216 | (defn main [_ node] 217 | (let [gl (.getContext node "webgl") 218 | width (.-clientWidth node) 219 | height (.-clientHeight node) 220 | driver (make-driver gl) 221 | program (gd/program driver program-source) 222 | state (app-state width height)] 223 | (set! (.-debugRedrawScene js/window) 224 | (fn [] 225 | (animate (draw-fn gl driver program) tick state))) 226 | (reset-gl-canvas! node) 227 | (.enable gl (.-DEPTH_TEST gl)) 228 | (.clearColor gl 0 0 0 1) 229 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 230 | (animate (draw-fn gl driver program) tick state))) 231 | 232 | (def explanation 233 | nil) 234 | 235 | (def summary 236 | {:title title 237 | :enter main 238 | :explanation explanation}) 239 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_05.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-load gampg.learn-gamma.lesson-05 2 | (:require [gamma.api :as g] 3 | [gamma.program :as p] 4 | [gamma-driver.api :as gd] 5 | [gamma-driver.drivers.basic :as driver] 6 | [thi.ng.geom.core :as geom] 7 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 8 | 9 | (def title 10 | "5. Introducing textures") 11 | 12 | (def u-p-matrix 13 | (g/uniform "uPMatrix" :mat4)) 14 | 15 | (def u-mv-matrix 16 | (g/uniform "uMVMatrix" :mat4)) 17 | 18 | (def a-position 19 | (g/attribute "aVertexPosition" :vec3)) 20 | 21 | (def a-texture-coord 22 | (g/attribute "aTextureCoord" :vec2)) 23 | 24 | (def v-texture-coord 25 | (g/varying "vTextureCoord" :vec2 :mediump)) 26 | 27 | (def u-sampler 28 | (g/uniform "uSampler" :sampler2D)) 29 | 30 | (def program-source 31 | (p/program 32 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 33 | (g/* u-mv-matrix) 34 | (g/* (g/vec4 a-position 1))) 35 | v-texture-coord a-texture-coord} 36 | :fragment-shader {(g/gl-frag-color) 37 | (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st)))}})) 38 | 39 | (defn get-perspective-matrix 40 | "Be sure to 41 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 42 | the GL context 43 | 2. (set! (.-width/height canvas-node) 44 | width/height), respectively, or you may see no results, or strange 45 | results" 46 | [width height] 47 | (mat/perspective 45 (/ width height) 0.1 100)) 48 | 49 | (defn get-data [p mv vertices texture texture-coords] 50 | {u-p-matrix p 51 | u-mv-matrix mv 52 | u-sampler texture 53 | a-position vertices 54 | a-texture-coord texture-coords}) 55 | 56 | (defn make-driver [gl] 57 | (driver/basic-driver gl)) 58 | 59 | (defn reset-gl-canvas! [canvas-node] 60 | (let [gl (.getContext canvas-node "webgl") 61 | width (.-clientWidth canvas-node) 62 | height (.-clientHeight canvas-node)] 63 | ;; Set the width/height (in terms of GL-resolution) to actual 64 | ;; canvas-element width/height (or else you'll see blurry results) 65 | (set! (.-width canvas-node) width) 66 | (set! (.-height canvas-node) height) 67 | ;; Setup GL Canvas 68 | (.viewport gl 0 0 width height))) 69 | 70 | ;; js/window.requestAnimationFrame doesn't take arguments, so we have 71 | ;; to store the state elsewhere - in this atom, for example. 72 | (defn app-state [width height] 73 | {:last-rendered 0 74 | :scene {:cube-vertices [ ;; Front face 75 | [-1.0 -1.0 1.0] 76 | [1.0 -1.0 1.0] 77 | [1.0 1.0 1.0] 78 | [-1.0 1.0 1.0] 79 | 80 | ;; Back face 81 | [-1.0 -1.0 -1.0] 82 | [-1.0 1.0 -1.0] 83 | [1.0 1.0 -1.0] 84 | [1.0 -1.0 -1.0] 85 | 86 | ;; Top face 87 | [-1.0 1.0 -1.0] 88 | [-1.0 1.0 1.0] 89 | [1.0 1.0 1.0] 90 | [1.0 1.0 -1.0] 91 | 92 | ;; Bottom face 93 | [-1.0 -1.0 -1.0] 94 | [1.0 -1.0 -1.0] 95 | [1.0 -1.0 1.0] 96 | [-1.0 -1.0 1.0] 97 | 98 | ;; Right face 99 | [1.0 -1.0 -1.0] 100 | [1.0 1.0 -1.0] 101 | [1.0 1.0 1.0] 102 | [1.0 -1.0 1.0] 103 | 104 | ;; Left face 105 | [-1.0 -1.0 -1.0] 106 | [-1.0 -1.0 1.0] 107 | [-1.0 1.0 1.0] 108 | [-1.0 1.0 -1.0]] 109 | :cube-texture-coords [ 110 | ;; Front face 111 | 0.0, 0.0, 112 | 1.0, 0.0, 113 | 1.0, 1.0, 114 | 0.0, 1.0, 115 | 116 | ;; Back face 117 | 1.0, 0.0, 118 | 1.0, 1.0, 119 | 0.0, 1.0, 120 | 0.0, 0.0, 121 | 122 | ;; Top face 123 | 0.0, 1.0, 124 | 0.0, 0.0, 125 | 1.0, 0.0, 126 | 1.0, 1.0, 127 | 128 | ;; Bottom face 129 | 1.0, 1.0, 130 | 0.0, 1.0, 131 | 0.0, 0.0, 132 | 1.0, 0.0, 133 | 134 | ;; Right face 135 | 1.0, 0.0, 136 | 1.0, 1.0, 137 | 0.0, 1.0, 138 | 0.0, 0.0, 139 | 140 | ;; Left face 141 | 0.0, 0.0, 142 | 1.0, 0.0, 143 | 1.0, 1.0, 144 | 0.0, 1.0,] 145 | :cube-indices [0 1 2 0 2 3 ;; Front face 146 | 4 5 6 4 6 7 ;; Back face 147 | 8 9 10 8 10 11 ;; Top face 148 | 12 13 14 12 14 15 ;; Bottom face 149 | 16 17 18 16 18 19 ;; Right face 150 | 20 21 22 20 22 23;; 151 | ] ;; Left face 152 | :cube-rotation 0 153 | :mv (mat/matrix44) 154 | :p (get-perspective-matrix width height)}}) 155 | 156 | (defn draw-fn [gl driver program] 157 | (fn [state] 158 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 159 | (let [{:keys [p mv 160 | texture 161 | cube-vertices cube-texture-coords 162 | cube-rotation cube-indices]} (:scene state)] 163 | (let [mv (-> mv 164 | (geom/translate [3 0 -7]) 165 | (geom/rotate-around-axis [1 0 0] cube-rotation) 166 | (geom/rotate-around-axis [0 1 0] cube-rotation) 167 | (geom/rotate-around-axis [0 0 1] cube-rotation))] 168 | (gd/draw-elements driver (gd/bind driver program 169 | (assoc (get-data p mv cube-vertices texture cube-texture-coords) 170 | {:tag :element-index} cube-indices)) 171 | {:draw-mode :triangles 172 | :first 0 173 | ;; Hard-coded 174 | :count 36}))))) 175 | 176 | (defn animate [draw-fn step-fn current-value] 177 | (js/requestAnimationFrame 178 | (fn [time] 179 | (let [next-value (step-fn time current-value)] 180 | (draw-fn next-value) 181 | (animate draw-fn step-fn next-value))))) 182 | 183 | (defn tick 184 | "Takes the old world value and produces a new world value, suitable 185 | for rendering" 186 | [time state] 187 | ;; We get the elapsed time since the last render to compensate for 188 | ;; lag, etc. 189 | (let [time-now (.getTime (js/Date.)) 190 | elapsed (- time-now (:last-rendered state)) 191 | cube-diff (/ (* 75 elapsed) 100000)] 192 | (-> state 193 | (update-in [:scene :cube-rotation] + cube-diff) 194 | (assoc-in [:last-rendered] time-now)))) 195 | 196 | (defn main [_ node] 197 | (let [gl (.getContext node "webgl") 198 | width (.-clientWidth node) 199 | height (.-clientHeight node) 200 | driver (make-driver gl) 201 | program program-source 202 | state (app-state width height)] 203 | (reset-gl-canvas! node) 204 | (.enable gl (.-DEPTH_TEST gl)) 205 | (.clearColor gl 0 0 0 1) 206 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 207 | (let [image (js/Image.)] 208 | (aset image "onload" 209 | (fn [] (let [texture {:data image 210 | :filter {:min :linear 211 | :mag :linear} 212 | :flip-y true}] 213 | (animate (draw-fn gl driver program) tick (assoc-in state [:scene :texture] texture))))) 214 | (aset image "src" "/images/nehe.gif")))) 215 | 216 | (def explanation 217 | nil) 218 | 219 | (def summary 220 | {:title title 221 | :enter main 222 | :explanation explanation}) 223 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_06.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-load gampg.learn-gamma.lesson-06 2 | (:require [gamma.api :as g] 3 | [gamma.program :as p] 4 | [gamma-driver.api :as gd] 5 | [gamma-driver.drivers.basic :as driver] 6 | [thi.ng.geom.core :as geom] 7 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 8 | 9 | (def title 10 | "6. Keyboard input and texture filters") 11 | 12 | (def u-p-matrix 13 | (g/uniform "uPMatrix" :mat4)) 14 | 15 | (def u-mv-matrix 16 | (g/uniform "uMVMatrix" :mat4)) 17 | 18 | (def a-position 19 | (g/attribute "aVertexPosition" :vec3)) 20 | 21 | (def a-texture-coord 22 | (g/attribute "aTextureCoord" :vec2)) 23 | 24 | (def v-texture-coord 25 | (g/varying "vTextureCoord" :vec2 :mediump)) 26 | 27 | (def u-sampler 28 | (g/uniform "uSampler" :sampler2D)) 29 | 30 | (def program-source 31 | (p/program 32 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 33 | (g/* u-mv-matrix) 34 | (g/* (g/vec4 a-position 1))) 35 | v-texture-coord a-texture-coord} 36 | :fragment-shader {(g/gl-frag-color) 37 | (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st)))}})) 38 | 39 | (defn get-perspective-matrix 40 | "Be sure to 41 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 42 | the GL context 43 | 2. (set! (.-width/height canvas-node) 44 | width/height), respectively, or you may see no results, or strange 45 | results" 46 | [width height] 47 | (mat/perspective 45 (/ width height) 0.1 100)) 48 | 49 | (defn get-data [p mv vertices texture texture-coords] 50 | {u-p-matrix p 51 | u-mv-matrix mv 52 | u-sampler texture 53 | a-position vertices 54 | a-texture-coord texture-coords}) 55 | 56 | (defn make-driver [gl] 57 | (driver/basic-driver gl)) 58 | 59 | (defn reset-gl-canvas! [canvas-node] 60 | (let [gl (.getContext canvas-node "webgl") 61 | width (.-clientWidth canvas-node) 62 | height (.-clientHeight canvas-node)] 63 | ;; Set the width/height (in terms of GL-resolution) to actual 64 | ;; canvas-element width/height (or else you'll see blurry results) 65 | (set! (.-width canvas-node) width) 66 | (set! (.-height canvas-node) height) 67 | ;; Setup GL Canvas 68 | (.viewport gl 0 0 width height))) 69 | 70 | ;; js/window.requestAnimationFrame doesn't take arguments, so we have 71 | ;; to store the state elsewhere - in this atom, for example. 72 | (defn app-state [width height] 73 | {:last-rendered 0 74 | :scene {:cube-vertices [ ;; Front face 75 | [-1.0 -1.0 1.0] 76 | [1.0 -1.0 1.0] 77 | [1.0 1.0 1.0] 78 | [-1.0 1.0 1.0] 79 | 80 | ;; Back face 81 | [-1.0 -1.0 -1.0] 82 | [-1.0 1.0 -1.0] 83 | [1.0 1.0 -1.0] 84 | [1.0 -1.0 -1.0] 85 | 86 | ;; Top face 87 | [-1.0 1.0 -1.0] 88 | [-1.0 1.0 1.0] 89 | [1.0 1.0 1.0] 90 | [1.0 1.0 -1.0] 91 | 92 | ;; Bottom face 93 | [-1.0 -1.0 -1.0] 94 | [1.0 -1.0 -1.0] 95 | [1.0 -1.0 1.0] 96 | [-1.0 -1.0 1.0] 97 | 98 | ;; Right face 99 | [1.0 -1.0 -1.0] 100 | [1.0 1.0 -1.0] 101 | [1.0 1.0 1.0] 102 | [1.0 -1.0 1.0] 103 | 104 | ;; Left face 105 | [-1.0 -1.0 -1.0] 106 | [-1.0 -1.0 1.0] 107 | [-1.0 1.0 1.0] 108 | [-1.0 1.0 -1.0]] 109 | :cube-texture-coords [ 110 | ;; Front face 111 | 0.0, 0.0, 112 | 1.0, 0.0, 113 | 1.0, 1.0, 114 | 0.0, 1.0, 115 | 116 | ;; Back face 117 | 1.0, 0.0, 118 | 1.0, 1.0, 119 | 0.0, 1.0, 120 | 0.0, 0.0, 121 | 122 | ;; Top face 123 | 0.0, 1.0, 124 | 0.0, 0.0, 125 | 1.0, 0.0, 126 | 1.0, 1.0, 127 | 128 | ;; Bottom face 129 | 1.0, 1.0, 130 | 0.0, 1.0, 131 | 0.0, 0.0, 132 | 1.0, 0.0, 133 | 134 | ;; Right face 135 | 1.0, 0.0, 136 | 1.0, 1.0, 137 | 0.0, 1.0, 138 | 0.0, 0.0, 139 | 140 | ;; Left face 141 | 0.0, 0.0, 142 | 1.0, 0.0, 143 | 1.0, 1.0, 144 | 0.0, 1.0,] 145 | :cube-indices [0 1 2 0 2 3 ;; Front face 146 | 4 5 6 4 6 7 ;; Back face 147 | 8 9 10 8 10 11 ;; Top face 148 | 12 13 14 12 14 15 ;; Bottom face 149 | 16 17 18 16 18 19 ;; Right face 150 | 20 21 22 20 22 23;; 151 | ] ;; Left face 152 | :cube-rotation 0 153 | :mv (mat/matrix44) 154 | :p (get-perspective-matrix width height)}}) 155 | 156 | (defn draw-fn [gl driver program] 157 | (fn [state] 158 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 159 | (let [{:keys [p mv 160 | texture 161 | cube-vertices cube-texture-coords 162 | cube-rotation cube-indices]} (:scene state)] 163 | (let [mv (-> mv 164 | (geom/translate [3 0 -7]) 165 | (geom/rotate-around-axis [1 0 0] cube-rotation) 166 | (geom/rotate-around-axis [0 1 0] cube-rotation) 167 | (geom/rotate-around-axis [0 0 1] cube-rotation))] 168 | (gd/draw-elements driver (gd/bind driver program 169 | (assoc (get-data p mv cube-vertices texture cube-texture-coords) 170 | {:tag :element-index} cube-indices)) 171 | {:draw-mode :triangles 172 | :first 0 173 | ;; Hard-coded 174 | :count 36}))))) 175 | 176 | (defn animate [draw-fn step-fn current-value] 177 | (js/requestAnimationFrame 178 | (fn [time] 179 | (let [next-value (step-fn time current-value)] 180 | (draw-fn next-value) 181 | (animate draw-fn step-fn next-value))))) 182 | 183 | (defn tick 184 | "Takes the old world value and produces a new world value, suitable 185 | for rendering" 186 | [time state] 187 | ;; We get the elapsed time since the last render to compensate for 188 | ;; lag, etc. 189 | (let [time-now (.getTime (js/Date.)) 190 | elapsed (- time-now (:last-rendered state)) 191 | cube-diff (/ (* 75 elapsed) 100000)] 192 | (-> state 193 | (update-in [:scene :cube-rotation] + cube-diff) 194 | (assoc-in [:last-rendered] time-now)))) 195 | 196 | (defn main [_ node] 197 | (let [gl (.getContext node "webgl") 198 | width (.-clientWidth node) 199 | height (.-clientHeight node) 200 | driver (make-driver gl) 201 | program program-source 202 | state (app-state width height)] 203 | 204 | (reset-gl-canvas! node) 205 | (.enable gl (.-DEPTH_TEST gl)) 206 | (.clearColor gl 0 0 0 1) 207 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 208 | 209 | (let [image (js/Image.)] 210 | (aset image "onload" 211 | (fn [] (let [texture {:data image 212 | :filter {:min :linear 213 | :mag :linear} 214 | :flip-y true 215 | :texture-id 0}] 216 | (animate (draw-fn gl driver program) tick (assoc-in state [:scene :texture] texture))))) 217 | (aset image "src" "/images/crate.gif")))) 218 | 219 | (def explanation 220 | nil) 221 | 222 | (def summary 223 | {:title title 224 | :enter main 225 | :explanation explanation}) 226 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_07.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.learn-gamma.lesson-07 2 | (:require [gamma.api :as g] 3 | [gamma.program :as p] 4 | [gamma-driver.api :as gd] 5 | [gamma-driver.drivers.basic :as driver] 6 | [thi.ng.geom.core :as geom] 7 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 8 | 9 | (def title 10 | "7. Basic directional and ambient lighting") 11 | 12 | (def u-p-matrix 13 | (g/uniform "uPMatrix" :mat4)) 14 | 15 | (def u-mv-matrix 16 | (g/uniform "uMVMatrix" :mat4)) 17 | 18 | (def u-n-matrix 19 | (g/uniform "uNMatrix" :mat3)) 20 | 21 | (def u-ambient-color 22 | (g/uniform "uAmbientColor" :vec3)) 23 | 24 | (def u-lighting-direction 25 | (g/uniform "uLightingDirection" :vec3)) 26 | 27 | (def u-directional-color 28 | (g/uniform "uDirectionalColor" :vec3)) 29 | 30 | (def u-use-lighting 31 | (g/uniform "uUseLighting" :bool)) 32 | 33 | (def a-position 34 | (g/attribute "aVertexPosition" :vec3)) 35 | 36 | (def a-vertex-normal 37 | (g/attribute "aVertexNormal" :vec3)) 38 | 39 | (def a-texture-coord 40 | (g/attribute "aTextureCoord" :vec2)) 41 | 42 | (def v-texture-coord 43 | (g/varying "vTextureCoord" :vec2 :mediump)) 44 | 45 | (def v-light-weighting 46 | (g/varying "vLightWeighting" :vec3 :mediump)) 47 | 48 | (def u-sampler 49 | (g/uniform "uSampler" :sampler2D)) 50 | 51 | (def program-source 52 | (p/program 53 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 54 | (g/* u-mv-matrix) 55 | (g/* (g/vec4 a-position 1))) 56 | v-texture-coord a-texture-coord 57 | v-light-weighting (g/if u-use-lighting 58 | (let [transformed-normal (g/* u-n-matrix a-vertex-normal) 59 | directional-light-weighting (g/max (g/dot transformed-normal u-lighting-direction) 0.0)] 60 | (g/* directional-light-weighting 61 | (g/+ u-ambient-color u-directional-color))) 62 | (g/vec3 1 1 1))} 63 | :fragment-shader (let [texture-color (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st))) 64 | rgb (g/* (g/swizzle texture-color :rgb) v-light-weighting) 65 | a (g/swizzle texture-color :a)] 66 | {(g/gl-frag-color) (g/vec4 rgb a)}) 67 | :precision {:float :mediump}})) 68 | 69 | (defn get-perspective-matrix 70 | "Be sure to 71 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 72 | the GL context 73 | 2. (set! (.-width/height canvas-node) 74 | width/height), respectively, or you may see no results, or strange 75 | results" 76 | [width height] 77 | (mat/perspective 45 (/ width height) 0.1 100)) 78 | 79 | (defn get-normal-matrix [mv] 80 | (-> mv 81 | (geom/invert) 82 | (geom/transpose) 83 | (mat/matrix44->matrix33))) 84 | 85 | (defn get-data [p mv vertices cube-normals texture texture-coords] 86 | (let [now (/ (.getTime (js/Date.)) 1000) 87 | use-lighting? true] 88 | {u-p-matrix p 89 | u-mv-matrix mv 90 | u-n-matrix (get-normal-matrix mv) 91 | u-ambient-color [0.8 0.8 0.8] 92 | u-lighting-direction [-0.25 0.25 1] 93 | u-directional-color [0.8 0.8 0.8] 94 | u-sampler texture 95 | u-use-lighting use-lighting? 96 | a-position vertices 97 | a-texture-coord texture-coords 98 | a-vertex-normal cube-normals})) 99 | 100 | (defn reset-gl-canvas! [canvas-node] 101 | (let [gl (.getContext canvas-node "webgl") 102 | width (.-clientWidth canvas-node) 103 | height (.-clientHeight canvas-node)] 104 | ;; Set the width/height (in terms of GL-resolution) to actual 105 | ;; canvas-element width/height (or else you'll see blurry results) 106 | (set! (.-width canvas-node) width) 107 | (set! (.-height canvas-node) height) 108 | ;; Setup GL Canvas 109 | (.viewport gl 0 0 width height))) 110 | 111 | ;; js/window.requestAnimationFrame doesn't take arguments, so we have 112 | ;; to store the state elsewhere - in this atom, for example. 113 | (defn app-state [width height] 114 | {:last-rendered 0 115 | :scene {:cube-vertices [ ;; Front face 116 | [-1.0 -1.0 1.0] 117 | [1.0 -1.0 1.0] 118 | [1.0 1.0 1.0] 119 | [-1.0 1.0 1.0] 120 | 121 | ;; Back face 122 | [-1.0 -1.0 -1.0] 123 | [-1.0 1.0 -1.0] 124 | [1.0 1.0 -1.0] 125 | [1.0 -1.0 -1.0] 126 | 127 | ;; Top face 128 | [-1.0 1.0 -1.0] 129 | [-1.0 1.0 1.0] 130 | [1.0 1.0 1.0] 131 | [1.0 1.0 -1.0] 132 | 133 | ;; Bottom face 134 | [-1.0 -1.0 -1.0] 135 | [1.0 -1.0 -1.0] 136 | [1.0 -1.0 1.0] 137 | [-1.0 -1.0 1.0] 138 | 139 | ;; Right face 140 | [1.0 -1.0 -1.0] 141 | [1.0 1.0 -1.0] 142 | [1.0 1.0 1.0] 143 | [1.0 -1.0 1.0] 144 | 145 | ;; Left face 146 | [-1.0 -1.0 -1.0] 147 | [-1.0 -1.0 1.0] 148 | [-1.0 1.0 1.0] 149 | [-1.0 1.0 -1.0]] 150 | :cube-texture-coords [ 151 | ;; Front face 152 | 0.0, 0.0, 153 | 1.0, 0.0, 154 | 1.0, 1.0, 155 | 0.0, 1.0, 156 | 157 | ;; Back face 158 | 1.0, 0.0, 159 | 1.0, 1.0, 160 | 0.0, 1.0, 161 | 0.0, 0.0, 162 | 163 | ;; Top face 164 | 0.0, 1.0, 165 | 0.0, 0.0, 166 | 1.0, 0.0, 167 | 1.0, 1.0, 168 | 169 | ;; Bottom face 170 | 1.0, 1.0, 171 | 0.0, 1.0, 172 | 0.0, 0.0, 173 | 1.0, 0.0, 174 | 175 | ;; Right face 176 | 1.0, 0.0, 177 | 1.0, 1.0, 178 | 0.0, 1.0, 179 | 0.0, 0.0, 180 | 181 | ;; Left face 182 | 0.0, 0.0, 183 | 1.0, 0.0, 184 | 1.0, 1.0, 185 | 0.0, 1.0,] 186 | :cube-indices [0 1 2 0 2 3 ;; Front face 187 | 4 5 6 4 6 7 ;; Back face 188 | 8 9 10 8 10 11 ;; Top face 189 | 12 13 14 12 14 15 ;; Bottom face 190 | 16 17 18 16 18 19 ;; Right face 191 | 20 21 22 20 22 23 ;; 192 | ] ;; Left face 193 | :cube-normals [;; Front face 194 | [0.0, 0.0, 1.0,] 195 | [0.0, 0.0, 1.0,] 196 | [0.0, 0.0, 1.0,] 197 | [0.0, 0.0, 1.0,] 198 | 199 | ;; Back face 200 | [0.0, 0.0, -1.0,] 201 | [0.0, 0.0, -1.0,] 202 | [0.0, 0.0, -1.0,] 203 | [0.0, 0.0, -1.0,] 204 | 205 | ;; Top face 206 | [0.0, 1.0, 0.0,] 207 | [0.0, 1.0, 0.0,] 208 | [0.0, 1.0, 0.0,] 209 | [0.0, 1.0, 0.0,] 210 | 211 | ;; Bottom face 212 | [0.0, -1.0, 0.0,] 213 | [0.0, -1.0, 0.0,] 214 | [0.0, -1.0, 0.0,] 215 | [0.0, -1.0, 0.0,] 216 | 217 | ;; Right face 218 | [1.0, 0.0, 0.0,] 219 | [1.0, 0.0, 0.0,] 220 | [1.0, 0.0, 0.0,] 221 | [1.0, 0.0, 0.0,] 222 | 223 | ;; Left face 224 | [-1.0, 0.0, 0.0,] 225 | [-1.0, 0.0, 0.0,] 226 | [-1.0, 0.0, 0.0,] 227 | [-1.0, 0.0, 0.0]] 228 | :cube-rotation 0 229 | :mv (mat/matrix44) 230 | :p (get-perspective-matrix width height)}}) 231 | 232 | (defn draw-fn [gl driver program] 233 | (fn [state] 234 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 235 | (let [{:keys [p mv 236 | texture 237 | cube-normals 238 | cube-vertices cube-texture-coords 239 | cube-rotation cube-indices]} (:scene state) 240 | now (/ (.getTime (js/Date.)) 1000) 241 | x (* 3 (js/Math.sin now))] 242 | (let [mv (-> mv 243 | (geom/translate [x 0 -7]) 244 | (geom/rotate-around-axis [1 0 0] cube-rotation) 245 | (geom/rotate-around-axis [0 -1 0] cube-rotation))] 246 | (gd/draw-elements driver (gd/bind driver program 247 | (assoc (get-data p mv cube-vertices cube-normals texture cube-texture-coords) 248 | {:tag :element-index} cube-indices)) 249 | {:draw-mode :triangles 250 | :first 0 251 | ;; Hard-coded 252 | :count 36}))))) 253 | 254 | (defn animate [draw-fn step-fn current-value] 255 | (js/requestAnimationFrame 256 | (fn [time] 257 | (let [next-value (step-fn time current-value)] 258 | (draw-fn next-value) 259 | (animate draw-fn step-fn next-value))))) 260 | 261 | (defn tick 262 | "Takes the old world value and produces a new world value, suitable 263 | for rendering" 264 | [time state] 265 | ;; We get the elapsed time since the last render to compensate for 266 | ;; lag, etc. 267 | (let [time-now (.getTime (js/Date.)) 268 | elapsed (- time-now (:last-rendered state)) 269 | cube-diff (/ (* 75 elapsed) 100000)] 270 | (-> state 271 | (update-in [:scene :cube-rotation] + cube-diff) 272 | (assoc-in [:last-rendered] time-now)))) 273 | 274 | (defn main [_ node] 275 | (let [gl (.getContext node "webgl") 276 | width (.-clientWidth node) 277 | height (.-clientHeight node) 278 | driver (driver/basic-driver gl) 279 | program program-source 280 | state (app-state width height)] 281 | (reset-gl-canvas! node) 282 | (.enable gl (.-DEPTH_TEST gl)) 283 | (.clearColor gl 0 0 0 1) 284 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 285 | (let [image (js/Image.)] 286 | (aset image "onload" 287 | (fn [] (let [texture {:data image 288 | :filter {:min :linear 289 | :mag :nearest} 290 | :flip-y true 291 | :texture-id 0}] 292 | (animate (draw-fn gl driver program) tick (assoc-in state [:scene :texture] texture))))) 293 | (aset image "src" "/images/crate.gif")))) 294 | 295 | (def explanation 296 | nil) 297 | 298 | (def summary 299 | {:title title 300 | :enter main 301 | :explanation explanation}) 302 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_08.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.learn-gamma.lesson-08 2 | (:require [gamma.api :as g] 3 | [gamma.program :as p] 4 | [gamma-driver.api :as gd] 5 | [gamma-driver.drivers.basic :as driver] 6 | [thi.ng.geom.core :as geom] 7 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 8 | 9 | (def title 10 | "8. The depth buffer, transparency, and blending") 11 | 12 | (def u-p-matrix 13 | (g/uniform "uPMatrix" :mat4)) 14 | 15 | (def u-mv-matrix 16 | (g/uniform "uMVMatrix" :mat4)) 17 | 18 | (def u-n-matrix 19 | (g/uniform "uNMatrix" :mat3)) 20 | 21 | (def u-ambient-color 22 | (g/uniform "uAmbientColor" :vec3)) 23 | 24 | (def u-lighting-direction 25 | (g/uniform "uLightingDirection" :vec3)) 26 | 27 | (def u-directional-color 28 | (g/uniform "uDirectionalColor" :vec3)) 29 | 30 | (def u-use-lighting 31 | (g/uniform "uUseLighting" :bool)) 32 | 33 | (def u-alpha 34 | (g/uniform "uAlpha" :float)) 35 | 36 | (def a-position 37 | (g/attribute "aVertexPosition" :vec3)) 38 | 39 | (def a-vertex-normal 40 | (g/attribute "aVertexNormal" :vec3)) 41 | 42 | (def a-texture-coord 43 | (g/attribute "aTextureCoord" :vec2)) 44 | 45 | (def v-texture-coord 46 | (g/varying "vTextureCoord" :vec2 :mediump)) 47 | 48 | (def v-light-weighting 49 | (g/varying "vLightWeighting" :vec3 :mediump)) 50 | 51 | (def u-sampler 52 | (g/uniform "uSampler" :sampler2D)) 53 | 54 | (def program-source 55 | (p/program 56 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 57 | (g/* u-mv-matrix) 58 | (g/* (g/vec4 a-position 1))) 59 | v-texture-coord a-texture-coord 60 | v-light-weighting (g/if (g/not u-use-lighting) 61 | (let [transformed-normal (g/* u-n-matrix a-vertex-normal) 62 | directional-light-weighting (g/max (g/dot transformed-normal u-lighting-direction) 0.0)] 63 | (g/* directional-light-weighting 64 | (g/+ u-ambient-color u-directional-color))) 65 | (g/vec3 1 1 1))} 66 | :fragment-shader (let [texture-color (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st))) 67 | rgb (g/* (g/swizzle texture-color :rgb) v-light-weighting) 68 | a (g/swizzle texture-color :a)] 69 | {(g/gl-frag-color) (g/vec4 rgb (g/* a u-alpha))}) 70 | :precision {:float :mediump}})) 71 | 72 | (defn get-perspective-matrix 73 | "Be sure to 74 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 75 | the GL context 76 | 2. (set! (.-width/height canvas-node) 77 | width/height), respectively, or you may see no results, or strange 78 | results" 79 | [width height] 80 | (mat/perspective 45 (/ width height) 0.1 100)) 81 | 82 | (defn get-normal-matrix [mv] 83 | (-> mv 84 | (geom/invert) 85 | (geom/transpose) 86 | (mat/matrix44->matrix33))) 87 | 88 | (defn get-data [p mv vertices cube-normals texture texture-coords] 89 | (let [now (/ (.getTime (js/Date.)) 1000) 90 | use-lighting? true] 91 | {u-p-matrix p 92 | u-mv-matrix mv 93 | u-n-matrix (get-normal-matrix mv) 94 | u-ambient-color [0.2 0.2 0.2] 95 | u-alpha 0.5 96 | u-lighting-direction [-0.25 0.25 1] 97 | u-directional-color [0.8 0.8 0.8] 98 | u-sampler texture 99 | u-use-lighting use-lighting? 100 | a-position vertices 101 | a-texture-coord texture-coords 102 | a-vertex-normal cube-normals})) 103 | 104 | (defn reset-gl-canvas! [canvas-node] 105 | (let [gl (.getContext canvas-node "webgl") 106 | width (.-clientWidth canvas-node) 107 | height (.-clientHeight canvas-node)] 108 | ;; Set the width/height (in terms of GL-resolution) to actual 109 | ;; canvas-element width/height (or else you'll see blurry results) 110 | (set! (.-width canvas-node) width) 111 | (set! (.-height canvas-node) height) 112 | ;; Setup GL Canvas 113 | (.viewport gl 0 0 width height))) 114 | 115 | ;; js/window.requestAnimationFrame doesn't take arguments, so we have 116 | ;; to store the state elsewhere - in this atom, for example. 117 | (defn app-state [width height] 118 | {:last-rendered 0 119 | :scene {:cube-vertices [ ;; Front face 120 | [-1.0 -1.0 1.0] 121 | [1.0 -1.0 1.0] 122 | [1.0 1.0 1.0] 123 | [-1.0 1.0 1.0] 124 | 125 | ;; Back face 126 | [-1.0 -1.0 -1.0] 127 | [-1.0 1.0 -1.0] 128 | [1.0 1.0 -1.0] 129 | [1.0 -1.0 -1.0] 130 | 131 | ;; Top face 132 | [-1.0 1.0 -1.0] 133 | [-1.0 1.0 1.0] 134 | [1.0 1.0 1.0] 135 | [1.0 1.0 -1.0] 136 | 137 | ;; Bottom face 138 | [-1.0 -1.0 -1.0] 139 | [1.0 -1.0 -1.0] 140 | [1.0 -1.0 1.0] 141 | [-1.0 -1.0 1.0] 142 | 143 | ;; Right face 144 | [1.0 -1.0 -1.0] 145 | [1.0 1.0 -1.0] 146 | [1.0 1.0 1.0] 147 | [1.0 -1.0 1.0] 148 | 149 | ;; Left face 150 | [-1.0 -1.0 -1.0] 151 | [-1.0 -1.0 1.0] 152 | [-1.0 1.0 1.0] 153 | [-1.0 1.0 -1.0]] 154 | :cube-texture-coords [ 155 | ;; Front face 156 | 0.0, 0.0, 157 | 1.0, 0.0, 158 | 1.0, 1.0, 159 | 0.0, 1.0, 160 | 161 | ;; Back face 162 | 1.0, 0.0, 163 | 1.0, 1.0, 164 | 0.0, 1.0, 165 | 0.0, 0.0, 166 | 167 | ;; Top face 168 | 0.0, 1.0, 169 | 0.0, 0.0, 170 | 1.0, 0.0, 171 | 1.0, 1.0, 172 | 173 | ;; Bottom face 174 | 1.0, 1.0, 175 | 0.0, 1.0, 176 | 0.0, 0.0, 177 | 1.0, 0.0, 178 | 179 | ;; Right face 180 | 1.0, 0.0, 181 | 1.0, 1.0, 182 | 0.0, 1.0, 183 | 0.0, 0.0, 184 | 185 | ;; Left face 186 | 0.0, 0.0, 187 | 1.0, 0.0, 188 | 1.0, 1.0, 189 | 0.0, 1.0,] 190 | :cube-indices [0 1 2 0 2 3 ;; Front face 191 | 4 5 6 4 6 7 ;; Back face 192 | 8 9 10 8 10 11 ;; Top face 193 | 12 13 14 12 14 15 ;; Bottom face 194 | 16 17 18 16 18 19 ;; Right face 195 | 20 21 22 20 22 23 ;; 196 | ] ;; Left face 197 | :cube-normals [;; Front face 198 | [0.0, 0.0, 1.0,] 199 | [0.0, 0.0, 1.0,] 200 | [0.0, 0.0, 1.0,] 201 | [0.0, 0.0, 1.0,] 202 | 203 | ;; Back face 204 | [0.0, 0.0, -1.0,] 205 | [0.0, 0.0, -1.0,] 206 | [0.0, 0.0, -1.0,] 207 | [0.0, 0.0, -1.0,] 208 | 209 | ;; Top face 210 | [0.0, 1.0, 0.0,] 211 | [0.0, 1.0, 0.0,] 212 | [0.0, 1.0, 0.0,] 213 | [0.0, 1.0, 0.0,] 214 | 215 | ;; Bottom face 216 | [0.0, -1.0, 0.0,] 217 | [0.0, -1.0, 0.0,] 218 | [0.0, -1.0, 0.0,] 219 | [0.0, -1.0, 0.0,] 220 | 221 | ;; Right face 222 | [1.0, 0.0, 0.0,] 223 | [1.0, 0.0, 0.0,] 224 | [1.0, 0.0, 0.0,] 225 | [1.0, 0.0, 0.0,] 226 | 227 | ;; Left face 228 | [-1.0, 0.0, 0.0,] 229 | [-1.0, 0.0, 0.0,] 230 | [-1.0, 0.0, 0.0,] 231 | [-1.0, 0.0, 0.0]] 232 | :cube-rotation 0 233 | :mv (mat/matrix44) 234 | :p (get-perspective-matrix width height)}}) 235 | 236 | (defn draw-fn [gl driver program] 237 | (fn [state] 238 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 239 | (let [{:keys [p mv 240 | texture 241 | cube-normals 242 | cube-vertices cube-texture-coords 243 | cube-rotation cube-indices]} (:scene state) 244 | now (/ (.getTime (js/Date.)) 1000) 245 | x (* 3 (js/Math.sin now))] 246 | (let [mv (-> mv 247 | (geom/translate [x 0 -7]) 248 | (geom/rotate-around-axis [1 0 0] cube-rotation) 249 | (geom/rotate-around-axis [0 -1 0] cube-rotation))] 250 | (gd/draw-elements driver (gd/bind driver program 251 | (assoc (get-data p mv cube-vertices cube-normals texture cube-texture-coords) 252 | {:tag :element-index} cube-indices)) 253 | {:draw-mode :triangles 254 | :first 0 255 | ;; Hard-coded 256 | :count 36}))))) 257 | 258 | (defn animate [draw-fn step-fn current-value] 259 | (js/requestAnimationFrame 260 | (fn [time] 261 | (let [next-value (step-fn time current-value)] 262 | (draw-fn next-value) 263 | (animate draw-fn step-fn next-value))))) 264 | 265 | (defn tick 266 | "Takes the old world value and produces a new world value, suitable 267 | for rendering" 268 | [time state] 269 | ;; We get the elapsed time since the last render to compensate for 270 | ;; lag, etc. 271 | (let [time-now (.getTime (js/Date.)) 272 | elapsed (- time-now (:last-rendered state)) 273 | cube-diff (/ (* 75 elapsed) 100000)] 274 | (-> state 275 | (update-in [:scene :cube-rotation] + cube-diff) 276 | (assoc-in [:last-rendered] time-now)))) 277 | 278 | (defn main [_ node] 279 | (let [gl (.getContext node "webgl") 280 | width (.-clientWidth node) 281 | height (.-clientHeight node) 282 | driver (driver/basic-driver gl) 283 | program program-source 284 | state (app-state width height)] 285 | (reset-gl-canvas! node) 286 | ;; Set the blending function 287 | (.blendFunc gl (.-SRC_ALPHA gl) (.-ONE gl)) 288 | (.enable gl (.-BLEND gl)) 289 | (.disable gl (.-DEPTH_TEST gl)) 290 | (.clearColor gl 0 0 0 1) 291 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 292 | (let [image (js/Image.)] 293 | (aset image "onload" 294 | (fn [] (let [texture {:data image 295 | :filter {:min :linear 296 | :mag :nearest} 297 | :flip-y true 298 | :texture-id 0}] 299 | (animate (draw-fn gl driver program) tick (assoc-in state [:scene :texture] texture))))) 300 | (aset image "src" "/images/glass.gif")))) 301 | 302 | (def explanation 303 | nil) 304 | 305 | (def summary 306 | {:title title 307 | :enter main 308 | :explanation explanation}) 309 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_09.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.learn-gamma.lesson-09 2 | (:require [gamma.api :as g] 3 | [gamma.program :as p] 4 | [gamma-driver.api :as gd] 5 | [gamma-driver.drivers.basic :as driver] 6 | [thi.ng.geom.core :as geom] 7 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 8 | 9 | (def title 10 | "9. Lots of moving objects") 11 | 12 | (def u-p-matrix 13 | (g/uniform "uPMatrix" :mat4)) 14 | 15 | (def u-mv-matrix 16 | (g/uniform "uMVMatrix" :mat4)) 17 | 18 | (def a-position 19 | (g/attribute "aVertexPosition" :vec3)) 20 | 21 | (def a-vertex-normal 22 | (g/attribute "aVertexNormal" :vec3)) 23 | 24 | (def a-texture-coord 25 | (g/attribute "aTextureCoord" :vec2)) 26 | 27 | (def v-texture-coord 28 | (g/varying "vTextureCoord" :vec2 :mediump)) 29 | 30 | (def u-color 31 | (g/uniform "uColor" :vec3)) 32 | 33 | (def u-sampler 34 | (g/uniform "uSampler" :sampler2D)) 35 | 36 | (def program-source 37 | (p/program 38 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 39 | (g/* u-mv-matrix) 40 | (g/* (g/vec4 a-position 1))) 41 | v-texture-coord a-texture-coord} 42 | :fragment-shader (let [texture-color (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st)))] 43 | {(g/gl-frag-color) (g/* texture-color (g/vec4 u-color 1))}) 44 | :precision {:float :mediump}})) 45 | 46 | (defn get-perspective-matrix 47 | "Be sure to 48 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 49 | the GL context 50 | 2. (set! (.-width/height canvas-node) 51 | width/height), respectively, or you may see no results, or strange 52 | results" 53 | [width height] 54 | (mat/perspective 45 (/ width height) 0.1 100)) 55 | 56 | (defn get-normal-matrix [mv] 57 | (-> mv 58 | (geom/invert) 59 | (geom/transpose) 60 | (mat/matrix44->matrix33))) 61 | 62 | (defn get-data [p mv vertices texture texture-coords color] 63 | {u-p-matrix p 64 | u-mv-matrix mv 65 | u-color color 66 | u-sampler texture 67 | a-position vertices 68 | a-texture-coord texture-coords}) 69 | 70 | (defn reset-gl-canvas! [canvas-node] 71 | (let [gl (.getContext canvas-node "webgl") 72 | width (.-clientWidth canvas-node) 73 | height (.-clientHeight canvas-node)] 74 | ;; Set the width/height (in terms of GL-resolution) to actual 75 | ;; canvas-element width/height (or else you'll see blurry results) 76 | (set! (.-width canvas-node) width) 77 | (set! (.-height canvas-node) height) 78 | ;; Setup GL Canvas 79 | (.viewport gl 0 0 width height))) 80 | 81 | (defn random-color [] 82 | [(js/Math.random) (js/Math.random) (js/Math.random)]) 83 | 84 | (defn make-star [star-count idx] 85 | {:distance (* (/ idx star-count) 5) 86 | :rotation-speed (/ idx star-count 10) 87 | :angle 0 88 | :color (random-color) 89 | :twinkle (random-color)}) 90 | 91 | ;; js/window.requestAnimationFrame doesn't take arguments, so we have 92 | ;; to store the state elsewhere - in this atom, for example. 93 | (defn app-state [width height] 94 | (let [star-count 50] 95 | {:last-rendered (.getTime (js/Date.)) 96 | :scene {:stars (map (partial make-star star-count) (range star-count)) 97 | :star-vertices {:id :star-vertices 98 | :data [[-1.0, -1.0, 0.0,] 99 | [1.0, -1.0, 0.0,] 100 | [-1.0, 1.0, 0.0,] 101 | [1.0, 1.0, 0.0]] 102 | :immutable? true} 103 | :star-texture-coords {:id :star-texture-coords 104 | :data [0.0, 0.0, 105 | 1.0, 0.0, 106 | 0.0, 1.0, 107 | 1.0, 1.0] 108 | :immutable? true} 109 | :mv (mat/matrix44) 110 | :p (get-perspective-matrix width height)}})) 111 | 112 | (defn draw-fn [gl driver program] 113 | (fn [state] 114 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 115 | (let [{:keys [p mv texture 116 | stars star-vertices 117 | star-texture-coords]} (:scene state) 118 | time-now (.getTime (js/Date.)) 119 | twinkle? (pos? (js/Math.sin (/ time-now 1000)))] 120 | (doseq [star stars] 121 | (let [mv (-> mv 122 | (geom/translate [0 0 -7]) 123 | (geom/rotate-around-axis [0 0 1] (:angle star)) 124 | (geom/translate [(:distance star) 0 -7]) 125 | (geom/rotate-around-axis [0 0 -1] (- (:angle star))))] 126 | (gd/draw-arrays driver (gd/bind driver program 127 | (get-data p mv star-vertices texture star-texture-coords (if twinkle? 128 | (:twinkle star) 129 | (:color star)))) 130 | {:draw-mode :triangle-strip})))))) 131 | 132 | (defn animate [draw-fn step-fn current-value] 133 | (js/requestAnimationFrame 134 | (fn [time] 135 | (let [next-value (step-fn time current-value)] 136 | (draw-fn next-value) 137 | (animate draw-fn step-fn next-value))))) 138 | 139 | (def effective-fpms 140 | (/ 60 1000)) 141 | 142 | (defn move-star [elapsed star] 143 | (let [distance (- (:distance star) 144 | (* effective-fpms elapsed 0.01)) 145 | reset (if (pos? distance) 0 5)] 146 | (-> star 147 | (assoc-in [:distance] (+ distance reset)) 148 | (update-in [:angle] + (* (:rotation-speed star)))))) 149 | 150 | (defn tick 151 | "Takes the old world value and produces a new world value, suitable 152 | for rendering" 153 | [time state] 154 | ;; We get the elapsed time since the last render to compensate for 155 | ;; lag, etc. 156 | (let [time-now (.getTime (js/Date.)) 157 | elapsed (- time-now (:last-rendered state)) 158 | cube-diff (/ (* 75 elapsed) 100000)] 159 | (-> state 160 | ;; This is painful at 60FPS. Transducers? 161 | (update-in [:scene :stars] (fn [stars] (mapv (partial move-star elapsed) stars))) 162 | (assoc-in [:last-rendered] time-now)))) 163 | 164 | (defn main [_ node] 165 | (let [gl (.getContext node "webgl") 166 | width (.-clientWidth node) 167 | height (.-clientHeight node) 168 | driver (driver/basic-driver gl) 169 | program program-source 170 | state (app-state width height)] 171 | (reset-gl-canvas! node) 172 | ;; Set the blending function 173 | (.blendFunc gl (.-SRC_ALPHA gl) (.-ONE gl)) 174 | (.enable gl (.-BLEND gl)) 175 | (.disable gl (.-DEPTH_TEST gl)) 176 | (.clearColor gl 0 0 0 1) 177 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 178 | (let [image (js/Image.)] 179 | (aset image "onload" 180 | (fn [] (let [texture {:data image 181 | :filter {:min :linear 182 | :mag :nearest} 183 | :flip-y true 184 | :texture-id 0}] 185 | (animate (draw-fn gl driver program) tick (assoc-in state [:scene :texture] texture))))) 186 | (aset image "src" "/images/star.gif")))) 187 | 188 | (def explanation 189 | nil) 190 | 191 | (def summary 192 | {:title title 193 | :enter main 194 | :explanation explanation}) 195 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_11.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-always gampg.learn-gamma.lesson-11 2 | (:require [gamma.api :as g] 3 | [gamma.program :as p] 4 | [gamma-driver.api :as gd] 5 | [gamma-driver.drivers.basic :as driver] 6 | [thi.ng.geom.core :as geom] 7 | [thi.ng.geom.core.matrix :as mat :refer [M44]])) 8 | 9 | (def title 10 | "11. Spheres, rotation matrices, and mouse events") 11 | 12 | (def u-p-matrix 13 | (g/uniform "uPMatrix" :mat4)) 14 | 15 | (def u-mv-matrix 16 | (g/uniform "uMVMatrix" :mat4)) 17 | 18 | (def u-n-matrix 19 | (g/uniform "uNMatrix" :mat3)) 20 | 21 | (def u-ambient-color 22 | (g/uniform "uAmbientColor" :vec3)) 23 | 24 | (def u-lighting-direction 25 | (g/uniform "uLightingDirection" :vec3)) 26 | 27 | (def u-directional-color 28 | (g/uniform "uDirectionalColor" :vec3)) 29 | 30 | (def u-use-lighting 31 | (g/uniform "uUseLighting" :bool)) 32 | 33 | (def u-alpha 34 | (g/uniform "uAlpha" :float)) 35 | 36 | (def a-position 37 | (g/attribute "aVertexPosition" :vec3)) 38 | 39 | (def a-vertex-normal 40 | (g/attribute "aVertexNormal" :vec3)) 41 | 42 | (def a-texture-coord 43 | (g/attribute "aTextureCoord" :vec2)) 44 | 45 | (def v-texture-coord 46 | (g/varying "vTextureCoord" :vec2 :mediump)) 47 | 48 | (def v-light-weighting 49 | (g/varying "vLightWeighting" :vec3 :mediump)) 50 | 51 | (def u-sampler 52 | (g/uniform "uSampler" :sampler2D)) 53 | 54 | (def program-source 55 | (p/program 56 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 57 | (g/* u-mv-matrix) 58 | (g/* (g/vec4 a-position 1))) 59 | v-texture-coord a-texture-coord 60 | v-light-weighting (g/if (g/not u-use-lighting) 61 | (let [transformed-normal (g/* u-n-matrix a-vertex-normal) 62 | directional-light-weighting (g/max (g/dot transformed-normal u-lighting-direction) 0.0)] 63 | (g/* directional-light-weighting 64 | (g/+ u-ambient-color u-directional-color))) 65 | (g/vec3 1 1 1))} 66 | :fragment-shader (let [texture-color (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st))) 67 | rgb (g/* (g/swizzle texture-color :rgb) v-light-weighting) 68 | a (g/swizzle texture-color :a)] 69 | {(g/gl-frag-color) (g/vec4 rgb (g/* a u-alpha))}) 70 | :precision {:float :mediump}})) 71 | 72 | (defn get-perspective-matrix 73 | "Be sure to 74 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 75 | the GL context 76 | 2. (set! (.-width/height canvas-node) 77 | width/height), respectively, or you may see no results, or strange 78 | results" 79 | [width height] 80 | (mat/perspective 45 (/ width height) 0.1 100)) 81 | 82 | (defn get-normal-matrix [mv] 83 | (-> mv 84 | (geom/invert) 85 | (geom/transpose) 86 | (mat/matrix44->matrix33))) 87 | 88 | (defn reset-gl-canvas! [canvas-node] 89 | (let [gl (.getContext canvas-node "webgl") 90 | width (.-clientWidth canvas-node) 91 | height (.-clientHeight canvas-node)] 92 | ;; Set the width/height (in terms of GL-resolution) to actual 93 | ;; canvas-element width/height (or else you'll see blurry results) 94 | (set! (.-width canvas-node) width) 95 | (set! (.-height canvas-node) height) 96 | ;; Setup GL Canvas 97 | (.viewport gl 0 0 width height))) 98 | 99 | ;; js/window.requestAnimationFrame doesn't take arguments, so we have 100 | ;; to store the state elsewhere - in this atom, for example. 101 | (defn app-state [width height sphere] 102 | {:last-rendered 0 103 | :scene {:sphere sphere 104 | :mv (mat/matrix44) 105 | :p (get-perspective-matrix width height)}}) 106 | 107 | (defn get-data [p mv vertices normals texture texture-coords] 108 | (let [now (/ (.getTime (js/Date.)) 1000) 109 | use-lighting? true] 110 | {u-p-matrix p 111 | u-mv-matrix mv 112 | u-n-matrix (get-normal-matrix mv) 113 | u-ambient-color [0 0 0] 114 | u-alpha 1 115 | u-lighting-direction [-0.25 0.25 1] 116 | u-directional-color [0.8 0.8 0.8] 117 | u-sampler texture 118 | u-use-lighting use-lighting? 119 | a-position vertices 120 | a-texture-coord texture-coords 121 | a-vertex-normal normals})) 122 | 123 | (defn draw-fn [gl driver program] 124 | (fn [state] 125 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 126 | (let [{:keys [p mv 127 | texture sphere 128 | rotation]} (:scene state) 129 | now (/ (.getTime (js/Date.)) 1000) 130 | rotation (* js/Math.PI (js/Math.sin now))] 131 | (let [mv (-> mv 132 | (geom/translate [0 0 -10]) 133 | (geom/rotate-around-axis [1 0 0] rotation) 134 | (geom/rotate-around-axis [0 -1 0] (/ rotation 10))) 135 | scene-data (select-keys (get-data p mv (:vertices sphere) (:normals sphere) texture (:texture-coords sphere)) 136 | (:inputs program)) 137 | scene-data (assoc scene-data 138 | {:tag :element-index} (:indices sphere))] 139 | (gd/draw-elements driver 140 | (gd/bind driver program scene-data) 141 | {:draw-mode :triangles 142 | :first 0 143 | :count (get-in sphere [:indices :count])}))))) 144 | 145 | (def manual-step-frame-by-frame? 146 | false) 147 | 148 | (defn animate [draw-fn step-fn current-value] 149 | (js/requestAnimationFrame 150 | (fn [time] 151 | (let [next-value (step-fn time current-value)] 152 | (draw-fn next-value) 153 | (if manual-step-frame-by-frame? 154 | (set! (.-tick js/window) 155 | #(animate draw-fn step-fn next-value)) 156 | (animate draw-fn step-fn next-value)))))) 157 | 158 | (defn tick 159 | "Takes the old world value and produces a new world value, suitable 160 | for rendering" 161 | [time state] 162 | ;; We get the elapsed time since the last render to compensate for 163 | ;; lag, etc. 164 | (let [time-now (.getTime (js/Date.)) 165 | elapsed (- time-now (:last-rendered state)) 166 | cube-diff (/ (* 75 elapsed) 100000)] 167 | (-> state 168 | (update-in [:scene :cube-rotation] + cube-diff) 169 | (assoc-in [:last-rendered] time-now)))) 170 | 171 | ;; TODO: Convert this to use transducers, it's waaaaaaaaaaay to slow, 172 | ;; unnecessarily 173 | (defn generate-sphere [latitude-bands longitude-bands radius] 174 | (let [raw-sphere (vec (mapcat (fn [lat-number] 175 | (let [theta (/ (* lat-number js/Math.PI) latitude-bands) 176 | sin-theta (js/Math.sin theta) 177 | cos-theta (js/Math.cos theta)] 178 | (mapv (fn [long-number] 179 | (let [phi (/ (* long-number 2 js/Math.PI) longitude-bands) 180 | sin-phi (js/Math.sin phi) 181 | cos-phi (js/Math.cos phi) 182 | x (* cos-phi sin-theta) 183 | y cos-theta 184 | z (* sin-phi sin-theta) 185 | u (- 1 (/ long-number longitude-bands)) 186 | v (- 1 (/ lat-number latitude-bands)) 187 | normal [x y z] 188 | texture-coord [u v] 189 | vertex [(* x radius) (* y radius) (* z radius)]] 190 | [normal texture-coord vertex])) (range (inc longitude-bands))))) (range (inc latitude-bands)))) 191 | sphere (reduce (fn [run [normal texture-coord vertex]] 192 | (-> run 193 | (update-in [:normals :data] concat normal) 194 | (update-in [:texture-coords :data] concat texture-coord) 195 | (update-in [:vertices :data] concat vertex))) 196 | {:normals {:data [] 197 | :immutable? true} 198 | :texture-coords {:data [] 199 | :immutable? true} 200 | :vertices {:data [] 201 | :immutable? true}} raw-sphere) 202 | indices (vec (mapcat (fn [lat-number] 203 | (mapcat (fn [long-number] 204 | (let [fst (+ (* lat-number (inc longitude-bands)) long-number) 205 | scnd (+ fst longitude-bands 1)] 206 | [fst scnd (inc fst) 207 | scnd (inc scnd) (inc fst)])) (range longitude-bands))) (range latitude-bands)))] 208 | (assoc sphere :indices {:data indices 209 | :count (count indices) 210 | :immutable? true}))) 211 | 212 | (defn main [_ node] 213 | (let [gl (.getContext node "webgl") 214 | width (.-clientWidth node) 215 | height (.-clientHeight node) 216 | driver (driver/basic-driver gl) 217 | program (gd/program driver program-source) 218 | sphere (time (generate-sphere 30 30 2)) 219 | state (time (app-state width height sphere))] 220 | (reset-gl-canvas! node) 221 | ;; Set the blending function 222 | ;;(.blendFunc gl (.-SRC_ALPHA gl) (.-ONE gl)) 223 | ;;(.enable gl (.-BLEND gl)) 224 | ;;(.disable gl (.-DEPTH_TEST gl)) 225 | (.enable gl (.-DEPTH_TEST gl)) 226 | (.clearColor gl 0 0 0 1) 227 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 228 | (let [image (js/Image.)] 229 | (aset image "onload" 230 | (fn [] (let [texture {:data image 231 | :filter {:min :linear 232 | :mag :nearest} 233 | :flip-y true 234 | :immutable? true}] 235 | (animate (draw-fn gl driver program) tick (assoc-in state [:scene :texture] texture))))) 236 | (aset image "src" "/images/moon.gif")))) 237 | 238 | (def explanation 239 | nil) 240 | 241 | (def summary 242 | {:title title 243 | :enter main 244 | :explanation explanation}) 245 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_14.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-always gampg.learn-gamma.lesson-14 2 | (:require [cljs.core.async :as async :refer [ (g/* -1 v-pos-xyz) 89 | g/normalize) 90 | reflection-direction (g/reflect (g/* -1 light-direction) normal) 91 | specular-light-weighting (-> (g/dot reflection-direction eye-direction) 92 | (g/max 0) 93 | (g/pow u-material-shininess)) 94 | diffuse-light-weighting (-> (g/dot normal light-direction) 95 | (g/max 0)) 96 | light-weighting (-> (g/+ u-ambient-color u-point-lighting-specular-color) 97 | (g/* specular-light-weighting) 98 | (g/+ u-point-lighting-diffuse-color) 99 | (g/* direction-light-weighting)) 100 | texture-color (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st))) 101 | a (g/swizzle texture-color :a) 102 | rgb (g/* (g/swizzle texture-color :rgb) light-weighting)] 103 | {(g/gl-frag-color) (do (g/vec4 rgb (g/swizzle texture-color :a)) 104 | (g/vec4 (g/* (g/swizzle texture-color :rgb) 105 | light-weighting) 1))}) 106 | :precision {:float :mediump}})) 107 | 108 | (defn get-perspective-matrix 109 | "Be sure to 110 | 1. pass the WIDTH and HEIGHT of the canvas *node*, not 111 | the GL context 112 | 2. (set! (.-width/height canvas-node) 113 | width/height), respectively, or you may see no results, or strange 114 | results" 115 | [width height] 116 | (mat/perspective 45 (/ width height) 0.1 100)) 117 | 118 | (defn get-normal-matrix [mv] 119 | (-> mv 120 | (geom/invert) 121 | (geom/transpose) 122 | (mat/matrix44->matrix33))) 123 | 124 | (defn reset-gl-canvas! [canvas-node] 125 | (let [gl (.getContext canvas-node "webgl") 126 | width (.-clientWidth canvas-node) 127 | height (.-clientHeight canvas-node)] 128 | ;; Set the width/height (in terms of GL-resolution) to actual 129 | ;; canvas-element width/height (or else you'll see blurry results) 130 | (set! (.-width canvas-node) width) 131 | (set! (.-height canvas-node) height) 132 | ;; Setup GL Canvas 133 | (.viewport gl 0 0 width height))) 134 | 135 | (defn app-state [width height] 136 | {:last-rendered 0 137 | :scene {:mv (mat/matrix44) 138 | :p (get-perspective-matrix width height)}}) 139 | 140 | (defn get-data [p mv vertices normals texture texture-coords point-lighting-location] 141 | (let [now (/ (.getTime (js/Date.)) 1000) 142 | use-lighting? true] 143 | {u-p-matrix p 144 | u-mv-matrix mv 145 | u-n-matrix (get-normal-matrix mv) 146 | u-ambient-color [0.2 0.2 0.2] 147 | u-alpha 1 148 | u-lighting-direction [-0.25 0.25 1] 149 | u-directional-color [0.8 0.8 0.8] 150 | u-point-lighting-location point-lighting-location 151 | u-point-lighting-diffuse-color [0.8 0.8 0.8] 152 | u-point-lighting-specular-color [0.8 0.8 0.8] 153 | u-material-shininess 32 154 | u-sampler texture 155 | u-use-lighting use-lighting? 156 | a-position vertices 157 | a-texture-coord texture-coords 158 | a-vertex-normal normals})) 159 | 160 | (defn draw-fn [gl driver programs] 161 | (fn [state] 162 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 163 | (let [{:keys [p mv 164 | texture model 165 | rotation]} (:scene state) 166 | now (/ (.getTime (js/Date.)) 1000) 167 | rotation (* js/Math.PI (js/Math.sin now)) 168 | program (get programs :specular) 169 | point-lighting-location {:data #js[-10 4 -20] 170 | :immutable? false} 171 | model-mv (-> mv 172 | (geom/translate [-4 0 -40]) 173 | (geom/* (-> (mat/matrix44) 174 | (geom/rotate-around-axis [1 0 0] 0.7719505037767067) 175 | (geom/rotate-around-axis [0 1 0] rotation) 176 | ))) 177 | model-scene (-> (get-data p model-mv (:vertices model) (:normals model) texture (:texture-coords model) point-lighting-location) 178 | (select-keys (:inputs program)) 179 | (assoc {:tag :element-index} (:indices model)))] 180 | (gd/draw-elements driver 181 | (gd/bind driver program model-scene) 182 | {:draw-mode :triangles 183 | :first 0 184 | :count (get-in model [:indices :count])})))) 185 | 186 | (def manual-step-frame-by-frame? 187 | (do 188 | true 189 | false 190 | )) 191 | 192 | (defn animate [draw-fn step-fn current-value] 193 | (js/requestAnimationFrame 194 | (fn [time] 195 | (let [next-value (step-fn time current-value)] 196 | (draw-fn next-value) 197 | (if manual-step-frame-by-frame? 198 | (set! (.-tick js/window) 199 | #(animate draw-fn step-fn next-value)) 200 | (animate draw-fn step-fn next-value)))))) 201 | 202 | (defn tick 203 | "Takes the old world value and produces a new world value, suitable 204 | for rendering" 205 | [time state] 206 | ;; We get the elapsed time since the last render to compensate for 207 | ;; lag, etc. 208 | (let [time-now (.getTime (js/Date.)) 209 | elapsed (- time-now (:last-rendered state)) 210 | cube-diff (/ (* 75 elapsed) 100000)] 211 | (-> state 212 | (update-in [:scene :cube-rotation] + cube-diff) 213 | (assoc-in [:last-rendered] time-now)))) 214 | 215 | (defn process-immutable-json-model [id model-json key-mapping] 216 | (let [process-key (fn [[json-key edn-key]] 217 | {edn-key {:id (keyword (str (name id) "-" (name edn-key))) 218 | :immutable? true 219 | :data (js->clj (aget model-json json-key))}}) 220 | model (reduce merge {} (map process-key key-mapping)) 221 | indices-count (when-let [data (get-in model [:indices :data])] 222 | (if-let [length (.-length data)] 223 | length 224 | (count data)))] 225 | (if indices-count 226 | (assoc-in model [:indices :count] indices-count) 227 | model))) 228 | 229 | (defn main [_ node] 230 | (let [gl (.getContext node "webgl") 231 | width (.-clientWidth node) 232 | height (.-clientHeight node) 233 | driver (driver/basic-driver gl) 234 | programs {:specular (gd/program driver program-specular)} 235 | state (time (app-state width height))] 236 | (reset-gl-canvas! node) 237 | (.enable gl (.-DEPTH_TEST gl)) 238 | (.clearColor gl 0 0 0 1) 239 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 240 | (go 241 | (let [texture-loaded-ch (chan) 242 | texture-source "/images/metal.jpg" 243 | _ (utils/load-texture! texture-source #(put! texture-loaded-ch %)) 244 | model-loaded-ch (chan) 245 | model-source "/models/teapot.json" 246 | _ (utils/http-get model-source #(put! model-loaded-ch %)) 247 | model-json (js/JSON.parse ( state 259 | (assoc-in [:scene :texture] texture) 260 | (assoc-in [:scene :model] model))))] 261 | (if manual-step-frame-by-frame? 262 | (set! (.-tick js/window) next-tick) 263 | (next-tick)))))) 264 | 265 | (def explanation 266 | nil) 267 | 268 | (def summary 269 | {:title title 270 | :enter main 271 | :explanation explanation}) 272 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/lesson_16.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-always gampg.learn-gamma.lesson-16 2 | (:require [cljs.core.async :as async :refer [js ks)))] 36 | (merge defaults initial))) 37 | 38 | (defn app-state [width height] 39 | {:last-rendered 0 40 | :scene {:mv (mat/matrix44) 41 | :p (utils/get-perspective-matrix width height) 42 | :square-vertices {:data [[ 1 1 0] 43 | [-1 1 0] 44 | [ 1 -1 0] 45 | [-1 -1 0]] 46 | :immutable? true 47 | :id :square-vertices} 48 | :square-colors {:data [[1 0 0 1] 49 | [0 1 0 1] 50 | [0 0 1 1] 51 | [1 1 1 1]] 52 | :immutable? true 53 | :id :square-colors}} 54 | :canvas {:width width 55 | :height height}}) 56 | 57 | (def laptop-screen 58 | {:vertices {:data [ 0.580687 0.659 0.813106 59 | -0.580687 0.659 0.813107 60 | 0.580687 0.472 0.113121 61 | -0.580687 0.472 0.113121] 62 | :immutable? true 63 | :id :laptop-screen-vertices} 64 | :normals {:data [0.000000 -0.965926 0.258819 65 | 0.000000 -0.965926 0.258819 66 | 0.000000 -0.965926 0.258819 67 | 0.000000 -0.965926 0.258819] 68 | :immutable? true 69 | :id :laptop-screen-normals} 70 | :texture-coords {:data [1.0 1.0 71 | 0.0 1.0 72 | 1.0 0.0 73 | 0.0 0.0] 74 | :immutable? true 75 | :id :laptop-screen-texture-coords}}) 76 | 77 | (defn get-data [p mv vertices normals color-texture texture-coords point-lighting-location diffuse-color emissive-color ambient-color] 78 | (let [now (/ (.getTime (js/Date.)) 1000)] 79 | {progs/u-p-matrix p 80 | progs/u-mv-matrix mv 81 | progs/u-n-matrix (utils/get-normal-matrix mv) 82 | progs/u-ambient-lighting-color [0.8 0.8 0.8] 83 | progs/u-point-lighting-location point-lighting-location 84 | progs/u-point-lighting-diffuse-color [0.8 0.8 0.8] 85 | progs/u-point-lighting-specular-color [0.8 0.8 0.8] 86 | progs/u-material-ambient-color ambient-color 87 | progs/u-material-diffuse-color diffuse-color 88 | progs/u-material-emissive-color emissive-color 89 | progs/u-material-shininess 32 90 | progs/u-sampler color-texture 91 | progs/a-position vertices 92 | progs/a-texture-coord texture-coords 93 | progs/a-vertex-normal normals})) 94 | 95 | (defn draw-fn [gl driver programs] 96 | (fn [state] 97 | (let [{:keys [p mv 98 | color-texture specular-texture 99 | model 100 | rotation]} (:scene state) 101 | now (/ (.getTime (js/Date.)) 50) 102 | rotation (- (utils/deg->rad now)) 103 | program (get programs :simple) 104 | point-lighting-location {:data #js[-10 4 0] 105 | :immutable? false} 106 | square-mv (-> mv 107 | ;;(geom/translate [(* rotation 4) -0.5 -5]) 108 | (geom/translate [(* (js/Math.sin (/ now 8)) 4) (* (js/Math.cos (/ now 3)) 2) -5]) 109 | (geom/* (-> (mat/matrix44) 110 | (geom/rotate-around-axis [0 1 0] rotation) 111 | (geom/rotate-around-axis [1 0 0] (- (/ js/Math.PI 3))))) 112 | object-array) 113 | model-mv (-> mv 114 | (geom/translate [0 -0.5 -2.5]) 115 | (geom/* (-> M44 116 | (geom/rotate-around-axis [0 1 0] rotation) 117 | (geom/rotate-around-axis [1 0 0] (- (/ js/Math.PI 2)))))) 118 | scene-data (-> (get-data p model-mv (:vertices model) (:normals model) color-texture (:texture-coords model) point-lighting-location #js[0.2 0.2 0.2] #js[0.2 0.2 0.2] #js[0.2 0.2 0.2]) 119 | (select-keys (get-in programs [:specular :inputs])) 120 | (assoc {:tag :element-index} (:indices model))) 121 | screen-data (-> (get-data p model-mv (:vertices laptop-screen) (:normals laptop-screen) (get-in state [:framebuffer :color]) 122 | (:texture-coords laptop-screen) point-lighting-location #js[0.2 0.2 0.2] #js[0.2 0.2 0.2] #js[0.2 0.2 0.2]) 123 | (select-keys (get-in programs [:specular :inputs])) 124 | (assoc {:tag :element-index} (:indices model))) 125 | screen-texture-data {progs/u-p-matrix p 126 | progs/u-mv-matrix square-mv 127 | progs/a-position (get-in state [:scene :square-vertices]) 128 | progs/a-color (get-in state [:scene :square-colors])}] 129 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 130 | ;; First draw to the framebuffer 131 | (.viewport gl 0 0 512 512) 132 | (.bindFramebuffer gl ggl/FRAMEBUFFER (get-in state [:framebuffer :frame-buffer])) 133 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 134 | (skybox/draw-skybox driver (get-in programs [:skybox]) p (-> M44 135 | (geom/rotate-around-axis [1 0 0] (js/Math.cos (/ now 10))) 136 | (geom/rotate-around-axis [0 1 0] (js/Math.sin (/ now 100))) 137 | ) (get-in state [:skybox :texture]) (:framebuffer state)) 138 | (.clear gl (.-DEPTH_BUFFER_BIT gl)) 139 | (gd/bind driver (get programs :simple) screen-texture-data) 140 | (gd/draw-arrays driver (get programs :simple) {:draw-mode :triangle-strip 141 | :count 4} (:framebuffer state)) 142 | #_(gd/bind driver (get programs :specular) (update-in scene-data [progs/u-mv-matrix] 143 | #(geom/rotate-around-axis % [0 1 0] rotation))) 144 | #_(gd/draw-elements driver (get programs :specular) 145 | {:draw-mode :triangles 146 | :first 0 147 | :count (get-in model [:indices :count])} (:framebuffer state)) 148 | 149 | (.viewport gl 0 0 (get-in state [:canvas :width]) (get-in state [:canvas :height])) 150 | (.bindTexture gl ggl/TEXTURE_2D (get-in state [:framebuffer :color :texture])) 151 | (gd/bind driver (get programs :specular) screen-data) 152 | (gd/draw-arrays driver (get programs :specular) {:draw-mode :triangle-strip 153 | :count 4}) 154 | (gd/draw-elements driver (gd/bind driver (get programs :specular) scene-data) {:draw-mode :triangles 155 | :first 0 156 | :count (get-in model [:indices :count])})))) 157 | 158 | (defn animate [draw-fn step-fn state] 159 | (js/requestAnimationFrame 160 | (fn [time] 161 | (let [next-value (swap! state step-fn time)] 162 | (draw-fn next-value) 163 | (if (:manual-tick? initial-query-map) 164 | (set! (.-tick js/window) 165 | #(animate draw-fn step-fn state)) 166 | (animate draw-fn step-fn state)))))) 167 | 168 | (defn tick 169 | "Takes the old world value and produces a new world value, suitable 170 | for rendering" 171 | [state time] 172 | ;; We get the elapsed time since the last render to compensate for 173 | ;; lag, etc. 174 | (let [time-now (.getTime (js/Date.)) 175 | elapsed (- time-now (:last-rendered state)) 176 | cube-diff (/ (* 75 elapsed) 100000)] 177 | (-> state 178 | (update-in [:scene :cube-rotation] + cube-diff) 179 | (assoc-in [:last-rendered] time-now)))) 180 | 181 | (defn main [global-app-state node] 182 | (let [gl (.getContext node "webgl") 183 | gl-extensions [(.getExtension gl "WEBGL_depth_texture")] 184 | width (.-clientWidth node) 185 | height (.-clientHeight node) 186 | driver (utils/make-driver gl) 187 | programs {:specular (gd/program driver progs/program-specular) 188 | :simple (gd/program driver lesson-02/program-source) 189 | :skybox (gd/program driver skybox/program-skybox)} 190 | ;; WxH must be a power of two (e.g. 64, 128, 256, 512, 1024, etc.) 191 | fb-width 512 192 | fb-height 512 193 | fb (utils/make-frame-buffer driver fb-width fb-height) 194 | local-state (-> (app-state width height) 195 | (assoc :framebuffer fb)) 196 | _ (swap! global-app-state merge local-state) 197 | state global-app-state] 198 | (utils/reset-gl-canvas! node) 199 | (.enable gl (.-DEPTH_TEST gl)) 200 | (.clearColor gl 0 0 0 1) 201 | (.clear gl (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl))) 202 | (go 203 | (let [texture-loaded-ch (chan) 204 | color-texture-source "/images/earth.jpg" 205 | _ (utils/load-texture! color-texture-source #(put! texture-loaded-ch %)) 206 | model-loaded-ch (chan) 207 | model-source "/models/laptop.json" 208 | _ (utils/http-get model-source #(put! model-loaded-ch %)) 209 | model-json (js/JSON.parse ( state 232 | (assoc-in [:scene :color-texture] color-texture) 233 | (assoc-in [:scene :model] model) 234 | (assoc-in [:skybox :texture] (assoc skybox 235 | :immutable? true 236 | :texture-id 3)) 237 | (assoc-in [:webgl :extensions] gl-extensions)))) 238 | next-tick (fn [] (animate (draw-fn gl driver programs) tick state))] 239 | ;; Wait 100ms, and then fix the WebGL inspector if it's there. 240 | (js ks))) 30 | special {:skybox-name (when-let [skybox-name (second (utils/uri-param parsed-uri "skybox-name" "sky1.png"))] 31 | (vec (string/split skybox-name ".")))}] 32 | (merge defaults initial special))) 33 | 34 | (def half-pi 35 | (/ js/Math.PI 2)) 36 | 37 | (defn handle-mouse-move [cast! event] 38 | (cast! :mouse-moved [(.. event -pageX) 39 | (.. event -pageY)] true)) 40 | 41 | (defn handle-mouse-down [cast! event] 42 | (cast! :mouse-depressed [(.. event -pageX) 43 | (.. event -pageY)] false)) 44 | 45 | (defn handle-mouse-up [cast! event] 46 | (cast! :mouse-released [(.. event -pageX) 47 | (.. event -pageY)] false)) 48 | 49 | (defn disable-mouse-wheel [event] 50 | (.stopPropagation event)) 51 | 52 | (def u-sky-box-p-matrix 53 | (g/uniform "uSkyBoxPositionMatrix" :mat4)) 54 | 55 | (def u-sky-box-mv-matrix 56 | (g/uniform "uSkyBoxMVMatrix" :mat4)) 57 | 58 | (def u-sky-box-inverse-mv-matrix 59 | (g/uniform "uSkyBoxInverseMVMatrix" :mat4)) 60 | 61 | (def a-sky-box-position 62 | (g/attribute "position" :vec3)) 63 | 64 | (def a-sky-box-texture-coord 65 | (g/attribute "aSkyBoxTextureCoord" :vec3)) 66 | 67 | (def v-sky-box-texture-coord 68 | (g/varying "vSkyBoxTextureCoord" :vec3 :highp)) 69 | 70 | (def u-sky-box-sampler 71 | (g/uniform "skybox" :samplerCube)) 72 | 73 | (def u-m4-matrix 74 | (g/uniform "um4_matrix" :mat4)) 75 | 76 | (def v-sky-box-position 77 | (g/varying "v_position" :vec4 :mediump)) 78 | 79 | (def program-sky-box 80 | (p/program 81 | {:id :lesson-20-sky-box 82 | :vertex-shader {(g/gl-position) (g/* u-sky-box-p-matrix (g/* u-sky-box-mv-matrix (g/vec4 a-sky-box-position 1))) 83 | v-sky-box-texture-coord a-sky-box-texture-coord} 84 | :fragment-shader {(g/gl-frag-color) (g/textureCube u-sky-box-sampler v-sky-box-texture-coord)} 85 | :precision {:float :mediump}})) 86 | 87 | (defn app-state [width height gl node now] 88 | {:last-rendered 0 89 | :mouse {:pos [0 0] 90 | :sensitivity 0.01} 91 | :scene {:rotation 0 92 | :mv (mat/matrix44) 93 | :p (utils/get-perspective-matrix 45 width height) 94 | :sky-box {} 95 | :camera {:pitch -0.039203673205104164, :yaw -5.3000000000000105, :x 3.7236995248610296, :y 1, :z -1.7775460356400952}} 96 | :webgl {:canvas {:node node 97 | :gl gl}}}) 98 | 99 | (def sky-box-vertices 100 | {:data [-1 1 1 101 | 1 1 1 102 | 1 -1 1 103 | -1 -1 1 104 | -1 1 -1 105 | 1 1 -1 106 | 1 -1 -1 107 | -1 -1 -1] 108 | :id :sky-box-vertices 109 | :immutable? true}) 110 | 111 | (def sky-box-indices 112 | {:data [3,2,0,0,2,1,2,6,1,1,6,5,0,1,4,4,1,5,5,6,4,6,7,4,4,7,0,7,3,0,6,2,7,2,3,7] 113 | :id :sky-box-indices 114 | :immutable? true}) 115 | 116 | (defn draw-sky-box [driver program state p sky-box pitch yaw] 117 | (let [gl (gd/gl driver) 118 | data (-> (select-keys {u-sky-box-p-matrix p 119 | u-sky-box-sampler sky-box 120 | a-sky-box-position sky-box-vertices 121 | a-sky-box-texture-coord sky-box-vertices 122 | u-sky-box-mv-matrix {:data (-> M44 123 | (geom/rotate-x pitch) 124 | (geom/rotate-y yaw))}} 125 | (:inputs program)) 126 | (assoc {:tag :element-index} sky-box-indices))] 127 | (gd/draw-elements driver (gd/bind driver program data) 128 | {:draw-mode :triangles 129 | :count 36}))) 130 | 131 | (defn draw-fn [driver state] 132 | (let [gl (:gl driver) 133 | now (/ (:now state) 1000) 134 | rot (* js/Math.PI (js/Math.sin now)) 135 | {:keys 136 | [p mv]} (:scene state) 137 | mouse-x (get-in state [:mouse 0] 0) 138 | mouse-y (get-in state [:mouse 1] 0) 139 | camera (get-in state [:scene :camera])] 140 | ;; First draw your skybox 141 | (draw-sky-box driver (get-in state [:runtime :programs :sky-box]) 142 | state p (get-in state [:skybox :texture]) 143 | (- (:pitch camera)) (- (:yaw camera))) 144 | ;; Clear the depth buffer so that other things can be drawn on top 145 | ;; of our sky. 146 | (.clear gl (.-DEPTH_BUFFER_BIT gl)) 147 | ;; Now draw the rest of the scene 148 | )) 149 | 150 | (defn animate-pure [draw-fn step-fn current-value] 151 | (js/requestAnimationFrame 152 | (fn [time] 153 | (let [next-value (step-fn time current-value)] 154 | (draw-fn next-value) 155 | (if (:manual-tick? initial-query-map) 156 | (set! (.-tick js/window) 157 | #(animate-pure draw-fn step-fn next-value)) 158 | (animate-pure draw-fn step-fn next-value)))))) 159 | 160 | (defn tick 161 | "Takes the old world value and produces a new world value, suitable 162 | for rendering" 163 | [state] 164 | ;; We get the elapsed time since the last render to compensate for 165 | ;; lag, etc. 166 | (let [time-now (.getTime (js/Date.)) 167 | elapsed (- time-now (:last-rendered state)) 168 | pos (when-let [sensor (get-in state [:hmds :pos])] 169 | (.call (aget sensor "getState") sensor)) 170 | camera (get-in state [:scene :camera])] 171 | (-> state 172 | (assoc-in [:last-rendered] time-now) 173 | (assoc :now time-now)))) 174 | 175 | (defmulti control-event 176 | (fn [message args state] message)) 177 | 178 | (defmethod control-event :default 179 | [message args state] 180 | ;;(print "Unhandled message in controls: " message) 181 | state) 182 | 183 | (defmethod control-event :key-state-changed 184 | [message [{:keys [key-name-kw depressed?]}] state] 185 | (when-let [capture (and (= key-name-kw :c?) (.-captureNextFrame js/window))] 186 | (.call capture)) 187 | (assoc-in state [:keyboard key-name-kw] depressed?)) 188 | 189 | (defmethod control-event :mouse-moved 190 | [message [new-screen-x new-screen-y] state] 191 | (let [[old-screen-x 192 | old-screen-y] (get-in state [:mouse :pos]) 193 | sensitivity (get-in state [:mouse :sensitivity]) 194 | dx (* sensitivity (- old-screen-x new-screen-x)) 195 | dy (* sensitivity (- old-screen-y new-screen-y)) 196 | new-pos [new-screen-x new-screen-y] 197 | next-state (-> state 198 | (update-in [:scene :camera :yaw] + dx) 199 | (update-in [:scene :camera :pitch] (fn [pitch] 200 | (-> (+ pitch dy) 201 | (min half-pi) 202 | (max (- half-pi))))) 203 | (assoc-in [:mouse :pos] new-pos))] 204 | next-state)) 205 | 206 | (defn post-control-event! [msg data previous-state new-state] 207 | nil) 208 | 209 | (defn render-loop [draw-fn state-atom stop-ch] 210 | (js/requestAnimationFrame 211 | (fn [time] 212 | (let [state @state-atom] 213 | (draw-fn (get-in state [:runtime :driver]) state) 214 | (when-let [ext (.getExtension (get-in state [:runtime :driver :gl]) "GLI_frame_terminator")] 215 | ;; Useful for WebGL inspector until we have Gamma-Inspector 216 | (.frameTerminator ext))) 217 | (if (:manual-tick? initial-query-map) 218 | (set! (.-tick js/window) 219 | #(render-loop draw-fn state-atom stop-ch)) 220 | (render-loop draw-fn state-atom stop-ch))))) 221 | 222 | (defn main* [app-state opts] 223 | (let [histories (get opts :histories (atom {})) 224 | [controls-ch 225 | stop-ch 226 | keyboard-ch] [(get-in @app-state [:comms :controls]) 227 | (get-in @app-state [:comms :stop]) 228 | (get-in @app-state [:comms :keyboard])] 229 | cast! (fn [message data & [elide-from-history?]] 230 | (async/put! controls-ch [message data elide-from-history?])) 231 | handle-mouse-move! #(handle-mouse-move cast! %) 232 | handle-canvas-mouse-down #(handle-mouse-down cast! %) 233 | handle-canvas-mouse-up #(handle-mouse-up cast! %) 234 | handle-close! #(cast! :application-shutdown [@histories])] 235 | (js/document.addEventListener "mousemove" handle-mouse-move! false) 236 | (js/window.addEventListener "beforeunload" handle-close!) 237 | (def as app-state) 238 | (set! (.-state js/window) app-state) 239 | (render-loop draw-fn app-state nil) 240 | (async/go 241 | (loop [] 242 | (async/alt! 243 | controls-ch ([[msg data transient?]] 244 | ;;(print "Controls Message: " (pr-str msg) " -> " (pr-str data)) 245 | (let [previous-state @app-state] 246 | (swap! app-state 247 | (fn [state] 248 | (tick (control-event msg data state)))) 249 | (post-control-event! msg data previous-state @app-state))) 250 | ;; XXX: Should probably remove this for replay needs 251 | (async/timeout 15) ([] 252 | (swap! app-state tick))) 253 | (recur))))) 254 | 255 | (defn main [global-app-state node] 256 | (let [stop (async/chan) 257 | controls (async/chan) 258 | keyboard (async/chan) 259 | gl (.getContext node "webgl" #js {:antialias true}) 260 | width (.-clientWidth node) 261 | height (.-clientHeight node) 262 | driver (utils/make-driver gl) 263 | programs {:sky-box (gd/program driver program-sky-box)} 264 | now (.getTime (js/Date.)) 265 | watch-key (gensym)] 266 | (set! (.-glHandle js/window) gl) 267 | (utils/reset-gl-canvas! node) 268 | (doto gl 269 | (.enable (.-DEPTH_TEST gl)) 270 | (.clearColor 0 0 0 1) 271 | (.clear (bit-or (.-COLOR_BUFFER_BIT gl) (.-DEPTH_BUFFER_BIT gl)))) 272 | (go 273 | (let [fix-webgl-ch ( (app-state width height gl node now) 277 | (update-in [:comms] merge {:controls controls 278 | :stop stop 279 | :keyboard keyboard}) 280 | (assoc-in [:runtime :programs] programs) 281 | (assoc-in [:runtime :driver] driver) 282 | (assoc-in [:debug] initial-query-map)) 283 | _ (utils/load-cube-map gl (str "/images/skybox/" (get-in initial-query-map [:skybox-name 0])) 284 | (get-in initial-query-map [:skybox-name 1]) 285 | {:min :linear 286 | :mag :linear} 287 | {:s :clamp-to-edge 288 | :t :clamp-to-edge} 289 | #(put! texture-ch %)) 290 | skybox ( app-state 292 | (assoc-in [:skybox :texture] (assoc skybox 293 | :immutable? true 294 | :texture-id 1))) 295 | _ (swap! global-app-state merge app-state) 296 | app-state global-app-state 297 | next-tick (fn [] 298 | (main* app-state {:comms {:stop stop 299 | :controls controls 300 | :keyboard keyboard}}))] 301 | ;; Give the inspector 100ms to load if it's there, then try to fix it. 302 | ( u-p-matrix 82 | (g/* u-mv-matrix) 83 | (g/* (g/vec4 a-position 1)))} 84 | :fragment-shader {(g/gl-frag-color) u-color} 85 | :precision {:float :mediump}})) 86 | 87 | (defn draw-black [driver draw program p mv vertices opts] 88 | (draw driver (gd/bind driver program {u-p-matrix p 89 | u-mv-matrix mv 90 | a-position vertices 91 | u-color #js[0 0 0 1]}) opts)) 92 | 93 | (defn draw-white [driver draw program p mv vertices opts] 94 | (draw driver (gd/bind driver program {u-p-matrix p 95 | u-mv-matrix mv 96 | a-position vertices 97 | u-color #js[1 1 1 1]}) opts)) 98 | 99 | (def flat-color 100 | (p/program 101 | {:id :common-flat-color 102 | :vertex-shader {(g/gl-position) (-> u-p-matrix 103 | (g/* u-mv-matrix) 104 | (g/* (g/vec4 a-position 1))) 105 | v-color a-color} 106 | :fragment-shader {(g/gl-frag-color) v-color}})) 107 | 108 | (def simple-color 109 | (p/program 110 | {:id :common-simple-color 111 | :vertex-shader {(g/gl-position) (-> u-p-matrix 112 | (g/* u-mv-matrix) 113 | (g/* (g/vec4 a-position 1))) 114 | v-color a-color} 115 | :fragment-shader {(g/gl-frag-color) v-color}})) 116 | 117 | (def simple-texture 118 | (p/program 119 | {:vertex-shader {(g/gl-position) (-> u-p-matrix 120 | (g/* u-mv-matrix) 121 | (g/* (g/vec4 a-position 1))) 122 | v-texture-coord a-texture-coord} 123 | :fragment-shader {(g/gl-frag-color) 124 | (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st)))}})) 125 | 126 | (defn draw-color [driver draw program p mv vertices colors opts] 127 | (draw driver (gd/bind driver program {u-p-matrix p 128 | u-mv-matrix mv 129 | a-position vertices 130 | a-color colors}) opts)) 131 | 132 | (def program-specular 133 | (p/program 134 | {:id :specular 135 | :vertex-shader {v-texture-coord a-texture-coord 136 | v-transformed-normal (g/* u-n-matrix a-vertex-normal) 137 | v-position (g/* u-mv-matrix (g/vec4 a-position 1)) 138 | (g/gl-position) (g/* u-p-matrix v-position)} 139 | :fragment-shader (let [ambient-light-weighting u-ambient-lighting-color 140 | light-direction (g/normalize (g/- u-point-lighting-location (g/swizzle v-position :xyz))) 141 | normal (g/normalize v-transformed-normal) 142 | eye-direction (-> (g/swizzle v-position :xyz) 143 | (g/* -1) 144 | (g/normalize)) 145 | reflection-direction (-> (g/* -1 light-direction) 146 | (g/reflect normal)) 147 | specular-light-brightness (-> (g/dot reflection-direction eye-direction) 148 | (g/max 0) 149 | (g/pow u-material-shininess)) 150 | specular-light-weighting (g/* u-point-lighting-diffuse-color specular-light-brightness) 151 | diffuse-light-brightness (-> (g/dot normal light-direction) 152 | (g/max 0)) 153 | diffuse-light-weighting (g/* u-point-lighting-diffuse-color diffuse-light-brightness) 154 | texture-color (g/texture2D u-sampler (g/swizzle v-texture-coord :st)) 155 | texture-rgb (g/swizzle texture-color :rgb) 156 | alpha (g/swizzle texture-color :a) 157 | material-ambient-color (g/* u-material-ambient-color texture-rgb) 158 | material-diffuse-color (g/* u-material-diffuse-color texture-rgb) 159 | material-specular-color (g/* u-material-specular-color texture-rgb) 160 | material-emissive-color (g/* u-material-emissive-color texture-rgb) 161 | sum-color (-> (g/* material-ambient-color ambient-light-weighting) 162 | (g/+ (g/* material-diffuse-color diffuse-light-weighting)) 163 | (g/+ (g/* material-specular-color specular-light-weighting)) 164 | (g/+ material-emissive-color) 165 | (g/vec4 alpha))] 166 | {(g/gl-frag-color) sum-color}) 167 | :precision {:float :mediump}})) 168 | 169 | (def u-spot-position 170 | (g/uniform "uLightPosition" :vec3)) 171 | 172 | (def u-shadow-map 173 | (g/uniform "uShadowMapSampler" :sampler2D)) 174 | 175 | (def depth-scale-matrix 176 | (g/mat4 0.5 0.0 0.0 0.0 0.0 0.5 0.0 0.0 0.0 0.0 0.5 0.0 0.5 0.5 0.5 1.0)) 177 | 178 | (def u-spot-color 179 | (g/uniform "uLightSpotColor" :vec3)) 180 | 181 | (def u-spot-direction 182 | (g/uniform "uLightSpotDirection" :vec3)) 183 | 184 | (def u-spot-inner-angle 185 | (g/uniform "uLightSpotInnerAngle" :float)) 186 | 187 | (def u-spot-outer-angle 188 | (g/uniform "uLightSpotOuterAngle" :float)) 189 | 190 | (def u-spot-radius 191 | (g/uniform "uLightSpotRadius" :float)) 192 | 193 | (def v-light-to-point 194 | (g/varying "vLightToPoint" :vec3 :mediump)) 195 | 196 | (def v-eye-to-point 197 | (g/varying "vEyeToPoint" :vec3 :mediump)) 198 | 199 | (def v-shadow-position 200 | (g/varying "vShadowPosition" :vec4 :mediump)) 201 | 202 | (def v-normal 203 | (g/varying "vNormal" :vec3 :mediump)) 204 | 205 | (defn compute-light [normal specular-level light-to-point eye-to-point 206 | spot-color spot-radius spot-direction spot-inner-angle spot-outer-angle] 207 | (let [;; Lambert term 208 | l (g/normalize light-to-point) 209 | n (g/normalize normal) 210 | lambert-term (-> (g/dot n l) 211 | (g/max 0)) 212 | 213 | ;; Light attenuation 214 | light-dist (g/length light-to-point) 215 | d (-> (g/div light-dist spot-radius) 216 | (g/max 0) 217 | (g/div (g/+ spot-radius 1))) 218 | dist-attn (g/div 1 (g/* d d)) 219 | 220 | ;; Spot attenuation 221 | sd (g/normalize spot-direction) 222 | spot-angle-delta (g/- spot-inner-angle spot-outer-angle) 223 | spot-angle (g/dot (g/* l -1) sd) 224 | spot-attn (-> (g/- spot-angle spot-outer-angle) 225 | (g/div spot-angle-delta) 226 | (g/clamp 0 1)) 227 | light-value (-> spot-color 228 | (g/* lambert-term) 229 | (g/* dist-attn) 230 | (g/* spot-attn)) 231 | 232 | ;; ;; Specular 233 | e (g/normalize eye-to-point) 234 | r (g/reflect (g/* l -1) n) 235 | shininess 8 ;; Why 8? 236 | specular-factor (-> (g/dot r e) 237 | (g/clamp 0 1) 238 | (g/pow shininess) 239 | (g/* specular-level)) 240 | light-value (g/if (g/< lambert-term 0) 241 | (g/vec3 0 0 0) 242 | (g/+ light-value (g/* spot-color specular-factor)))] 243 | light-value)) 244 | 245 | (defn compute-shadow [shadow-position shadow-map] 246 | (let [depth (g/div (g/swizzle shadow-position :xyz) 247 | (g/swizzle shadow-position :w)) 248 | depth-z (g/* (g/swizzle depth :z) 249 | 0.999) 250 | shadow-coords (g/swizzle depth :xy) 251 | shadow-value (-> shadow-map 252 | (g/texture2D shadow-coords) 253 | (g/swizzle :r))] 254 | (g/if (g/< shadow-value depth-z) 255 | 0 256 | 1))) 257 | 258 | (def u-light-projection-matrix 259 | (g/uniform "uLightProjectionMatrix" :mat4)) 260 | 261 | (def u-light-view-matrix 262 | (g/uniform "uLightViewMatrix" :mat4)) 263 | 264 | (def a-normal 265 | (g/attribute "aNormal" :vec3)) 266 | 267 | (def program-specular-with-shadow-map 268 | (let [world-position (g/* u-mv-matrix (g/vec4 a-position 1))] 269 | (p/program 270 | {:id :specular-with-shadow-map 271 | :vertex-shader {v-texture-coord a-texture-coord 272 | ;; Looks like probably don't need this 273 | ;;v-transformed-normal (g/* u-n-matrix a-vertex-normal) 274 | ;;v-transformed-normal (g/* a-vertex-normal u-n-matrix) 275 | v-position (g/* u-mv-matrix (g/vec4 a-position 1)) 276 | v-light-to-point (g/- u-spot-position (g/swizzle world-position :xyz)) 277 | v-eye-to-point (g/* -1 (g/swizzle world-position :xyz)) 278 | v-shadow-position (-> depth-scale-matrix 279 | (g/* u-light-projection-matrix) 280 | (g/* u-light-view-matrix) 281 | (g/* world-position)) 282 | (g/gl-position) (g/* u-p-matrix v-position) 283 | v-normal (g/* a-normal u-n-matrix)} 284 | :fragment-shader (let [light-value (compute-light v-normal 0.5 285 | v-light-to-point v-eye-to-point 286 | u-spot-color u-spot-radius u-spot-direction u-spot-inner-angle u-spot-outer-angle) 287 | shadow-value (compute-shadow v-shadow-position u-shadow-map) 288 | diffuse-color (g/texture2D u-sampler (g/swizzle v-texture-coord :st)) 289 | final-color (-> (g/swizzle diffuse-color :rgb) 290 | (g/* light-value) 291 | (g/* shadow-value))] 292 | {(g/gl-frag-color) (do (g/vec4 final-color (g/swizzle diffuse-color :a)) 293 | (g/vec4 (g/* (g/swizzle diffuse-color :rgb) shadow-value) 1))}) 294 | :precision {:float :mediump}}))) 295 | 296 | (defn draw-specular-with-shadow-map [driver draw program p mv normal-matrix vertices normals 297 | spot-location spot-direction 298 | spot-inner-angle spot-outer-angle 299 | spot-radius spot-color 300 | texture-coords 301 | color-texture shadow-map 302 | indices draw-mode fst draw-count] 303 | (let [angle (* 2 spot-outer-angle (/ 180 js/Math.PI)) 304 | light-projection-matrix (mat/perspective angle 1 1 256) 305 | light-view-matrix (mat/look-at spot-location spot-direction (vec3 0 0 1))] 306 | (when (< (js/Math.random) 0) 307 | (js/console.log (pr-str (:id vertices)) " vertec count: " (count (:data vertices))) 308 | (js/console.log (pr-str (:id normals)) " normal count: " (count (:data normals))) 309 | (js/console.log (pr-str (:id texture-coords)) " texture-coords count: " (count (:data texture-coords))) 310 | (js/console.log (pr-str (:id indices)) " texture-coords count: " (count (:data indices)) draw-count) 311 | (js/console.log "Spotlight from " (clj->js spot-location) " looking at " (clj->js spot-direction) " radius: " spot-radius)) 312 | (gd/bind driver program 313 | (-> {u-p-matrix p 314 | u-mv-matrix mv 315 | u-n-matrix normal-matrix 316 | u-spot-position spot-location 317 | u-shadow-map shadow-map 318 | a-position vertices 319 | a-texture-coord texture-coords 320 | u-sampler color-texture 321 | u-spot-outer-angle spot-inner-angle 322 | u-spot-inner-angle spot-outer-angle 323 | u-spot-direction spot-direction 324 | u-spot-color spot-color 325 | u-spot-radius spot-radius 326 | a-normal normals 327 | u-light-view-matrix light-view-matrix 328 | u-light-projection-matrix light-projection-matrix} 329 | (select-keys (:inputs program)) 330 | (assoc {:tag :element-index} indices)))) 331 | (draw driver program {:draw-mode draw-mode 332 | :first fst 333 | :count draw-count})) 334 | -------------------------------------------------------------------------------- /src/cljs/gampg/learn_gamma/shadows_notes.txt: -------------------------------------------------------------------------------- 1 | From #webgl on freenode 2 | 3 | <__doc__ (pyalot from http://codeflow.org/)> seangrove: 4 | 5 | Way I currently use it is to 1) cast a shadow 6 | from a random direction (halton sequence for even distribution, 7 | importance sampled to emphasize important directions) 2) accumulate 8 | radiance modified by a geometric occlusion term (lambert or ggx) and 9 | by the shadow compare into a floating point accumulation texture 10 | 11 | For the importance sampling case, colors are normalized 12 | 13 | For the directionality I use a 64x64 per side preconvolved (with ggx) 14 | radiance environment map, encoded in LogLuv 15 | 16 | The importance sampling is a simple measure that takes the maximum 17 | radiance of the environment map, and at each iteration, sets a random 18 | target and then looks for a direction where the luminance is above 19 | that target 20 | 21 | If a hit isn't produced in N iterations (currently defaults to 150) it 22 | returns a random direction 23 | 24 | Also note, don't use Math.random() for anything, use ROT.RNG's 25 | getUniform 26 | 27 | And if you're using Halton sequences for search, you need to be 28 | careful to decouple hit/miss sequences because otherwise there'll be 29 | correlation patterns in the sample distribution 30 | 31 | So google search terms for interest: LogLuv, HDR environment map, low 32 | discrepancy series, halton sequence, disney ggx, lambertian 33 | reflectance, hamersley sequence, poisson disk, quasi monte-carlo 34 | methods 35 | 36 | I also do a bit of bandwidth optimization where I use all 4 channels 37 | (via colorMask) of a RGBA texture to pack a separate depth map, so 38 | that each accumulation step can do 4 accumulations in one shot 39 | 40 | 41 | And to minimize time spent iterating, I do dynamic GPU scaling, aiming 42 | for 30fps and adjusting the number of iterations done per frame based 43 | on how far away away I am from target (gauss-seidel relaxed ease in) 44 | 45 | Also if you ever get bored and don't know what stuff to google, just 46 | ask me ^^ 47 | -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=1.8 2 | -------------------------------------------------------------------------------- /test/clj/gampg/example_test.clj: -------------------------------------------------------------------------------- 1 | (ns gampg.example-test 2 | (:require [clojure.test :refer :all])) 3 | 4 | (deftest example-passing-test 5 | (is (= 1 1))) 6 | -------------------------------------------------------------------------------- /test/cljs/gampg/core-test.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.core-test 2 | (:require-macros [cljs.test :refer (is deftest testing)]) 3 | (:require [cljs.test])) 4 | 5 | (deftest example-passing-test 6 | (is (= 1 1))) 7 | -------------------------------------------------------------------------------- /test/cljs/gampg/test-runner.cljs: -------------------------------------------------------------------------------- 1 | (ns gampg.test-runner 2 | (:require 3 | [cljs.test :refer-macros [run-tests]] 4 | [gampg.core-test])) 5 | 6 | (enable-console-print!) 7 | 8 | (defn runner [] 9 | (if (cljs.test/successful? 10 | (run-tests 11 | 'gampg.core-test)) 12 | 0 13 | 1)) 14 | --------------------------------------------------------------------------------