diff --git a/ShaderPlayground/build/debug/main.js b/ShaderPlayground/build/debug/main.js
index ccb86ca..420eb97 100644
--- a/ShaderPlayground/build/debug/main.js
+++ b/ShaderPlayground/build/debug/main.js
@@ -1 +1,626 @@
-var OS;!function(e){let t,i;!function(e){let t;!function(e){class t extends e.ListViewItemTag{itemlayout(){return{el:"div",children:[{el:"img",ref:"img"},{el:"p",ref:"name"}]}}ondatachange(){const e=this.data,t=this.refs.img,i=e.path.asFileHandle().getlink();t.src=i,$(this.refs.name).text(e.name)}init(){this.closable=!0}reload(e){}}e.define("afx-shader-texture-item",t)}(t=e.tag||(e.tag={}))}(t=e.GUI||(e.GUI={})),function(e){class i extends t.BasicDialog{constructor(){super("AddTextureDialog",i.scheme)}main(){super.main();const e=$("input",this.scheme);this.find("btnOk").onbtclick=t=>{let i={};for(const t of e){let e=t;if(""==e.value.trim())return this.notify(__("All fields should be filled"));i[e.name]=e.value.trim()}this.handle&&this.handle(i),this.quit()},this.find("btnFile").onbtclick=e=>{this.openDialog("FileDialog",{title:__("Select image file"),type:"file",mimes:["image/.*"]}).then(e=>{this.find("txtPath").value=e.file.path})}}}i.scheme='\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n';class s{constructor(e,t){this.glsl_values=[s.frg_template,""],this.renderer=t,this.ums=[new ace.UndoManager,new ace.UndoManager],this.current_idx=-1,this.tmp_canvas=$("")[0],this.gl_compiling_ctx=this.tmp_canvas.getContext("webgl"),this._filehandle=void 0,this.editormux=!1,ace.require("ace/ext/language_tools"),this._onfilechange=e=>{},this._ontextureadded=e=>{},this.editor=ace.edit(e),this.editor.setOptions({enableBasicAutocompletion:!0,enableLiveAutocompletion:!0,enableSnippets:!0,highlightActiveLine:!0}),this.editor.getSession().setUseWrapMode(!0),this.editor.session.setMode("ace/mode/glsl"),this.editor.setTheme("ace/theme/monokai"),this.cursors=[this.editor.getCursorPosition(),this.editor.getCursorPosition()],this.editor.on("input",e=>{const t=this.editor.getValue(),i=0==this.current_idx?this.gl_compiling_ctx.FRAGMENT_SHADER:this.gl_compiling_ctx.VERTEX_SHADER,s=this.compile(t,i);if(!1!==this.filehandle.dirty||this.editormux||(this.filehandle.dirty=!0,this._onfilechange(this.filehandle.path+"*")),this.editormux&&(this.editormux=!1),s){const e="ERROR:\\s*([0-9]+):([0-9]+):\\s*(.*)\\n",t=s.match(new RegExp(e,"g"));t&&this.editor.getSession().setAnnotations(t.map(t=>{const i=t.match(new RegExp(e));let s={};return i&&(s={row:parseInt(i[2])-1,column:0,text:i[3],type:"error"}),s}))}else this.editor.getSession().setAnnotations([]),this.glsl_values[this.current_idx]=t,this.renderer.apply_mat(this.glsl_values[0],this.glsl_values[1])})}set onfilechange(e){this._onfilechange=e}set ontextureadded(e){this._ontextureadded=e}set filehandle(e){this._filehandle=e,this.read()}get filehandle(){return this._filehandle}read(){return new Promise(async(e,t)=>{if(void 0===this._filehandle)return this.renderer.textures.length=0,this._filehandle="Untitled".asFileHandle(),this._onfilechange(this._filehandle.path),this.glsl_values=[s.frg_template,""],2!=this.current_idx&&-1!=this.current_idx&&(this.editormux=!0,this.editor.setValue(this.glsl_values[this.current_idx])),this._ontextureadded(void 0),e(void 0);try{const t=await this._filehandle.read("json");this.glsl_values[0]=t.source[0],this.glsl_values[1]=t.source[1],2!=this.current_idx&&-1!=this.current_idx&&(this.editormux=!0,this.editor.setValue(this.glsl_values[this.current_idx])),this._ontextureadded(void 0);for(const e of t.textures)this._ontextureadded(e);this._onfilechange(this._filehandle.path),e(void 0)}catch(e){t(e)}})}write(e){return new Promise(async(t,i)=>{let s=e;const n=__("Unknown save path");if(!s){if(void 0===this._filehandle)return i(n);s=this._filehandle.path}if("Untitled"===s)return i(n);try{this._filehandle.setPath(s);const e={};2!=this.current_idx&&(this.glsl_values[this.current_idx]=this.editor.getValue()),e.source=this.glsl_values,e.textures=this.renderer.textures.map(e=>({name:e.name,path:e.path})),this.filehandle.cache=e,await this.filehandle.write("object"),this._filehandle.dirty=!1,this._onfilechange(""+this.filehandle.path),t(void 0)}catch(e){i(e)}})}compile(e,t){let i=this.gl_compiling_ctx.createShader(t);this.gl_compiling_ctx.shaderSource(i,e),this.gl_compiling_ctx.compileShader(i);let s=void 0;return this.gl_compiling_ctx.getShaderParameter(i,this.gl_compiling_ctx.COMPILE_STATUS)||(s=this.gl_compiling_ctx.getShaderInfoLog(i)),this.gl_compiling_ctx.deleteShader(i),s}edit(e){if(e<0)return;if(2!=e&&2!=this.current_idx)0===e?(this.glsl_values[1]=this.editor.getValue(),this.cursors[1]=this.editor.getCursorPosition()):1===e&&(this.glsl_values[0]=this.editor.getValue(),this.cursors[0]=this.editor.getCursorPosition());else if(2==e)return this.glsl_values[this.current_idx]=this.editor.getValue(),this.cursors[this.current_idx]=this.editor.getCursorPosition(),void(this.current_idx=e);this.current_idx=e,this.editormux=!0,this.editor.getSession().setUndoManager(new ace.UndoManager),this.editor.setValue(this.glsl_values[e]),this.editor.getSession().setUndoManager(this.ums[e]);const t=this.cursors[e];this.editor.renderer.scrollCursorIntoView({row:t.row,column:t.column},.5),this.editor.selection.moveTo(t.row,t.column),this.editor.focus()}cleanup(){this.renderer.cleanup(),$(this.tmp_canvas).remove()}resize(){this.editor.resize(),this.renderer.viewport_resize()}}class n{constructor(e){this.textures=[],this.renderer=new THREE.WebGLRenderer({canvas:e,alpha:!0}),this.renderer.autoClearColor=!1,this.clock=new THREE.Clock,this.camera=new THREE.OrthographicCamera(-1,1,1,-1,-1,1),this.needupdateTexture=!1,this.scene=new THREE.Scene;const t=new THREE.MeshBasicMaterial({color:"white"}),i=new THREE.PlaneGeometry(2,2);this.mesh=new THREE.Mesh(i,t),this.scene.add(this.mesh),this.uniforms={u_resolution:{value:{x:0,y:0}},u_time:{value:0},u_mouse:{value:{x:0,y:0}}},this.viewport_resize(),this.ani_request_id=requestAnimationFrame(()=>this.viewport_render())}viewport_render(){this.needupdateTexture&&(this.update_textures(),this.needupdateTexture=!1),this.uniforms.u_time.value=this.clock.getElapsedTime();try{this.renderer.render(this.scene,this.camera)}catch(e){console.error(e)}this.ani_request_id=requestAnimationFrame(()=>this.viewport_render())}viewport_resize(){const e=this.renderer.domElement,t=e.clientWidth,i=e.clientHeight;this.uniforms.u_resolution.value.x=t,this.uniforms.u_resolution.value.y=i,(e.width!==t||e.height!==i)&&this.renderer.setSize(t,i,!1)}cleanup(){console.log("Stop the animation before quitting..."),window.cancelAnimationFrame(this.ani_request_id)}update_textures(){for(const e in this.uniforms)-1===["u_resolution","u_time","u_mouse"].indexOf(e)&&(this.uniforms[e]=new THREE.MeshBasicMaterial({color:"black"}));for(const e of this.textures)this.uniforms[e.name]={value:e.texture};console.log(this.uniforms)}apply_mat(e,t){const i={fragmentShader:""===e.trim()?"void main(){}":e,uniforms:this.uniforms,vertexShader:void 0};""!=t.trim()&&(i.vertexShader=t);const s=new THREE.ShaderMaterial(i);this.mesh.material=s,console.log(this.uniforms)}}s.frg_template="#ifdef GL_ES\nprecision mediump float;\n#endif\n// uniform vec2 u_resolution;\n// uniform vec2 u_mouse;\nuniform float u_time;\n\nvoid main() {\n gl_FragColor = vec4(abs(sin(u_time)),0.0,0.0,1.0);\n} ";class r extends e.BaseApplication{constructor(e){super("ShaderPlayground",e)}main(){this.init_editor(),this.init_textures_list(),this.bindKey("ALT-N",()=>this.newFile()),this.bindKey("ALT-O",()=>this.openFile()),this.bindKey("CTRL-S",()=>this.saveFile())}init_editor(){this.tabbar=this.find("tabbar"),this.tabbar.items=[{text:__("Fragment"),iconclass:"bi bi-palette"},{text:__("Vertex"),iconclass:"bi bi-intersect"},{text:__("Textures"),iconclass:"bi bi-image-alt"}],this.tabbar.ontabselect=e=>{this.selectTab()},this.editor=new s(this.find("editor-container"),new n(this.find("viewport"))),this.on("resize",e=>{this.editor.resize()}),this.editor.onfilechange=e=>{this.scheme.apptitle=e},this.editor.ontextureadded=e=>{this.add_texture(e)},this.editor.filehandle=void 0,this.tabbar.selected=0}add_texture(e){{const t=this.find("texture-list");if(!e)return this.editor.renderer.textures=[],t.data=this.editor.renderer.textures,void(this.editor.renderer.needupdateTexture=!0);const i=(new THREE.TextureLoader).load(e.path.asFileHandle().getlink());i.minFilter=THREE.NearestFilter,i.magFilter=THREE.NearestFilter,i.wrapS=THREE.RepeatWrapping,i.wrapT=THREE.RepeatWrapping,e.texture=i,t.push(e),this.editor.renderer.needupdateTexture=!0}}init_textures_list(){const e=this.find("texture-list");e.buttons=[{text:"__(Add texture)",iconclass:"bi bi-plus",onbtclick:e=>{this.openDialog(new i).then(e=>this.add_texture(e))}}],e.itemtag="afx-shader-texture-item",e.onitemclose=e=>(this.editor.renderer.needupdateTexture=!0,!0),this.add_texture(void 0)}selectTab(){const e=this.tabbar.selected;2===e?($(this.find("editor-container")).hide(),$(this.find("texture-list")).show()):($(this.find("editor-container")).show(),$(this.find("texture-list")).hide()),this.editor.edit(e)}menu(){return[{text:"__(File)",nodes:[{text:"__(New)",dataid:"new",shortcut:"A-N"},{text:"__(Open)",dataid:"open",shortcut:"A-O"},{text:"__(Save)",dataid:"save",shortcut:"C-S"}],onchildselect:e=>{switch(e.data.item.data.dataid){case"new":return this.newFile();case"open":return this.openFile();case"save":return this.saveFile()}}}]}ignore_unsaved(){return new Promise(async(e,t)=>!0===this.editor.filehandle.dirty?e(!!await this.ask({title:__("Unsaved shader"),text:__("Ignore unsaved file?")})):e(!0))}async newFile(){await this.ignore_unsaved()&&(this.editor.filehandle=void 0)}async openFile(){try{if(!await this.ignore_unsaved())return;const e=await this.openDialog("FileDialog",{title:__("Open file"),mimes:this.meta().mimes});this.editor.filehandle.setPath(e.file.path),await this.editor.read()}catch(e){this.error(__(e.toString()),e)}}async saveFile(){if("Untitled"!==this.editor.filehandle.path)return this.editor.write(void 0);const e=await this.openDialog("FileDialog",{title:__("Save as"),file:this.editor.filehandle});let t=e.file.path.asFileHandle();"file"===e.file.type&&(t=t.parent());try{await this.editor.write(`${t.path}/${e.name}`)}catch(e){this.error(__(e.toString()),e)}}cleanup(e){if(this.editor.filehandle.dirty)return this.ignore_unsaved().then(e=>{e&&(this.editor.filehandle.dirty=!1,this.quit(!0))}),void e.preventDefault();this.editor.cleanup()}}e.ShaderPlayground=r,r.dependencies=["pkg://libthreejs/main.js","pkg://ACECore/core/ace.js","pkg://ACECore/path.js","pkg://ACECore/core/ext-language_tools.js","pkg://ShaderPlayground/glslx.js"]}(i=e.application||(e.application={}))}(OS||(OS={}));
\ No newline at end of file
+
+var OS;
+(function (OS) {
+ let GUI;
+ (function (GUI) {
+ let tag;
+ (function (tag) {
+ class TextureListItem extends tag.ListViewItemTag {
+ itemlayout() {
+ return {
+ el: "div",
+ children: [
+ {
+ el: "img",
+ ref: "img"
+ },
+ {
+ el: "p",
+ ref: "name"
+ }
+ ]
+ };
+ }
+ ondatachange() {
+ const v = this.data;
+ const img = this.refs.img;
+ const uri = v.path.asFileHandle().getlink();
+ img.src = uri;
+ $(this.refs.name).text(v.name);
+ }
+ init() {
+ this.closable = true;
+ }
+ reload(d) {
+ }
+ }
+ tag.define("afx-shader-texture-item", TextureListItem);
+ })(tag = GUI.tag || (GUI.tag = {}));
+ })(GUI = OS.GUI || (OS.GUI = {}));
+ let application;
+ (function (application) {
+ class AddTextureDialog extends GUI.BasicDialog {
+ constructor() {
+ super("AddTextureDialog", AddTextureDialog.scheme);
+ }
+ main() {
+ super.main();
+ const inputs = $("input", this.scheme);
+ this.find("btnOk").onbtclick = (e) => {
+ let cdata = {};
+ for (const el of inputs) {
+ let input = el;
+ if (input.value.trim() == "") {
+ return this.notify(__("All fields should be filled"));
+ }
+ cdata[input.name] = input.value.trim();
+ }
+ if (this.handle)
+ this.handle(cdata);
+ this.quit();
+ };
+ this.find("btnFile").onbtclick = (e) => {
+ this.openDialog("FileDialog", {
+ title: __("Select image file"),
+ type: "file",
+ mimes: ["image/.*"]
+ })
+ .then((d) => {
+ this.find("txtPath").value = d.file.path;
+ });
+ };
+ }
+ }
+ AddTextureDialog.scheme = `\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+\
+`;
+ class ShaderEditor {
+ constructor(domel, renderer) {
+ this.glsl_values = [ShaderEditor.frg_template, ""];
+ this.renderer = renderer;
+ this.ums = [new ace.UndoManager(), new ace.UndoManager()];
+ this.current_idx = -1;
+ this.tmp_canvas = $("")[0];
+ this.gl_compiling_ctx = this.tmp_canvas.getContext("webgl");
+ this._filehandle = undefined;
+ this.editormux = false;
+ ace.require("ace/ext/language_tools");
+ this._onfilechange = (v) => { };
+ this._ontextureadded = (t) => { };
+ this.editor = ace.edit(domel);
+ this.editor.setOptions({
+ enableBasicAutocompletion: true,
+ enableLiveAutocompletion: true,
+ enableSnippets: true,
+ highlightActiveLine: true,
+ //fontSize: "9pt"
+ });
+ this.editor.getSession().setUseWrapMode(true);
+ this.editor.session.setMode("ace/mode/glsl");
+ this.editor.setTheme("ace/theme/monokai");
+ this.cursors = [
+ this.editor.getCursorPosition(),
+ this.editor.getCursorPosition()
+ ];
+ this.editor.on("input", (e) => {
+ const value = this.editor.getValue();
+ const stype = this.current_idx == 0 ? this.gl_compiling_ctx.FRAGMENT_SHADER : this.gl_compiling_ctx.VERTEX_SHADER;
+ const errors = this.compile(value, stype);
+ if (this.filehandle.dirty === false && !this.editormux) {
+ this.filehandle.dirty = true;
+ this._onfilechange(`${this.filehandle.path}*`);
+ }
+ if (this.editormux) {
+ this.editormux = false;
+ }
+ if (errors) {
+ const reg_str = "ERROR:\\s*([0-9]+):([0-9]+):\\s*(.*)\\n";
+ const matches = errors.match(new RegExp(reg_str, "g"));
+ if (matches) {
+ this.editor.getSession().setAnnotations(matches.map((match) => {
+ const err_data = match.match(new RegExp(reg_str));
+ let ret = {};
+ if (err_data) {
+ ret = {
+ row: parseInt(err_data[2]) - 1,
+ column: 0,
+ text: err_data[3],
+ type: "error"
+ };
+ }
+ return ret;
+ }));
+ }
+ }
+ else {
+ this.editor.getSession().setAnnotations([]);
+ this.glsl_values[this.current_idx] = value;
+ this.renderer.apply_mat(this.glsl_values[0], this.glsl_values[1]);
+ }
+ });
+ }
+ set onfilechange(fn) {
+ this._onfilechange = fn;
+ }
+ set ontextureadded(fn) {
+ this._ontextureadded = fn;
+ }
+ set filehandle(v) {
+ this._filehandle = v;
+ this.read();
+ }
+ get filehandle() {
+ return this._filehandle;
+ }
+ read() {
+ return new Promise(async (resolve, reject) => {
+ if (this._filehandle === undefined) {
+ this.renderer.textures.length = 0;
+ this._filehandle = "Untitled".asFileHandle();
+ this._onfilechange(this._filehandle.path);
+ this.glsl_values = [ShaderEditor.frg_template, ""];
+ if (this.current_idx != 2 && this.current_idx != -1) {
+ this.editormux = true;
+ this.editor.setValue(this.glsl_values[this.current_idx]);
+ }
+ this._ontextureadded(undefined);
+ return resolve(undefined);
+ }
+ try {
+ const data = await this._filehandle.read("json");
+ this.glsl_values[0] = data.source[0];
+ this.glsl_values[1] = data.source[1];
+ if (this.current_idx != 2 && this.current_idx != -1) {
+ this.editormux = true;
+ this.editor.setValue(this.glsl_values[this.current_idx]);
+ }
+ this._ontextureadded(undefined);
+ for (const v of data.textures) {
+ this._ontextureadded(v);
+ }
+ this._onfilechange(this._filehandle.path);
+ resolve(undefined);
+ }
+ catch (e) {
+ reject(e);
+ }
+ });
+ }
+ write(p) {
+ return new Promise(async (resolve, reject) => {
+ let path = p;
+ const error = __("Unknown save path");
+ if (!path) {
+ if (this._filehandle === undefined)
+ return reject(error);
+ path = this._filehandle.path;
+ }
+ if (path === "Untitled") {
+ return reject(error);
+ }
+ try {
+ this._filehandle.setPath(path);
+ const data = {};
+ if (this.current_idx != 2) {
+ this.glsl_values[this.current_idx] = this.editor.getValue();
+ }
+ data.source = this.glsl_values;
+ data.textures = this.renderer.textures.map((v) => {
+ return {
+ name: v.name,
+ path: v.path
+ };
+ });
+ this.filehandle.cache = data;
+ const ret = await this.filehandle.write("object");
+ this._filehandle.dirty = false;
+ this._onfilechange(`${this.filehandle.path}`);
+ resolve(undefined);
+ }
+ catch (e) {
+ reject(e);
+ }
+ });
+ }
+ compile(code, type) {
+ // Compiles either a shader of type gl.VERTEX_SHADER or gl.FRAGMENT_SHADER
+ let shader = this.gl_compiling_ctx.createShader(type);
+ this.gl_compiling_ctx.shaderSource(shader, code);
+ this.gl_compiling_ctx.compileShader(shader);
+ let errors = undefined;
+ if (!this.gl_compiling_ctx.getShaderParameter(shader, this.gl_compiling_ctx.COMPILE_STATUS)) {
+ errors = this.gl_compiling_ctx.getShaderInfoLog(shader);
+ }
+ this.gl_compiling_ctx.deleteShader(shader);
+ return errors;
+ }
+ edit(index) {
+ if (index < 0) {
+ return;
+ }
+ if (index != 2 && this.current_idx != 2) {
+ if (index === 0) {
+ this.glsl_values[1] = this.editor.getValue();
+ this.cursors[1] = this.editor.getCursorPosition();
+ }
+ else if (index === 1) {
+ this.glsl_values[0] = this.editor.getValue();
+ this.cursors[0] = this.editor.getCursorPosition();
+ }
+ }
+ else if (index == 2) {
+ this.glsl_values[this.current_idx] = this.editor.getValue();
+ this.cursors[this.current_idx] = this.editor.getCursorPosition();
+ this.current_idx = index;
+ return;
+ }
+ this.current_idx = index;
+ this.editormux = true;
+ this.editor.getSession().setUndoManager(new ace.UndoManager());
+ this.editor.setValue(this.glsl_values[index]);
+ this.editor.getSession().setUndoManager(this.ums[index]);
+ const c = this.cursors[index];
+ this.editor.renderer.scrollCursorIntoView({
+ row: c.row,
+ column: c.column,
+ }, 0.5);
+ this.editor.selection.moveTo(c.row, c.column);
+ this.editor.focus();
+ }
+ cleanup() {
+ this.renderer.cleanup();
+ $(this.tmp_canvas).remove();
+ }
+ resize() {
+ this.editor.resize();
+ this.renderer.viewport_resize();
+ }
+ }
+ ;
+ class ShaderRenderer {
+ constructor(canvas) {
+ this.textures = [];
+ this.renderer = new THREE.WebGLRenderer({
+ canvas: canvas,
+ alpha: true
+ });
+ this.renderer.autoClearColor = false;
+ this.clock = new THREE.Clock();
+ this.camera = new THREE.OrthographicCamera(-1, // left
+ 1, // right
+ 1, // top
+ -1, // bottom
+ -1, // near,
+ 1);
+ this.needupdateTexture = false;
+ this.scene = new THREE.Scene();
+ const material = new THREE.MeshBasicMaterial({
+ color: 'white',
+ });
+ const plane = new THREE.PlaneGeometry(2, 2);
+ this.mesh = new THREE.Mesh(plane, material);
+ this.scene.add(this.mesh);
+ this.uniforms = {
+ u_resolution: { value: { x: 0, y: 0 } },
+ u_time: { value: 0.0 },
+ u_mouse: { value: { x: 0, y: 0 } },
+ };
+ this.viewport_resize();
+ this.ani_request_id = requestAnimationFrame(() => this.viewport_render());
+ }
+ viewport_render() {
+ if (this.needupdateTexture) {
+ this.update_textures();
+ this.needupdateTexture = false;
+ }
+ this.uniforms.u_time.value = this.clock.getElapsedTime();
+ try {
+ this.renderer.render(this.scene, this.camera);
+ }
+ catch (e) {
+ console.error(e);
+ }
+ this.ani_request_id = requestAnimationFrame(() => this.viewport_render());
+ }
+ viewport_resize() {
+ const canvas = this.renderer.domElement;
+ const width = canvas.clientWidth;
+ const height = canvas.clientHeight;
+ this.uniforms.u_resolution.value.x = width;
+ this.uniforms.u_resolution.value.y = height;
+ const needResize = canvas.width !== width || canvas.height !== height;
+ if (needResize) {
+ this.renderer.setSize(width, height, false);
+ }
+ }
+ cleanup() {
+ console.log("Stop the animation before quitting...");
+ window.cancelAnimationFrame(this.ani_request_id);
+ }
+ update_textures() {
+ for (const key in this.uniforms) {
+ if (["u_resolution", "u_time", "u_mouse"].indexOf(key) === -1) {
+ this.uniforms[key] = new THREE.MeshBasicMaterial({
+ color: 'black',
+ });
+ }
+ }
+ for (const v of this.textures) {
+ this.uniforms[v.name] = { value: v.texture };
+ }
+ console.log(this.uniforms);
+ }
+ apply_mat(fragment_shader, vertex_shader) {
+ const empty_main = "void main(){}";
+ const opts = {
+ fragmentShader: fragment_shader.trim() === "" ? empty_main : fragment_shader,
+ uniforms: this.uniforms,
+ vertexShader: undefined
+ };
+ if (vertex_shader.trim() != "")
+ opts.vertexShader = vertex_shader;
+ const mat = new THREE.ShaderMaterial(opts);
+ this.mesh.material = mat;
+ console.log(this.uniforms);
+ }
+ }
+ ShaderEditor.frg_template = `\
+#ifdef GL_ES
+precision mediump float;
+#endif
+// uniform vec2 u_resolution;
+// uniform vec2 u_mouse;
+uniform float u_time;
+
+void main() {
+ gl_FragColor = vec4(abs(sin(u_time)),0.0,0.0,1.0);
+}\
+ `;
+ /**
+ *
+ * @class ShaderPlayground
+ * @extends {BaseApplication}
+ */
+ class ShaderPlayground extends application.BaseApplication {
+ constructor(args) {
+ super("ShaderPlayground", args);
+ }
+ main() {
+ this.init_editor();
+ this.init_textures_list();
+ this.bindKey("ALT-N", () => {
+ return this.newFile();
+ });
+ this.bindKey("ALT-O", () => {
+ return this.openFile();
+ });
+ this.bindKey("CTRL-S", () => {
+ return this.saveFile();
+ });
+ }
+ /**
+ * Init the editor for fragment and
+ * vertex shader
+ */
+ init_editor() {
+ this.tabbar = this.find("tabbar");
+ this.tabbar.items = [
+ {
+ text: __("Fragment"),
+ iconclass: "bi bi-palette"
+ },
+ {
+ text: __("Vertex"),
+ iconclass: "bi bi-intersect"
+ },
+ {
+ text: __("Textures"),
+ iconclass: "bi bi-image-alt"
+ }
+ ];
+ this.tabbar.ontabselect = (_e) => {
+ this.selectTab();
+ };
+ this.editor = new ShaderEditor(this.find("editor-container"), new ShaderRenderer(this.find("viewport")));
+ this.on("resize", (e) => {
+ this.editor.resize();
+ });
+ this.editor.onfilechange = (v) => {
+ this.scheme.apptitle = v;
+ };
+ this.editor.ontextureadded = (v) => {
+ this.add_texture(v);
+ };
+ this.editor.filehandle = undefined;
+ this.tabbar.selected = 0;
+ }
+ add_texture(data) {
+ {
+ const listview = this.find("texture-list");
+ if (!data) {
+ this.editor.renderer.textures = [];
+ listview.data = this.editor.renderer.textures;
+ this.editor.renderer.needupdateTexture = true;
+ return;
+ }
+ const loader = new THREE.TextureLoader();
+ const texture = loader.load(data.path.asFileHandle().getlink());
+ texture.minFilter = THREE.NearestFilter;
+ texture.magFilter = THREE.NearestFilter;
+ texture.wrapS = THREE.RepeatWrapping;
+ texture.wrapT = THREE.RepeatWrapping;
+ data.texture = texture;
+ listview.push(data);
+ this.editor.renderer.needupdateTexture = true;
+ }
+ }
+ init_textures_list() {
+ const listview = this.find("texture-list");
+ listview.buttons = [
+ {
+ text: "__(Add texture)",
+ iconclass: "bi bi-plus",
+ onbtclick: (_e) => {
+ this
+ .openDialog(new AddTextureDialog())
+ .then((data) => this.add_texture(data));
+ }
+ }
+ ];
+ listview.itemtag = "afx-shader-texture-item";
+ listview.onitemclose = (e) => {
+ this.editor.renderer.needupdateTexture = true;
+ return true;
+ };
+ this.add_texture(undefined);
+ }
+ selectTab() {
+ const index = this.tabbar.selected;
+ if (index === 2) {
+ $(this.find("editor-container")).hide();
+ $(this.find("texture-list")).show();
+ }
+ else {
+ $(this.find("editor-container")).show();
+ $(this.find("texture-list")).hide();
+ }
+ this.editor.edit(index);
+ }
+ menu() {
+ return [
+ {
+ text: "__(File)",
+ nodes: [
+ {
+ text: "__(New)",
+ dataid: "new",
+ shortcut: 'A-N'
+ },
+ {
+ text: "__(Open)",
+ dataid: "open",
+ shortcut: 'A-O'
+ },
+ {
+ text: "__(Save)",
+ dataid: "save",
+ shortcut: 'C-S'
+ }
+ ],
+ onchildselect: (e) => {
+ switch (e.data.item.data.dataid) {
+ case "new":
+ return this.newFile();
+ case "open":
+ return this.openFile();
+ case "save":
+ return this.saveFile();
+ }
+ }
+ }
+ ];
+ }
+ ignore_unsaved() {
+ return new Promise(async (resolve, reject) => {
+ if (this.editor.filehandle.dirty === true) {
+ const r = await this.ask({
+ title: __("Unsaved shader"),
+ text: __("Ignore unsaved file?")
+ });
+ if (!r) {
+ return resolve(false);
+ }
+ return resolve(true);
+ }
+ return resolve(true);
+ });
+ }
+ async newFile() {
+ const ignore = await this.ignore_unsaved();
+ if (!ignore)
+ return;
+ this.editor.filehandle = undefined;
+ }
+ async openFile() {
+ try {
+ const ignore = await this.ignore_unsaved();
+ if (!ignore)
+ return;
+ const d = await this.openDialog("FileDialog", {
+ title: __("Open file"),
+ mimes: this.meta().mimes
+ });
+ this.editor.filehandle.setPath(d.file.path);
+ await this.editor.read();
+ }
+ catch (e) {
+ this.error(__(e.toString()), e);
+ }
+ }
+ async saveFile() {
+ if (this.editor.filehandle.path !== "Untitled") {
+ return this.editor.write(undefined);
+ }
+ const f = await this.openDialog("FileDialog", {
+ title: __("Save as"),
+ file: this.editor.filehandle
+ });
+ let handle = f.file.path.asFileHandle();
+ if (f.file.type === "file") {
+ handle = handle.parent();
+ }
+ try {
+ await this.editor.write(`${handle.path}/${f.name}`);
+ }
+ catch (e) {
+ this.error(__(e.toString()), e);
+ }
+ }
+ cleanup(e) {
+ if (this.editor.filehandle.dirty) {
+ this.ignore_unsaved()
+ .then((d) => {
+ if (d) {
+ this.editor.filehandle.dirty = false;
+ this.quit(true);
+ }
+ });
+ e.preventDefault();
+ return;
+ }
+ this.editor.cleanup();
+ }
+ }
+ application.ShaderPlayground = ShaderPlayground;
+ /**
+ * Application dependenicies preload
+ */
+ ShaderPlayground.dependencies = [
+ "pkg://libthreejs/main.js",
+ "pkg://ACECore/core/ace.js",
+ "pkg://ACECore/path.js",
+ "pkg://ACECore/core/ext-language_tools.js"
+ ];
+ })(application = OS.application || (OS.application = {}));
+})(OS || (OS = {}));
diff --git a/ShaderPlayground/build/release/ShaderPlayground.zip b/ShaderPlayground/build/release/ShaderPlayground.zip
deleted file mode 100644
index 214df13..0000000
Binary files a/ShaderPlayground/build/release/ShaderPlayground.zip and /dev/null differ
diff --git a/ShaderPlayground/main.ts b/ShaderPlayground/main.ts
index 053a4ac..71afec2 100644
--- a/ShaderPlayground/main.ts
+++ b/ShaderPlayground/main.ts
@@ -753,8 +753,7 @@ void main() {
"pkg://libthreejs/main.js",
"pkg://ACECore/core/ace.js",
"pkg://ACECore/path.js",
- "pkg://ACECore/core/ext-language_tools.js",
- "pkg://ShaderPlayground/glslx.js"
+ "pkg://ACECore/core/ext-language_tools.js"
];
}
}
\ No newline at end of file