import nimgl/opengl
import imageman
type
texturePriv = object
texID: GLuint
data: Image[ColorRGBU]
texture* = ref texturePriv
proc loadTexture*(path: string): texture =
var image = loadImage[ColorRGBU](path)
new(result)
result.data = image
glGenTextures(1,result.texID.addr)
var er = glGetError()
if GL_NO_ERROR != er:
echo "Error", er.cint
glBindTexture(GL_TEXTURE_2D.GLenum, result.texID)
er = glGetError()
if GL_NO_ERROR != er:
echo "Błąd!", er.cint
glTexImage2D(GL_TEXTURE_2D.GLenum, 0.GLint, GL_RGB8.GLint, image.width.GLsizei, image.height.GLsizei, 0.GLint, GL_RGB.GLenum, GL_UNSIGNED_BYTE.GLenum, result.data.data[0].addr)
var er2 = glGetError()
if GL_NO_ERROR != er2:
echo "Błąd!", er2.cint
proc bindTexture*(a: texture) =
glBindTexture(GL_TEXTURE_2D.GLenum, a.texID)
The problem is I see a black screen
Of course, not.
import nimgl/[glfw, opengl]
import Screen
import ../../common/utils
import ../KnowledgeTheGamepkg/Windowing
import textures
import os
type
canvas* = object
r*,g*,b*,a*: float
txt*: texture
shape* = ref object
cnv: ptr canvas
vertBO, vertAO: GLuint
data: ptr UncheckedArray[cfloat]
dCount: cint
var
currCNV: ptr canvas
proc compileShader*(kind: GLenum, shader: cstring): GLuint =
var success: GLint
var sh = glCreateShader(kind)
glShaderSource(sh, 1.GLsizei, shader.unsafeAddr, nil)
glCompileShader(sh)
glGetShaderiv(sh, GL_COMPILE_STATUS, success.addr)
if 0 == success:
var i: GLsizei
var buf: array[4096, char]
glGetShaderInfoLog(sh, GLsizei(buf.len), i.addr, buf.unsafeAddr)
echo buf.unsafeAddr
quit(-1)
return sh
# Wydzielić metodę z rectangle i triangle
# Dodać kod, co sprawdzi czy płótno ma określoną teksturę.
# Jeśli tak, to dodać do tablicy koordynaty tesktury
# Koordynaty tekstury nie będą po koordynatach wierzchołku, tylko
# po zakończeniu tablicy koordynatów wierzchołków
proc createShape(Pcnv: ptr canvas,vertCount: int, texCords: openArray[cfloat]): shape =
var
vert: ptr UncheckedArray[cfloat]
vertBO, vertAO: GLuint
dasize: int = vertCount*cfloat.sizeof*2
haveTxt = nil != Pcnv and nil != Pcnv.txt
if haveTxt:
dasize += vertCount * cfloat.sizeof * 2
vert = cast[ptr UncheckedArray[cfloat]](allocShared(dasize))
new(result)
glGenVertexArrays(1, vertAO.addr)
glBindVertexArray(vertAO)
glGenBuffers(1, vertBO.addr)
result.cnv = Pcnv
result.vertBO = vertBO
result.vertAO = vertAO
result.data = cast [ptr UncheckedArray[cfloat]](vert[].unsafeAddr)
result.dCount = vertCount.cint*2
if haveTxt:
for i in 0..texCords.len()-1:
vert[vertCount*cfloat.sizeof*2+i*cfloat.sizeof] = texCords[i]
proc bindShape(sh: shape, glPos,glTexPos: int) =
glBindVertexArray(sh.vertAO)
glBindBuffer(GL_ARRAY_BUFFER, sh.vertBO)
glBufferData(GL_ARRAY_BUFFER, cfloat.sizeof * sh.dCount, sh.data[].addr, GL_STATIC_DRAW)
glEnableVertexAttribArray(glPos.GLuint)
glVertexAttribPointer(glPos.GLuint, 2.GLint, 0x1406.GLenum, GL_FALSE.GLboolean, 0.GLsizei, nil)
if nil != sh.cnv and nil != sh.cnv.txt:
glEnableVertexAttribArray(glTexPos.GLuint)
glVertexAttribPointer(glTexPos.GLuint, 2.GLint, 0x1406.GLenum, GL_FALSE.GLboolean, 0.GLsizei, cast[pointer](sh.dCount * cfloat.sizeof * 2))
proc rectangle*(Pcnv: ptr canvas, x,y,w,h, glPos,glTexPos: int): shape =
var cnv = Pcnv
if nil == cnv:
cnv = currCNV
var
x_1,y_1: float
x_2,y_2: float
d2_x2,d2_y2: int
vert: ptr UncheckedArray[cfloat]
sh: shape = createShape(Pcnv, 6, [-1.0.cfloat,1.0.cfloat,1.0.cfloat,1.0.cfloat,-1.0,-1.0,-1.0,-1.0,1.0.cfloat,-1.0.cfloat,1.0.cfloat,1.0.cfloat])
d2_x2 = x + w
d2_y2 = y + h
(x_1,y_1) = get2DNorPos(x, y)
(x_2,y_2) = get2DNorPos(d2_x2, d2_y2)
vert = sh.data
vert[0] = x_1
vert[1] = y_1
vert[2] = x_1
vert[3] = y_2
vert[4] = x_2
vert[5] = y_2
vert[6] = x_2
vert[7] = y_2
vert[8] = x_2
vert[9] = y_1
vert[10] = x_1
vert[11] = y_1
bindShape(sh, glPos, glTexPos)
return sh
proc triangle*(Pcnv: ptr canvas, x1,y1,x2,y2,x3,y3,glPos,glTexPos: int): shape =
var cnv = Pcnv
if nil == cnv:
cnv = currCNV
var
cx_1,cy_1: float
cx_2,cy_2: float
cx_3,cy_3: float
vert: ptr UncheckedArray[cfloat]
sh: shape = createShape(Pcnv, 3, [-1.0.cfloat,1.0.cfloat,1.0.cfloat,0.5.cfloat,-1.0.cfloat,-1.0.cfloat])
(cx_1,cy_1) = get2DNorPos(x1, y1)
(cx_2,cy_2) = get2DNorPos(x2, y2)
(cx_3,cy_3) = get2DNorPos(x3, y3)
vert = sh.data
vert[0] = cx_1
vert[1] = cy_1
vert[2] = cx_2
vert[3] = cy_2
vert[4] = cx_3
vert[5] = cy_3
bindShape(sh, glPos, glTexPos)
return sh
proc drawShape*(sh: shape) =
glBindVertexArray(sh.vertAO)
if nil != sh.cnv and nil != sh.cnv.txt:
bindTexture(sh.cnv.txt)
glDrawArrays(GL_TRIANGLES, 0.GLint, sh.dCount.GLsizei)
proc setCNVTexture*(cnv: var canvas; tex: texture) =
cnv.txt = tex
type
PaintProc = proc ()
PaintQueue = seq[PaintProc]
var
MPaintQueue: PaintQueue
proc repaintScreen* =
glClearColor(0.0,0.0,0.0,0.0)
glClear(GL_COLOR_BUFFER_BIT)
for a in MPaintQueue:
a()
draw()
proc addPaintProc*(a: PaintProc): int =
MPaintQueue.addRetIdx a
proc removePaintProc*(idx: int) =
MPaintQueue.delete idx
proc destroyShape*(sh: var shape) =
glDeleteBuffers(1.GLsizei, sh.vertBO.addr)
glDeleteVertexArrays(1.GLsizei, sh.vertAO.addr)
deallocShared(sh.data)
GC_unref(sh)
import os
import nimgl/[glfw, opengl]
import common
import ../KnowledgeTheGamepkg/Menu
var
vertexShader: cstring = r"""#version 150 core
in vec2 texcoord;
in vec2 position;
out vec2 Texcoord;
void main()
{
Texcoord = texcoord;
gl_Position = vec4(position, 0.0, 1.0);
}"""
fragmentShader: cstring = r"""#version 330 core
out vec4 FragColor;
in vec2 Texcoord;
uniform sampler2D tex;
void main()
{
FragColor = texture(tex, Texcoord);
}"""
shaderProgram: GLuint
proc menuElPaint*(btn: MenuObject) =
glUseProgram(shaderProgram)
if nil == btn.drCache:
btn.drCache = rectangle(nil, btn.x, btn.y, btn.width, btn.height, glGetAttribLocation(shaderProgram, "position"), glGetAttribLocation(shaderProgram, "texcoord"))
drawShape(btn.drCache)
proc initMenuGraphics*() =
var success: GLint
var vertexSh = compileShader(GL_VERTEX_SHADER, vertexShader)
var fragmentSh = compileShader(GL_FRAGMENT_SHADER, fragmentShader)
shaderProgram = glCreateProgram()
glAttachShader(shaderProgram, vertexSh)
glAttachShader(shaderProgram, fragmentSh)
glBindFragDataLocation(shaderProgram, 0, "FragColor")
glLinkProgram(shaderProgram)
glGetProgramiv(shaderProgram, GL_LINK_STATUS, success.addr)
if 0 == success:
var i: GLsizei
var buf: array[4096, char]
glGetProgramInfoLog(shaderProgram, 4096, i.addr, buf.unsafeAddr);
echo buf.unsafeAddr
#os.sleep 5000
glDeleteShader(vertexSh)
glDeleteShader(fragmentSh)
echo "Menu Intialized"
vert[vertCount*cfloat.sizeof*2+i*cfloat.sizeof]
if you index an UncheckedArray, it's indexed per element not per byte, so the multiplications by sizeof(cfloat) should be unnecessary.
Otherwise I can't really find anything wrong. If this doesn't fix the problem, I highly recommend checking out renderdoc <https://renderdoc.org/> for debugging problems like this.
1. I change Unchecked array index calculation method. It does not made program working as excepted. 2. result.dCount = vertCount.cint*2 . Multiply by 2 is necessary, because vertex shader operates and return on two dimensional space.
I change some files:
import os
import nimgl/[glfw, opengl]
import common
import ../KnowledgeTheGamepkg/Menu
var
vertexShader: cstring = r"""#version 150 core
in vec2 texcoord;
in vec2 position;
out vec2 Texcoord;
void main()
{
Texcoord = texcoord;
gl_Position = vec4(position, 0.0, 1.0);
}"""
fragmentShader: cstring = r"""#version 330 core
out vec4 FragColor;
in vec2 Texcoord;
uniform sampler2D tex;
void main()
{
FragColor = texture(tex, Texcoord);
}"""
shaderProgram: GLuint
proc getMMenuShaderProgram*: GLuint = shaderProgram
proc menuElPaint*(btn: MenuObject) =
glUseProgram(shaderProgram)
if nil == btn.drCache:
btn.drCache = rectangle(nil, btn.x, btn.y, btn.width, btn.height, glGetAttribLocation(shaderProgram, "position"), glGetAttribLocation(shaderProgram, "texcoord"))
drawShape(btn.drCache)
proc initMenuGraphics*() =
var success: GLint
var vertexSh = compileShader(GL_VERTEX_SHADER, vertexShader)
var fragmentSh = compileShader(GL_FRAGMENT_SHADER, fragmentShader)
shaderProgram = glCreateProgram()
glAttachShader(shaderProgram, vertexSh)
glAttachShader(shaderProgram, fragmentSh)
glBindFragDataLocation(shaderProgram, 0, "FragColor")
glLinkProgram(shaderProgram)
glGetProgramiv(shaderProgram, GL_LINK_STATUS, success.addr)
if 0 == success:
var i: GLsizei
var buf: array[4096, char]
glGetProgramInfoLog(shaderProgram, 4096, i.addr, buf.unsafeAddr);
echo buf.unsafeAddr
#os.sleep 5000
glDeleteShader(vertexSh)
glDeleteShader(fragmentSh)
echo "Menu Intialized"
And:
import ../Graphics/common
import ../Graphics/menu
import ../Graphics/textures
import NWMenu
import Menu
import State
import nimgl/[glfw, opengl]
import sequtils
import posix
var
# Main menu
newGBTN: MenuObject
loadWorldBTN: MenuObject
options: MenuObject
gameQuit: MenuObject
background: shape
proc paintMenu()
iterator getMenuEls: MenuObject =
yield newGBTN
yield loadWorldBTN
yield options
yield gameQuit
proc test1(event: MEvents; a: MenuObject) =
initNWMenu()
proc gameQuitClicked(event: MEvents; a: MenuObject) =
setCurrClientState StateEnded
proc initMenu* =
var ev: MEventHandler
var cnv: ptr canvas
if nil == background:
var shaderProgram = getMMenuShaderProgram()
cnv = cast[ptr canvas](allocShared(canvas.sizeof))
cnv.r = 0
cnv.g = 0
cnv.b = 0
cnv.a = 0
cnv.txt = loadTexture("./Assets/Graphics/UI/Menu/bg.png")
#cnv.txt = loadTexture("/usr/share/icons/oxygen/64x64/apps/yast.png")
background = rectangle(cnv, 0, 0, 800, 600,glGetAttribLocation(shaderProgram, "position"), glGetAttribLocation(shaderProgram, "texcoord"))
new(newGBTN)
newGBTN.x = 10
newGBTN.y = 10
newGBTN.width = 200
newGBTN.height = 30
ev.events = {Click}
ev.handler = test1
newGBTN.handlers.add ev
new MenuObject(loadWorldBTN)
loadWorldBTN.x = 10
loadWorldBTN.y = 50
loadWorldBTN.width = 200
loadWorldBTN.height = 30
new MenuObject(options )
options.x = 10
options.y = 100
options.width = 200
options.height = 30
new MenuObject(gameQuit)
gameQuit.x = 10
gameQuit.y = 150
gameQuit.width = 200
gameQuit.height = 30
ev.events = {Click}
ev.handler = gameQuitClicked
gameQuit.handlers.add ev
discard addPaintProc paintMenu
setActiveMenuItems toSeq(getMenuEls())
proc paintMenu() =
glUseProgram(getMMenuShaderProgram())
drawShape background
for a in getMenuEls():
menuElPaint a
Now, when change FragmentShader as demotomohiro's suggested, I see big red rectangle (fill whole screen). When using FragmentShader changes without previous changes, only red buttons are displayed.
Maybe should I load shaderProgram before texture loading?
Reading manual/reference/specification of the API is the only way to use it correctly and smoothly.
Reading these things might take long time, but that would not take longer time than asking a question on the internet and wait for someone kindly read your code and find out the problem everytime you encounter a problem.
This page has links to each version of opengl specification: https://www.khronos.org/registry/OpenGL/index_gl.php
Here is opengl wiki: https://www.khronos.org/opengl/wiki/