update ShaderPlayground

This commit is contained in:
lxsang 2021-06-21 00:04:52 +02:00
parent 916fd7b418
commit 52e24e9b2b
11 changed files with 419 additions and 215 deletions

View File

@ -4,4 +4,7 @@ Playground for working with Open GL shader language, the sharder is rendered
with the Three.js library with the Three.js library
## Change logs ## Change logs
- v0.0.2-a:
- Remove GLSLX, use the default WEBGL API for shader compiling
- Allow save/open shader source code to/from file (JSON)
- v0.0.1-a: Initial version - v0.0.1-a: Initial version

View File

@ -47,8 +47,7 @@
"scheme.html", "scheme.html",
"package.json", "package.json",
"README.md", "README.md",
"main.css", "main.css"
"glslx.js"
], ],
"dest":"build/debug" "dest":"build/debug"
} }
@ -79,6 +78,20 @@
"build and run": { "build and run": {
"depend": ["clean", "build", "copy", "run"], "depend": ["clean", "build", "copy", "run"],
"jobs": [] "jobs": []
},
"locale": {
"require": ["locale"],
"jobs": [
{
"name":"locale-gen",
"data": {
"src": "",
"exclude": ["build/"],
"locale": "en_GB",
"dest": "package.json"
}
}
]
} }
} }
} }

View File

@ -4,4 +4,7 @@ Playground for working with Open GL shader language, the sharder is rendered
with the Three.js library with the Three.js library
## Change logs ## Change logs
- v0.0.2-a:
- Remove GLSLX, use the default WEBGL API for shader compiling
- Allow save/open shader source code to/from file (JSON)
- v0.0.1-a: Initial version - v0.0.1-a: Initial version

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,42 @@
{ {
"pkgname": "ShaderPlayground", "pkgname": "ShaderPlayground",
"app":"ShaderPlayground", "app": "ShaderPlayground",
"name":"OpenGL Shader Playground", "name": "OpenGL Shader Playground",
"description":"OpenGL Shader Playground", "description": "OpenGL Shader Playground",
"info":{ "info": {
"author": "Xuan Sang LE", "author": "Xuan Sang LE",
"email": "mrsang@iohub.dev" "email": "mrsang@iohub.dev"
}, },
"version":"0.0.1-a", "version": "0.0.2-a",
"category":"Development", "category": "Development",
"iconclass":"bi bi-lightbulb-fill", "iconclass": "bi bi-lightbulb-fill",
"mimes":["none"], "mimes": [
"dependencies":["libthreejs@0.0.129-r"], "application/json"
"locale": {} ],
"dependencies": [
"libthreejs@0.0.129-r"
],
"locale": {},
"locales": {
"en_GB": {
"Name": "Name",
"Path/URL": "Path/URL",
"Ok": "Ok",
"Add texture": "Add texture",
"File": "File",
"New": "New",
"Open": "Open",
"Save": "Save",
"All fields should be filled": "All fields should be filled",
"Select image file": "Select image file",
"Unknown save path": "Unknown save path",
"Fragment": "Fragment",
"Vertex": "Vertex",
"Textures": "Textures",
"Unsaved shader": "Unsaved shader",
"Ignore unsaved file?": "Ignore unsaved file?",
"Open file": "Open file",
"Save as": "Save as"
}
}
} }

File diff suppressed because one or more lines are too long

View File

@ -39,7 +39,6 @@ namespace OS {
export namespace application { export namespace application {
declare var ace: any; declare var ace: any;
declare var THREE: any; declare var THREE: any;
declare var GLSLX:any;
class AddTextureDialog extends GUI.BasicDialog class AddTextureDialog extends GUI.BasicDialog
{ {
@ -106,20 +105,32 @@ namespace OS {
class ShaderEditor class ShaderEditor
{ {
static frg_template: string;
private ums: any[]; private ums: any[];
private glsl_values: string[]; private glsl_values: string[];
private cursors: any[]; private cursors: any[];
private editor: any; private editor: any;
private current_idx: number; private current_idx: number;
private editormux:boolean;
private gl_compiling_ctx: WebGLRenderingContext;
renderer: ShaderRenderer; renderer: ShaderRenderer;
private tmp_canvas: HTMLCanvasElement;
private _filehandle: API.VFS.BaseFileHandle;
private _onfilechange: (t: string) => void;
private _ontextureadded: (data: GenericObject<any>) => void;
constructor(domel: HTMLElement, renderer: ShaderRenderer) constructor(domel: HTMLElement, renderer: ShaderRenderer)
{ {
const empty_main = "void main(){}"; this.glsl_values = [ShaderEditor.frg_template, ""];
this.renderer = renderer; this.renderer = renderer;
this.ums = [new ace.UndoManager(), new ace.UndoManager()]; this.ums = [new ace.UndoManager(), new ace.UndoManager()];
this.current_idx = -1; this.current_idx = -1;
this.glsl_values = [empty_main, ""]; this.tmp_canvas = $("<canvas />")[0] as HTMLCanvasElement;
this.gl_compiling_ctx = this.tmp_canvas.getContext("webgl");
this._filehandle = undefined;
this.editormux = false;
ace.require("ace/ext/language_tools"); ace.require("ace/ext/language_tools");
this._onfilechange = (v) =>{};
this._ontextureadded = (t) => {};
this.editor = ace.edit(domel); this.editor = ace.edit(domel);
this.editor.setOptions({ this.editor.setOptions({
enableBasicAutocompletion: true, enableBasicAutocompletion: true,
@ -136,19 +147,23 @@ namespace OS {
this.editor.getCursorPosition(), this.editor.getCursorPosition(),
this.editor.getCursorPosition() this.editor.getCursorPosition()
]; ];
this.editor.on("input", (e) => { this.editor.on("input", (e) => {
const value = this.editor.getValue(); const value = this.editor.getValue();
const stype = this.current_idx == 0 ? this.gl_compiling_ctx.FRAGMENT_SHADER: this.gl_compiling_ctx.VERTEX_SHADER;
const result = GLSLX.compile(value, { const errors = this.compile(value, stype);
format: "json" if(this.filehandle.dirty === false && !this.editormux)
}); {
if (result.output) { this.filehandle.dirty = true;
this.editor.getSession().setAnnotations([]); this._onfilechange(`${this.filehandle.path}*`);
this.glsl_values[this.current_idx] = value; }
this.renderer.apply_mat(this.glsl_values[0],this.glsl_values[1]); if(this.editormux)
} else { {
const reg_str = "<stdin>:([0-9]+):([0-9]+):\\s*error:\\s*(.*)\\n"; this.editormux = false;
const matches = (result.log as string).match(new RegExp(reg_str, "g")); }
if(errors) {
const reg_str = "ERROR:\\s*([0-9]+):([0-9]+):\\s*(.*)\\n";
const matches = errors.match(new RegExp(reg_str, "g"));
if(matches) if(matches)
{ {
this.editor.getSession().setAnnotations( this.editor.getSession().setAnnotations(
@ -158,8 +173,8 @@ namespace OS {
if(err_data) if(err_data)
{ {
ret = { ret = {
row: parseInt(err_data[1]) - 1, row: parseInt(err_data[2]) - 1,
column: parseInt(err_data[2]), column: 0,
text: err_data[3], text: err_data[3],
type: "error" type: "error"
}; };
@ -169,9 +184,131 @@ namespace OS {
); );
} }
} }
else
{
this.editor.getSession().setAnnotations([]);
this.glsl_values[this.current_idx] = value;
this.renderer.apply_mat(this.glsl_values[0], this.glsl_values[1]);
}
}); });
} }
public set onfilechange(fn:(v: string) => void)
{
this._onfilechange = fn;
}
public set ontextureadded(fn:(v:GenericObject<any>)=> void)
{
this._ontextureadded = fn;
}
public set filehandle(v: API.VFS.BaseFileHandle)
{
this._filehandle = v;
this.read();
}
public get filehandle(): API.VFS.BaseFileHandle
{
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: string) {
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 = {} as GenericObject<any>;
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);
}
});
}
private compile(code: string, type: number)
{
// 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: string = 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:number): void edit(index:number): void
{ {
if(index < 0) if(index < 0)
@ -185,7 +322,7 @@ namespace OS {
this.glsl_values[1] = this.editor.getValue(); this.glsl_values[1] = this.editor.getValue();
this.cursors[1] = this.editor.getCursorPosition(); this.cursors[1] = this.editor.getCursorPosition();
} }
else else if(index === 1)
{ {
this.glsl_values[0] = this.editor.getValue(); this.glsl_values[0] = this.editor.getValue();
this.cursors[0] = this.editor.getCursorPosition(); this.cursors[0] = this.editor.getCursorPosition();
@ -199,6 +336,7 @@ namespace OS {
return; return;
} }
this.current_idx = index; this.current_idx = index;
this.editormux = true;
this.editor.getSession().setUndoManager(new ace.UndoManager()); this.editor.getSession().setUndoManager(new ace.UndoManager());
this.editor.setValue(this.glsl_values[index]); this.editor.setValue(this.glsl_values[index]);
this.editor.getSession().setUndoManager(this.ums[index]); this.editor.getSession().setUndoManager(this.ums[index]);
@ -220,6 +358,7 @@ namespace OS {
cleanup(): void cleanup(): void
{ {
this.renderer.cleanup(); this.renderer.cleanup();
$(this.tmp_canvas).remove();
} }
resize(): void resize(): void
{ {
@ -344,6 +483,19 @@ namespace OS {
this.mesh.material = mat; this.mesh.material = mat;
} }
} }
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 * @class ShaderPlayground
@ -355,13 +507,21 @@ namespace OS {
*/ */
private tabbar: GUI.tag.TabBarTag; private tabbar: GUI.tag.TabBarTag;
private editor: ShaderEditor; private editor: ShaderEditor;
constructor(args: AppArgumentsType[]) { constructor(args: AppArgumentsType[]) {
super("ShaderPlayground", args); super("ShaderPlayground", args);
} }
main(): void { main(): void {
this.init_editor(); this.init_editor();
this.init_textures_list(); 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();
});
} }
/** /**
@ -394,22 +554,25 @@ namespace OS {
this.on("resize", (e) =>{ this.on("resize", (e) =>{
this.editor.resize(); this.editor.resize();
}); });
this.editor.onfilechange = (v: string) => {
(this.scheme as GUI.tag.WindowTag).apptitle = v;
};
this.editor.ontextureadded = (v: GenericObject<any>) =>
{
this.add_texture(v);
}
this.editor.filehandle = undefined;
this.tabbar.selected = 0; this.tabbar.selected = 0;
} }
private init_textures_list(): void private add_texture(data: GenericObject<any>): void
{
{ {
const listview = this.find("texture-list") as GUI.tag.ListViewTag; const listview = this.find("texture-list") as GUI.tag.ListViewTag;
listview.buttons = [
{
text: "__(Add texture)",
iconclass: "bi bi-plus",
onbtclick: (e) => {
this
.openDialog(new AddTextureDialog())
.then((data) => {
if(!data) if(!data)
{ {
this.editor.renderer.textures = [];
listview.data = this.editor.renderer.textures;
return; return;
} }
const loader = new THREE.TextureLoader(); const loader = new THREE.TextureLoader();
@ -421,7 +584,19 @@ namespace OS {
data.texture = texture; data.texture = texture;
listview.push(data); listview.push(data);
this.editor.renderer.needupdateTexture = true; this.editor.renderer.needupdateTexture = true;
}); }
}
private init_textures_list(): void
{
const listview = this.find("texture-list") as GUI.tag.ListViewTag;
listview.buttons = [
{
text: "__(Add texture)",
iconclass: "bi bi-plus",
onbtclick: (_e: any) => {
this
.openDialog(new AddTextureDialog())
.then((data) => this.add_texture(data));
} }
} }
]; ];
@ -430,7 +605,7 @@ namespace OS {
this.editor.renderer.needupdateTexture = true; this.editor.renderer.needupdateTexture = true;
return true; return true;
}; };
listview.data = this.editor.renderer.textures; this.add_texture(undefined);
} }
private selectTab(): void private selectTab(): void
@ -449,12 +624,124 @@ namespace OS {
this.editor.edit(index); this.editor.edit(index);
} }
protected cleanup(_e: any): void menu() {
return [
{ {
this.editor.cleanup(); 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();
}
}
}
];
}
private ignore_unsaved(): Promise<boolean>
{
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);
});
}
private async newFile()
{
const ignore = await this.ignore_unsaved();
if(!ignore)
return;
this.editor.filehandle = undefined;
}
private 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);
} }
} }
private 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);
}
}
protected cleanup(e: any): void
{
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 dependenicies preload * Application dependenicies preload
*/ */

View File

@ -1,16 +1,42 @@
{ {
"pkgname": "ShaderPlayground", "pkgname": "ShaderPlayground",
"app":"ShaderPlayground", "app": "ShaderPlayground",
"name":"OpenGL Shader Playground", "name": "OpenGL Shader Playground",
"description":"OpenGL Shader Playground", "description": "OpenGL Shader Playground",
"info":{ "info": {
"author": "Xuan Sang LE", "author": "Xuan Sang LE",
"email": "mrsang@iohub.dev" "email": "mrsang@iohub.dev"
}, },
"version":"0.0.1-a", "version": "0.0.2-a",
"category":"Development", "category": "Development",
"iconclass":"bi bi-lightbulb-fill", "iconclass": "bi bi-lightbulb-fill",
"mimes":["none"], "mimes": [
"dependencies":["libthreejs@0.0.129-r"], "application/json"
"locale": {} ],
"dependencies": [
"libthreejs@0.0.129-r"
],
"locale": {},
"locales": {
"en_GB": {
"Name": "Name",
"Path/URL": "Path/URL",
"Ok": "Ok",
"Add texture": "Add texture",
"File": "File",
"New": "New",
"Open": "Open",
"Save": "Save",
"All fields should be filled": "All fields should be filled",
"Select image file": "Select image file",
"Unknown save path": "Unknown save path",
"Fragment": "Fragment",
"Vertex": "Vertex",
"Textures": "Textures",
"Unsaved shader": "Unsaved shader",
"Ignore unsaved file?": "Ignore unsaved file?",
"Open file": "Open file",
"Save as": "Save as"
}
}
} }

View File

@ -335,7 +335,7 @@
"description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/ShaderPlayground/README.md", "description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/ShaderPlayground/README.md",
"category": "Development", "category": "Development",
"author": "Xuan Sang LE", "author": "Xuan Sang LE",
"version": "0.0.1-a", "version": "0.0.2-a",
"dependencies": ["libthreejs@0.0.129-r"], "dependencies": ["libthreejs@0.0.129-r"],
"download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/ShaderPlayground/build/release/ShaderPlayground.zip" "download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/ShaderPlayground/build/release/ShaderPlayground.zip"
}, },