diff --git a/web/pw-visualizer/assets/res/earth.png b/web/pw-visualizer/assets/res/earth.png new file mode 100644 index 0000000..7897e32 Binary files /dev/null and b/web/pw-visualizer/assets/res/earth.png differ diff --git a/web/pw-visualizer/assets/shaders/frag/image.glsl b/web/pw-visualizer/assets/shaders/frag/image.glsl index 69c8b91..f8d62c9 100644 --- a/web/pw-visualizer/assets/shaders/frag/image.glsl +++ b/web/pw-visualizer/assets/shaders/frag/image.glsl @@ -10,5 +10,4 @@ uniform sampler2D u_texture; void main() { gl_FragColor = texture2D(u_texture, v_texCoord); -// gl_FragColor = vec4(0.7, 0.7, 0.0, 1.0); } diff --git a/web/pw-visualizer/assets/shaders/frag/masked_image.glsl b/web/pw-visualizer/assets/shaders/frag/masked_image.glsl new file mode 100644 index 0000000..da0c787 --- /dev/null +++ b/web/pw-visualizer/assets/shaders/frag/masked_image.glsl @@ -0,0 +1,21 @@ +#ifdef GL_ES +precision mediump float; +#endif + +// Passed in from the vertex shader. +varying vec2 v_texCoord; + +uniform float u_step_interval; +uniform float u_time; +uniform vec3 u_color; +uniform vec3 u_color_next; + + +// The texture. +uniform sampler2D u_texture; + +void main() { + float alpha = texture2D(u_texture, v_texCoord).a; + vec3 color = mix(u_color, u_color_next, u_time); + gl_FragColor = vec4(color, alpha); +} diff --git a/web/pw-visualizer/src/assets.ts b/web/pw-visualizer/src/assets.ts index e04f2c1..be155c5 100644 --- a/web/pw-visualizer/src/assets.ts +++ b/web/pw-visualizer/src/assets.ts @@ -4,9 +4,13 @@ export {default as earthSvg} from "../assets/res/earth.svg"; export {default as marsSvg} from "../assets/res/mars.svg"; export {default as venusSvg} from "../assets/res/venus.svg"; +export {default as earthPng} from "../assets/res/earth.png"; + export {default as fontPng} from "../assets/res/font.png"; export {default as imageFragmentShader} from "../assets/shaders/frag/image.glsl?url"; +export {default as maskedImageFragmentShader} from "../assets/shaders/frag/masked_image.glsl?url"; + export {default as simpleFragmentShader} from "../assets/shaders/frag/simple.glsl?url"; export {default as vorFragmentShader} from "../assets/shaders/frag/vor.glsl?url"; diff --git a/web/pw-visualizer/src/index.ts b/web/pw-visualizer/src/index.ts index cd58aa7..6f2c1b4 100644 --- a/web/pw-visualizer/src/index.ts +++ b/web/pw-visualizer/src/index.ts @@ -22,12 +22,13 @@ import { UniformMatrix3fv, UniformBool, } from "./webgl/shader"; -import { Renderer } from "./webgl/renderer"; +import { DefaultRenderable, Renderer } from "./webgl/renderer"; import { VertexBuffer, IndexBuffer } from "./webgl/buffer"; import { VertexBufferLayout, VertexArray } from "./webgl/vertexBufferLayout"; import { defaultLabelFactory, LabelFactory, Align, Label } from "./webgl/text"; import { VoronoiBuilder } from "./voronoi/voronoi"; import * as assets from "./assets"; +import { Texture } from "./webgl/texture"; function to_bbox(box: number[]): BBox { @@ -133,6 +134,7 @@ export class GameInstance { shader: Shader; vor_shader: Shader; image_shader: Shader; + masked_image_shader: Shader; text_factory: LabelFactory; planet_labels: Label[]; @@ -174,6 +176,7 @@ export class GameInstance { this.vor_shader = shaders["vor"].create_shader(GL, { PLANETS: "" + planets.length, }); + this.masked_image_shader = shaders["masked_image"].create_shader(GL); this.text_factory = defaultLabelFactory(GL, this.image_shader); this.planet_labels = []; @@ -234,45 +237,53 @@ export class GameInstance { } _create_planets(planets: Float32Array, meshes: Mesh[]) { + const earth = Texture.fromImage(GL, assets.earthPng, 'earth'); + for (let i = 0; i < this.planet_count; i++) { { const transform = new UniformMatrix3fv([ - 1, - 0, - 0, - 0, - 1, - 0, - -planets[i * 3], - -planets[i * 3 + 1], - 1, + 1, 0, 0, + 0, 1, 0, + -planets[i * 3], -planets[i * 3 + 1], 1, // TODO: why are negations needed? ]); - const indexBuffer = new IndexBuffer( - GL, - meshes[i % meshes.length].cells - ); - const positionBuffer = new VertexBuffer( - GL, - meshes[i % meshes.length].positions - ); - - const layout = new VertexBufferLayout(); - layout.push(GL.FLOAT, 3, 4, "a_position"); + const gl = GL; + const ib = new IndexBuffer(gl, [ + 0, 1, 2, + 1, 2, 3 + ]); + const vb_pos = new VertexBuffer(gl, [ + -1, 1, + 1, 1, + -1, -1, + 1, -1 + ]); + const vb_tex = new VertexBuffer(gl, [ + 0, 0, + 1, 0, + 0, 1, + 1, 1]); + + const layout_pos = new VertexBufferLayout(); + // 2? + layout_pos.push(gl.FLOAT, 2, 4, "a_position"); + + const layout_tex = new VertexBufferLayout(); + layout_tex.push(gl.FLOAT, 2, 4, "a_texCoord"); + const vao = new VertexArray(); - vao.addBuffer(positionBuffer, layout); - - this.renderer.addToDraw( - indexBuffer, - vao, - this.shader, - { - u_trans: transform, - u_trans_next: transform, - }, - [], - LAYERS.planet - ); + vao.addBuffer(vb_pos, layout_pos); + vao.addBuffer(vb_tex, layout_tex); + + const uniforms = { + u_trans: transform, + u_trans_next: transform, + }; + + const renderable = new DefaultRenderable(ib, vao, this.masked_image_shader, [earth], uniforms); + + this.renderer.addRenderable(renderable, LAYERS.planet); + } { @@ -430,25 +441,30 @@ export class GameInstance { this.use_vor = false; } + const shaders_to_update = [ + this.shader, + this.image_shader, + this.masked_image_shader, + ]; + + // If not playing, still reder with different viewbox, so people can still pan etc. if (!this.playing) { this.last_time = time; - this.shader.uniform( - GL, - "u_viewbox", - new Uniform4f(this.resizer.get_viewbox()) - ); + shaders_to_update.forEach((shader) => { + shader.uniform( + GL, + "u_viewbox", + new Uniform4f(this.resizer.get_viewbox()) + ); + }) + this.vor_shader.uniform( GL, "u_viewbox", new Uniform4f(this.resizer.get_viewbox()) ); - this.image_shader.uniform( - GL, - "u_viewbox", - new Uniform4f(this.resizer.get_viewbox()) - ); this.renderer.render(GL); return; @@ -481,39 +497,24 @@ export class GameInstance { this.vor_shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION)); this.vor_shader.uniform(GL, "u_vor", new UniformBool(this.use_vor)); - this.shader.uniform( - GL, - "u_time", - new Uniform1f((time - this.last_time) / ms_per_frame) - ); - this.shader.uniform( - GL, - "u_mouse", - new Uniform2f(this.resizer.get_mouse_pos()) - ); - this.shader.uniform( - GL, - "u_viewbox", - new Uniform4f(this.resizer.get_viewbox()) - ); - this.shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION)); - - this.image_shader.uniform( - GL, - "u_time", - new Uniform1f((time - this.last_time) / ms_per_frame) - ); - this.image_shader.uniform( - GL, - "u_mouse", - new Uniform2f(this.resizer.get_mouse_pos()) - ); - this.image_shader.uniform( - GL, - "u_viewbox", - new Uniform4f(this.resizer.get_viewbox()) - ); - this.image_shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION)); + shaders_to_update.forEach((shader) => { + shader.uniform( + GL, + "u_time", + new Uniform1f((time - this.last_time) / ms_per_frame) + ); + shader.uniform( + GL, + "u_mouse", + new Uniform2f(this.resizer.get_mouse_pos()) + ); + shader.uniform( + GL, + "u_viewbox", + new Uniform4f(this.resizer.get_viewbox()) + ); + shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION)); + }); // Render this.renderer.render(GL); @@ -616,6 +617,15 @@ export async function set_instance(source: string): Promise { assets.simpleVertexShader, ), ])(), + (async () => + <[string, ShaderFactory]>[ + "masked_image", + await ShaderFactory.create_factory( + assets.maskedImageFragmentShader, + assets.simpleVertexShader, + ), + ])(), + ]; let shaders_array: [string, ShaderFactory][]; [meshes, shaders_array] = await Promise.all([ diff --git a/web/pw-visualizer/src/webgl/index.ts b/web/pw-visualizer/src/webgl/index.ts index 1742713..8d785ef 100644 --- a/web/pw-visualizer/src/webgl/index.ts +++ b/web/pw-visualizer/src/webgl/index.ts @@ -57,8 +57,8 @@ async function main() { return; } + // TODO: do we still need this? const mesh = await url_to_mesh("static/res/images/earth.svg"); - console.log(Math.max(...mesh.positions), Math.min(...mesh.positions)); const renderer = new Renderer(); const factory = await ShaderFactory.create_factory(assets.simpleFragmentShader, assets.simpleVertexShader);