From ac8fdaf0224300dfae4c906e4fca46aa3bea98d1 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Thu, 4 Apr 2024 00:23:44 +0300 Subject: [PATCH] change: use opengl --- folx.nimble | 3 + src/explorer.nim | 12 +- src/folx.nim | 22 ++- src/graphics/gl.nim | 223 ++++++++++++++++++++++++++ src/graphics/globals.nim | 3 + src/graphics/shaders.nim | 200 +++++++++++++++++++++++ src/graphics/shaderutils.nim | 54 +++++++ src/render.nim | 302 ++++++++++++++++++++++++----------- src/side_explorer.nim | 11 +- src/status_bar.nim | 5 +- src/text_editor.nim | 6 +- src/title.nim | 11 +- src/welcome.nim | 4 +- 13 files changed, 731 insertions(+), 125 deletions(-) create mode 100644 src/graphics/gl.nim create mode 100644 src/graphics/globals.nim create mode 100644 src/graphics/shaders.nim create mode 100644 src/graphics/shaderutils.nim diff --git a/folx.nimble b/folx.nimble index 46a1fd2..437c76a 100644 --- a/folx.nimble +++ b/folx.nimble @@ -10,6 +10,9 @@ bin = @["folx"] requires "nim >= 2.0.2" requires "siwin ^= 0.8.4.6", "jsony", "pixie", "fusion", "winim" requires "cligen" +requires "shady ^= 0.1.3" # writing shaders in nim instead of glsl +requires "fusion ^= 1.2" # pattern matching and ast dsl +requires "opengl ^= 1.2.9" let dataDir = diff --git a/src/explorer.nim b/src/explorer.nim index 671f90a..27b7965 100644 --- a/src/explorer.nim +++ b/src/explorer.nim @@ -188,8 +188,8 @@ component Disk {.noexport.}: ) if selected: - Rect: color = colorTheme.bgSelection - Rect(w = 2): color = colorTheme.bgSelectionLabel + Rect: color = colorTheme.bgSelection.color + Rect(w = 2): color = colorTheme.bgSelectionLabel.color Text disk(x = 10): color = colorTheme.cActive @@ -207,14 +207,16 @@ component File {.noexport.}: let dy = round(glyphTableStack[^1].font.size * 1.27) if selected: - Rect: color = colorTheme.bgSelection - Rect(w = 2): color = colorTheme.bgSelectionLabel + Rect: color = colorTheme.bgSelection.color + Rect(w = 2): color = colorTheme.bgSelectionLabel.color let bg = if selected: colorTheme.bgSelection else: bg - contextStack[^1].image.draw(getIcon(file), translate(parentBox.xy + vec2(20, 4)) * scale(vec2(0.06 * dy, 0.06 * dy))) + let icon = getIcon(file) + Image icon(x = 20, y = 4, w = icon.width.float * 0.06 * dy, h = icon.height.float * 0.06 * dy): + discard Text (file.name & file.ext)(x = 40, w = 200, clip = true): color = colorTheme.cActive diff --git a/src/folx.nim b/src/folx.nim index b44c86f..c148e0e 100644 --- a/src/folx.nim +++ b/src/folx.nim @@ -1,6 +1,7 @@ import sequtils, os, times, math, unicode, std/monotimes, options -import cligen +import pkg/[cligen, opengl] import gui, configuration, git, text, text_editor, side_explorer, explorer, title, status_bar, welcome +import ./graphics/[globals, shaders, shaderutils] proc contains*(b: Rect, a: GVec2): bool = let a = a.vec2 @@ -30,13 +31,17 @@ proc folx(files: seq[string] = @[], workspace: string = "", preferWorkFolderReso else: files[0].splitPath.head if preferWorkFolderResources: configuration.workFolderResources() - - let window = newSoftwareRenderingWindow( + + let window = newOpenglWindow( title="folx", size=config.window.size, frameless = if config.window.customTitleBar: true else: false, transparent = if config.window.customTitleBar: true else: false, ) + loadExtensions() + + global_drawContext = newDrawContext() + var editor_gt = readFont(rc config.font).newGlyphTable(config.fontSize) interface_gt = readFont(rc config.interfaceFont).newGlyphTable(config.interfaceFontSize) @@ -215,12 +220,17 @@ proc folx(files: seq[string] = @[], workspace: string = "", preferWorkFolderReso window.eventsHandler.onRender = proc(e: RenderEvent) = image.clear colorTheme.bgTextArea.color.rgbx setCursor Cursor(kind: builtin, builtin: BuiltinCursor.arrow) + + glClearColor colorTheme.bgTextArea.color.r, colorTheme.bgTextArea.color.g, colorTheme.bgTextArea.color.b, colorTheme.bgTextArea.color.a + glClear(GL_COLOR_BUFFER_BIT) + + let size = e.window.size + glViewport 0, 0, size.x.GLsizei, size.y.GLsizei + global_drawContext.updateSizeRender(size) frame(w = window.size.x, h = window.size.y, clip=true): - withContext r: - handleFolx() + handleFolx() - window.drawImage image.data.toBgrx, ivec2(image.width.int32, image.height.int32) window.cursor = globalCursor isLeftClick = false isLeftDown = false diff --git a/src/graphics/gl.nim b/src/graphics/gl.nim new file mode 100644 index 0000000..a64cfb4 --- /dev/null +++ b/src/graphics/gl.nim @@ -0,0 +1,223 @@ +import vmath, opengl, pixie + +when (compiles do: import imageman): + import imageman + const hasImageman* = true +else: + const hasImageman* = false + +export vmath, opengl + + +type + Buffers* = ref BuffersObj + BuffersObj = object + n: int32 + obj: UncheckedArray[GlUint] + + VertexArrays* = ref VertexArraysObj + VertexArraysObj = object + n: int32 + obj: UncheckedArray[GlUint] + + Textures* = ref TexturesObj + ## note: texures can't be actualy deleted for now. glDeleteTextures is not enough. If you want to resize texture, use loadTexture multiple times instead. + TexturesObj = object + n: int32 + obj: UncheckedArray[GlUint] + + Shader* = ref ShaderObj + ShaderObj = object + obj: GlUint + + FrameBuffers* = ref FrameBuffersObj + FrameBuffersObj = object + n: int32 + obj: UncheckedArray[GlUint] + + ShaderCompileDefect* = object of Defect + + Shape* = ref object + kind: GlEnum + len: int + vao: VertexArrays + bo: Buffers + + OpenglUniform*[T] = distinct GlInt + + +# -------- Buffers, VertexArrays, Textures -------- +template makeOpenglObjectSeq(t, tobj, T, gen, del, newp, delextra) = + proc `=destroy`(xobj {.inject.}: tobj) = + delextra + del(xobj.n, cast[ptr T](xobj.obj.addr)) + + proc newp*(n: int): t = + if n == 0: return + assert n in 1..int32.high + unsafeNew result, int32.sizeof + n * T.sizeof + result.n = n.int32 + gen(n.int32, cast[ptr T](result.obj.addr)) + + proc len*(x: t): int = + if x == nil: 0 + else: x.n + + proc `[]`*(x: t, i: int): T = + if i notin 0.. size.x - radius and pos.y < radius: + let d = length(pos - vec2(size.x - radius, radius)) + return (radius - d + 0.5).max(0).min(1) + + elif pos.x < radius and pos.y > size.y - radius: + let d = length(pos - vec2(radius, size.y - radius)) + return (radius - d + 0.5).max(0).min(1) + + elif pos.x > size.x - radius and pos.y > size.y - radius: + let d = length(pos - vec2(size.x - radius, size.y - radius)) + return (radius - d + 0.5).max(0).min(1) + + return 1 + + +proc updateSizeRender*(ctx: var DrawContext, size: IVec2) = + # update size + ctx.px = vec2(2'f32 / size.x.float32, 2'f32 / size.y.float32) + ctx.wh = ivec2(size.x, -size.y).vec2 / 2 diff --git a/src/render.nim b/src/render.nim index 9e7cba1..51458aa 100644 --- a/src/render.nim +++ b/src/render.nim @@ -1,20 +1,21 @@ import tables, unicode, sequtils import pixie, markup +import ./graphics/[gl, shaders, shaderutils, globals] export markup type GlyphTable* = ref object ## table of pre-renered text images font*: Font - glyphs: Table[tuple[c: Rune; fg, bg: ColorRgb], Glyph] + glyphs: Table[tuple[c: Rune], Glyph] Glyph = object size: IVec2 - data: seq[ColorRgbx] + data: Textures var - contextStack*: seq[Context] + # contextStack*: seq[Context] glyphTableStack*: seq[GlyphTable] @@ -23,10 +24,10 @@ template withGlyphTable*(x: GlyphTable, body) = block: body glyphTableStack.del glyphTableStack.high -template withContext*(x: Context, body) = - contextStack.add x - block: body - contextStack.del contextStack.high +# template withContext*(x: Context, body) = +# contextStack.add x +# block: body +# contextStack.del contextStack.high proc newGlyphTable*(font: Font, fontSize: float32): GlyphTable = @@ -53,78 +54,122 @@ component Image {.noexport.}: # image (unscaled) proc handle( g: Glyph, - srcPos: IVec2 = ivec2(0, 0), + color: Color, ) - let - pos1 = parentBox.xy.ivec2 - size1 = parentBox.wh.ivec2 - r = contextStack[^1].image - - # clip - let pos2 = ivec2(max(-pos1.x, 0), max(-pos1.y, 0)) + srcPos - let pos = pos1 + pos2 - let size = ivec2( - (size1.x - srcPos.x).min(g.size.x - pos2.x).min(r.width.int32 - pos.x), - (size1.y - srcPos.y).min(g.size.y - pos2.y).min(r.height.int32 - pos.y) - ) - if size.x <= 0 or size.y <= 0: return + let shader = global_drawContext.makeShader: + proc vert( + gl_Position: var Vec4, + pos: var Vec2, + uv: var Vec2, + ipos: Vec2, + transform: Uniform[Mat4], + size: Uniform[Vec2], + px: Uniform[Vec2], + ) = + transformation(gl_Position, pos, size, px, ipos, transform) + uv = ipos + + proc frag( + glCol: var Vec4, + pos: Vec2, + uv: Vec2, + size: Uniform[Vec2], + color: Uniform[Vec4], + clipRectXy: Uniform[Vec2], + clipRectWh: Uniform[Vec2], + ) = + let c = gltex.texture(uv) + glCol = vec4(c.rgb, c.a) * vec4(color.rgb * color.a, color.a) + if ( + pos.x < clipRectXy.x or pos.x > (clipRectXy.x + clipRectWh.x) or + pos.y < clipRectXy.y or pos.y > (clipRectXy.y + clipRectWh.y) + ): + glCol = vec4(0, 0, 0, 0) + + if g.data == nil: return + + glEnable(GlBlend) + glBlendFuncSeparate(GlOne, GlOneMinusSrcAlpha, GlOne, GlOne) + + glBindTexture(GlTexture2d, g.data[0]) + + use shader.shader + global_drawContext.passTransform(shader, pos=parentBox.xy.ivec2.vec2, size=g.size.vec2) + shader.color.uniform = color.vec4 + if clipStack.len > 0: + shader.clipRectXy.uniform = clipStack[^1].xy - parentBox.xy + shader.clipRectWh.uniform = clipStack[^1].wh + else: + shader.clipRectXy.uniform = boxStack[0].xy - parentBox.xy + shader.clipRectWh.uniform = boxStack[0].wh + + draw global_drawContext.rect + + glBindTexture(GlTexture2d, 0) + + glDisable(GlBlend) - # draw - for y in 0.. (clipRectXy.x + clipRectWh.x) or + pos.y < clipRectXy.y or pos.y > (clipRectXy.y + clipRectWh.y) + ): + glCol = vec4(0, 0, 0, 0) + + + glEnable(GlBlend) + glBlendFuncSeparate(GlOne, GlOneMinusSrcAlpha, GlOne, GlOne) + + let tex = newTextures(1) + tex[0].loadTexture g + glBindTexture(GlTexture2d, tex[0]) + + use shader.shader + global_drawContext.passTransform(shader, pos=parentBox.xy, size=parentBox.wh) + shader.color.uniform = color.vec4 + if clipStack.len > 0: + shader.clipRectXy.uniform = clipStack[^1].xy - parentBox.xy + shader.clipRectWh.uniform = clipStack[^1].wh + else: + shader.clipRectXy.uniform = boxStack[0].xy - parentBox.xy + shader.clipRectWh.uniform = boxStack[0].wh + + draw global_drawContext.rect - # draw - for y in 0..