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
## 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

View File

@ -47,8 +47,7 @@
"scheme.html",
"package.json",
"README.md",
"main.css",
"glslx.js"
"main.css"
],
"dest":"build/debug"
}
@ -79,6 +78,20 @@
"build and run": {
"depend": ["clean", "build", "copy", "run"],
"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
## 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

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",
"app":"ShaderPlayground",
"name":"OpenGL Shader Playground",
"description":"OpenGL Shader Playground",
"info":{
"app": "ShaderPlayground",
"name": "OpenGL Shader Playground",
"description": "OpenGL Shader Playground",
"info": {
"author": "Xuan Sang LE",
"email": "mrsang@iohub.dev"
},
"version":"0.0.1-a",
"category":"Development",
"iconclass":"bi bi-lightbulb-fill",
"mimes":["none"],
"dependencies":["libthreejs@0.0.129-r"],
"locale": {}
"version": "0.0.2-a",
"category": "Development",
"iconclass": "bi bi-lightbulb-fill",
"mimes": [
"application/json"
],
"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 {
declare var ace: any;
declare var THREE: any;
declare var GLSLX:any;
class AddTextureDialog extends GUI.BasicDialog
{
@ -106,20 +105,32 @@ namespace OS {
class ShaderEditor
{
static frg_template: string;
private ums: any[];
private glsl_values: string[];
private cursors: any[];
private editor: any;
private current_idx: number;
private editormux:boolean;
private gl_compiling_ctx: WebGLRenderingContext;
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)
{
const empty_main = "void main(){}";
this.glsl_values = [ShaderEditor.frg_template, ""];
this.renderer = renderer;
this.ums = [new ace.UndoManager(), new ace.UndoManager()];
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");
this._onfilechange = (v) =>{};
this._ontextureadded = (t) => {};
this.editor = ace.edit(domel);
this.editor.setOptions({
enableBasicAutocompletion: true,
@ -136,19 +147,23 @@ namespace OS {
this.editor.getCursorPosition(),
this.editor.getCursorPosition()
];
this.editor.on("input", (e) => {
const value = this.editor.getValue();
const result = GLSLX.compile(value, {
format: "json"
});
if (result.output) {
this.editor.getSession().setAnnotations([]);
this.glsl_values[this.current_idx] = value;
this.renderer.apply_mat(this.glsl_values[0],this.glsl_values[1]);
} else {
const reg_str = "<stdin>:([0-9]+):([0-9]+):\\s*error:\\s*(.*)\\n";
const matches = (result.log as string).match(new RegExp(reg_str, "g"));
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(
@ -158,8 +173,8 @@ namespace OS {
if(err_data)
{
ret = {
row: parseInt(err_data[1]) - 1,
column: parseInt(err_data[2]),
row: parseInt(err_data[2]) - 1,
column: 0,
text: err_data[3],
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
{
if(index < 0)
@ -185,7 +322,7 @@ namespace OS {
this.glsl_values[1] = this.editor.getValue();
this.cursors[1] = this.editor.getCursorPosition();
}
else
else if(index === 1)
{
this.glsl_values[0] = this.editor.getValue();
this.cursors[0] = this.editor.getCursorPosition();
@ -199,6 +336,7 @@ namespace OS {
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]);
@ -220,6 +358,7 @@ namespace OS {
cleanup(): void
{
this.renderer.cleanup();
$(this.tmp_canvas).remove();
}
resize(): void
{
@ -344,6 +483,19 @@ namespace OS {
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
@ -355,13 +507,21 @@ namespace OS {
*/
private tabbar: GUI.tag.TabBarTag;
private editor: ShaderEditor;
constructor(args: AppArgumentsType[]) {
super("ShaderPlayground", args);
}
main(): void {
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();
});
}
/**
@ -394,9 +554,38 @@ namespace OS {
this.on("resize", (e) =>{
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;
}
private add_texture(data: GenericObject<any>): void
{
{
const listview = this.find("texture-list") as GUI.tag.ListViewTag;
if(!data)
{
this.editor.renderer.textures = [];
listview.data = this.editor.renderer.textures;
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;
}
}
private init_textures_list(): void
{
const listview = this.find("texture-list") as GUI.tag.ListViewTag;
@ -404,24 +593,10 @@ namespace OS {
{
text: "__(Add texture)",
iconclass: "bi bi-plus",
onbtclick: (e) => {
onbtclick: (_e: any) => {
this
.openDialog(new AddTextureDialog())
.then((data) => {
if(!data)
{
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;
});
.then((data) => this.add_texture(data));
}
}
];
@ -430,7 +605,7 @@ namespace OS {
this.editor.renderer.needupdateTexture = true;
return true;
};
listview.data = this.editor.renderer.textures;
this.add_texture(undefined);
}
private selectTab(): void
@ -449,12 +624,124 @@ namespace OS {
this.editor.edit(index);
}
protected cleanup(_e: any): void
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();
}
}
}
];
}
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
*/

View File

@ -1,16 +1,42 @@
{
"pkgname": "ShaderPlayground",
"app":"ShaderPlayground",
"name":"OpenGL Shader Playground",
"description":"OpenGL Shader Playground",
"info":{
"app": "ShaderPlayground",
"name": "OpenGL Shader Playground",
"description": "OpenGL Shader Playground",
"info": {
"author": "Xuan Sang LE",
"email": "mrsang@iohub.dev"
},
"version":"0.0.1-a",
"category":"Development",
"iconclass":"bi bi-lightbulb-fill",
"mimes":["none"],
"dependencies":["libthreejs@0.0.129-r"],
"locale": {}
"version": "0.0.2-a",
"category": "Development",
"iconclass": "bi bi-lightbulb-fill",
"mimes": [
"application/json"
],
"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",
"category": "Development",
"author": "Xuan Sang LE",
"version": "0.0.1-a",
"version": "0.0.2-a",
"dependencies": ["libthreejs@0.0.129-r"],
"download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/ShaderPlayground/build/release/ShaderPlayground.zip"
},