diff --git a/internal/game/logic.go b/internal/game/logic.go index 7cf27ea..fb0a998 100644 --- a/internal/game/logic.go +++ b/internal/game/logic.go @@ -145,6 +145,12 @@ func (g *Game) Init(win *glfw.Window) { g.fullscreen = true } + win.MakeContextCurrent() + glfw.SwapInterval(1) + } + + if key == glfw.KeyF3 { + io.ShowDebugInfo = !io.ShowDebugInfo } } }) @@ -225,6 +231,8 @@ func (g *Game) Update(win *glfw.Window, delta time.Duration) { g.player.Update(g.world, delta) g.view.LookAt(g.player.EyePosition().ToFloat32(), g.rotY, itype.Degrees(g.rotZ)) + io.ViewPos = g.player.EyePosition() + io.ViewDir = itype.Vec3d(mgl64.Rotate3DY(float64(g.rotY.Radians())).Mul3(mgl64.Rotate3DZ(float64(itype.Degrees(g.rotZ)))).Mul3x1(mgl64.Vec3{1, 0, 0})) render.Framewire.PushBox(g.player.WorldHitbox().ToFloat32(), color.White) diff --git a/internal/io/io.go b/internal/io/io.go index d714800..fd44407 100644 --- a/internal/io/io.go +++ b/internal/io/io.go @@ -4,4 +4,10 @@ import "edgaru089.ml/go/gl01/internal/util/itype" var ( DisplaySize itype.Vec2i // Size of the window viewport in pixels. + + // Directions are not always normalized. + ViewPos, ViewDir itype.Vec3d // Position and Direction of the player view. + RenderPos, RenderDir itype.Vec3d // Position and Direction of view for the current render pass. Might be different for e.g. lighting passes + + ShowDebugInfo bool // Show debug info (F3 screen)? ) diff --git a/internal/render/drawtexture.frag b/internal/render/drawtexture.frag new file mode 100644 index 0000000..f3d0938 --- /dev/null +++ b/internal/render/drawtexture.frag @@ -0,0 +1,23 @@ +#version 330 + +#define DrawTextureChannels_R (1<<0) +#define DrawTextureChannels_G (1<<1) +#define DrawTextureChannels_B (1<<2) +#define DrawTextureChannels_A (1<<3) + +uniform float valueMin, valueMax; +uniform int channels; + +uniform sampler2D tex; + +in vec2 fragVert; +in vec2 fragTexCoord; + +out vec4 outputColor; + + +void main() { + + outputColor = vec4(0, 0, 0, 1); + +} diff --git a/internal/render/drawtexture.vert b/internal/render/drawtexture.vert new file mode 100644 index 0000000..409dd2b --- /dev/null +++ b/internal/render/drawtexture.vert @@ -0,0 +1,17 @@ +#version 330 + +uniform vec2 onScreenPos, onScreenSize; + +layout (location = 0) in vec2 vert; +layout (location = 1) in vec2 texCoord; + +out vec2 fragVert; +out vec2 fragTexCoord; + + +void main() { + gl_Position = vec4(onScreenPos + onScreenSize * texCoord, 1, 0); + fragVert = texCoord * 2 - vec2(1, 1); + fragTexCoord = texCoord; +} + diff --git a/internal/render/render_world.go b/internal/render/render_world.go index d587cc6..d225c24 100644 --- a/internal/render/render_world.go +++ b/internal/render/render_world.go @@ -138,6 +138,8 @@ func (r *WorldRenderer) ResizeDisplay(newSize itype.Vec2i) { var sun = [3]float32{0.2, 0.4, 0.3} func (r *WorldRenderer) Render(world *world.World, view *View) { + io.RenderPos = io.ViewPos + io.RenderDir = io.ViewDir // re-generate the G-buffers if the display size changed if r.lastDisplaySize != io.DisplaySize { @@ -169,12 +171,18 @@ func (r *WorldRenderer) Render(world *world.World, view *View) { lightProjection := mgl32.Ortho(-50, 50, -50, 50, 1, 100) lightspace := lightProjection.Mul4(lightView) + io.RenderPos = lightPos.ToFloat64() + io.RenderDir = view.EyePos.Add(lightPos.Negative()).ToFloat64() + r.depthmap.shader.UseProgram() r.depthmap.shader.SetUniformMat4("lightspace", lightspace) world.Render() // 2. Geometry pass, render to G-buffer + io.RenderPos = io.ViewPos + io.RenderDir = io.ViewDir + gl.Viewport(0, 0, int32(r.lastDisplaySize[0]), int32(r.lastDisplaySize[1])) gl.BindFramebuffer(gl.FRAMEBUFFER, r.gbuffer.fbo) gl.ClearColor(0, 0, 0, 1) @@ -202,5 +210,11 @@ func (r *WorldRenderer) Render(world *world.World, view *View) { r.shader.SetUniformVec4f("fogColor", itype.Vec4f{0.6, 0.8, 1.0, 1.0}) r.shader.SetUniformVec3f("sun", normalSun) - r.drawScreenQuad() + DrawScreenQuad() + + // Show G-buffers? + /*if io.ShowDebugInfo { + DrawTexture(r.gbuffer.pos, itype.Rectf{0.5, 0.5, 0.5, 0.5}, DrawTextureChannels_R|DrawTextureChannels_G|DrawTextureChannels_B, 0, 32) + DrawTexture(r.gbuffer.depth, itype.Rectf{0.5, 0, 0.5, 0.5}, DrawTextureChannels_R, 0, 1) + }*/ } diff --git a/internal/render/render_world_helper.go b/internal/render/render_world_helper.go deleted file mode 100644 index bf40145..0000000 --- a/internal/render/render_world_helper.go +++ /dev/null @@ -1,54 +0,0 @@ -package render - -import ( - "unsafe" - - "github.com/go-gl/gl/all-core/gl" -) - -var ( - screenQuadVerts = []float32{ - -1, -1, - 0, 0, - 1, -1, - 1, 0, - 1, 1, - 1, 1, - -1, -1, - 0, 0, - 1, 1, - 1, 1, - -1, 1, - 0, 1, - } - screenQuadVAO uint32 - screenQuadVBO uint32 -) - -func initScreenQuad() { - gl.GenBuffers(1, &screenQuadVBO) - gl.BindBuffer(gl.ARRAY_BUFFER, screenQuadVBO) - gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(float32(0)))*len(screenQuadVerts), gl.Ptr(screenQuadVerts), gl.STATIC_DRAW) - - gl.GenVertexArrays(1, &screenQuadVAO) - gl.BindVertexArray(screenQuadVAO) - - gl.VertexAttribPointer(0, 2, gl.FLOAT, false, int32(4*unsafe.Sizeof(float32(0))), gl.PtrOffset(0)) - gl.VertexAttribPointer(1, 2, gl.FLOAT, false, int32(4*unsafe.Sizeof(float32(0))), gl.PtrOffset(int(2*unsafe.Sizeof(float32(0))))) - gl.EnableVertexAttribArray(0) - gl.EnableVertexAttribArray(1) -} - -// drawScreenQuad draws a Quad covering the entire screen. -// -// Attribute: vert: [ -1.0 --> 1.0] -func (r *WorldRenderer) drawScreenQuad() { - if screenQuadVAO == 0 { - initScreenQuad() - } - - gl.Disable(gl.DEPTH_TEST) - gl.Disable(gl.CULL_FACE) - gl.BindVertexArray(screenQuadVAO) - gl.DrawArrays(gl.TRIANGLES, 0, 6) -} diff --git a/internal/render/screenquad.go b/internal/render/screenquad.go new file mode 100644 index 0000000..bfe494c --- /dev/null +++ b/internal/render/screenquad.go @@ -0,0 +1,122 @@ +package render + +import ( + _ "embed" + "unsafe" + + "edgaru089.ml/go/gl01/internal/util/itype" + "github.com/go-gl/gl/all-core/gl" +) + +var ( + screenQuadVerts = []float32{ + -1, -1, + 0, 0, + 1, -1, + 1, 0, + 1, 1, + 1, 1, + -1, -1, + 0, 0, + 1, 1, + 1, 1, + -1, 1, + 0, 1, + } + screenQuadVAO uint32 + screenQuadVBO uint32 + + drawTextureShader *Shader // Shader for dumping textures to screen +) + +func initScreenQuad() { + gl.GenBuffers(1, &screenQuadVBO) + gl.BindBuffer(gl.ARRAY_BUFFER, screenQuadVBO) + gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(float32(0)))*len(screenQuadVerts), gl.Ptr(screenQuadVerts), gl.STATIC_DRAW) + + gl.GenVertexArrays(1, &screenQuadVAO) + gl.BindVertexArray(screenQuadVAO) + + gl.VertexAttribPointer(0, 2, gl.FLOAT, false, int32(4*unsafe.Sizeof(float32(0))), gl.PtrOffset(0)) + gl.VertexAttribPointer(1, 2, gl.FLOAT, false, int32(4*unsafe.Sizeof(float32(0))), gl.PtrOffset(int(2*unsafe.Sizeof(float32(0))))) + gl.EnableVertexAttribArray(0) + gl.EnableVertexAttribArray(1) +} + +// DrawScreenQuad draws a Quad covering the entire screen with the current binding shader. +// +// Attribute: (location=0) vert: [ -1.0 --> 1.0] +// (location=1) texCoord: [ 0 --> 1] +func DrawScreenQuad() { + if screenQuadVAO == 0 { + initScreenQuad() + } + + gl.Disable(gl.DEPTH_TEST) + gl.Disable(gl.CULL_FACE) + gl.BindVertexArray(screenQuadVAO) + gl.DrawArrays(gl.TRIANGLES, 0, 6) +} + +// DrawTextureChannels specifies the channels in RGBA to be drawn. +type DrawTextureChannels int + +const ( + DrawTextureChannels_R DrawTextureChannels = 1 << iota + DrawTextureChannels_G + DrawTextureChannels_B + DrawTextureChannels_A +) + +var ( + //go:embed drawtexture.vert + drawTextureShaderVert string + //go:embed drawtexture.frag + drawTextureShaderFrag string +) + +func initDrawTexture() { + var err error + drawTextureShader, err = NewShader(drawTextureShaderVert, drawTextureShaderFrag) + if err != nil { + panic(err) + } + + gl.BindFragDataLocation(drawTextureShader.Handle(), 0, gl.Str("outputColor\x00")) +} + +// DrawTexture dumps the contents of a Texture onto the portion of the screen. +// It disables depth test and cull face. +// +// onScreen is specified in [-1, 1] size. +// +// If only one channel is specified, that channel is drawn in black and white. +// Otherwise, R/G/B are drawn in their specified channels and A is ignored. +// +// The texture values from vauleRange is mapped to [0, 1] linearly. +// +// TODO: This does not work. Maybe something wrong with the shaders or blending +func DrawTexture(texture uint32, onScreen itype.Rectf, channels DrawTextureChannels, valueMin, valueMax float32) { + if screenQuadVAO == 0 { + initScreenQuad() + } + if drawTextureShader == nil { + initDrawTexture() + } + + drawTextureShader.UseProgram() + + drawTextureShader.SetUniformVec2f("onScreenPos", onScreen.MinPoint()) + drawTextureShader.SetUniformVec2f("onScreenSize", onScreen.Size()) + drawTextureShader.SetUniformFloat("valueMin", valueMin) + drawTextureShader.SetUniformFloat("valueMax", valueMax) + drawTextureShader.SetUniformInt("channels", int32(channels)) + drawTextureShader.SetUniformTextureHandle("tex", texture) + + drawTextureShader.BindTextures() + + gl.Disable(gl.DEPTH_TEST) + gl.Disable(gl.CULL_FACE) + gl.BindVertexArray(screenQuadVAO) + gl.DrawArrays(gl.TRIANGLES, 0, 6) +} diff --git a/internal/render/texture.go b/internal/render/texture.go index e00fa1a..05e2326 100644 --- a/internal/render/texture.go +++ b/internal/render/texture.go @@ -66,6 +66,11 @@ func NewTextureRGBA(image *image.RGBA) *Texture { return &Texture{tex: tex} } +// NewTextureFromHandle creates a new *Texture from an existing OpenGL handle. +func NewTextureFromHandle(handle uint32) *Texture { + return &Texture{tex: handle} +} + // updateFilters updates the MIN/MAG_FILTER parameters of the texture based on t.smooth and t.hasMipmap. // // It does not bind the texture; the caller has to do that diff --git a/internal/util/itype/rect.go b/internal/util/itype/rect.go index ec28189..bb40b55 100644 --- a/internal/util/itype/rect.go +++ b/internal/util/itype/rect.go @@ -14,12 +14,36 @@ type Rectf struct { Width, Height float32 } +func (r Rectf) MinPoint() Vec2f { + return Vec2f{r.Left, r.Top} +} + +func (r Rectf) MaxPoint() Vec2f { + return Vec2f{r.Left + r.Width, r.Top + r.Height} +} + +func (r Rectf) Size() Vec2f { + return Vec2f{r.Width, r.Height} +} + // Rectd is a 2D rectangle with float64 coordinates. type Rectd struct { Left, Top float64 Width, Height float64 } +func (r Rectd) MinPoint() Vec2d { + return Vec2d{r.Left, r.Top} +} + +func (r Rectd) MaxPoint() Vec2d { + return Vec2d{r.Left + r.Width, r.Top + r.Height} +} + +func (r Rectd) Size() Vec2d { + return Vec2d{r.Width, r.Height} +} + // Boxi is a 3D box with int coordinates. type Boxi struct { OffX, OffY, OffZ int diff --git a/internal/util/itype/vec.go b/internal/util/itype/vec.go index 877d340..043e957 100644 --- a/internal/util/itype/vec.go +++ b/internal/util/itype/vec.go @@ -79,6 +79,10 @@ func (v Vec2f) ToFloat64() Vec2d { // Vec3f is a three-element float vector type Vec3f [3]float32 +func (v Vec3f) Negative() Vec3f { + return Vec3f{-v[0], -v[1], -v[2]} +} + func (v Vec3f) Add(add Vec3f) Vec3f { return Vec3f{v[0] + add[0], v[1] + add[1], v[2] + add[2]} } diff --git a/internal/world/render.go b/internal/world/render.go index c1f6c9d..06c8cc6 100644 --- a/internal/world/render.go +++ b/internal/world/render.go @@ -6,6 +6,7 @@ import ( "unsafe" "edgaru089.ml/go/gl01/internal/asset" + "edgaru089.ml/go/gl01/internal/io" "edgaru089.ml/go/gl01/internal/util/itype" "github.com/go-gl/gl/all-core/gl" ) @@ -67,14 +68,45 @@ func (c *Chunk) updateRender() { select { case vert := <-c.vertUpdate: - log.Printf("Chunk [%d,%d]: received len=%d", c.X, c.Z, len(vert)) gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(Vertex{}))*len(vert), gl.Ptr(vert), gl.DYNAMIC_DRAW) c.vbolen = len(vert) default: // do nothing } } +var checkViewOffset = []itype.Vec3d{ + {0, 0, 0}, + {ChunkSizeX, 0, 0}, + {0, 0, ChunkSizeZ}, + {ChunkSizeX, 0, ChunkSizeZ}, + {0, ChunkSizeY, 0}, + {ChunkSizeX, ChunkSizeY, 0}, + {0, ChunkSizeY, ChunkSizeZ}, + {ChunkSizeX, ChunkSizeY, ChunkSizeZ}, +} + +// checkView checks if the chunk is in the front-facing direction of the view. +func (c *Chunk) checkView(from, facing itype.Vec3d) bool { + off := itype.Vec3d{ + float64(c.X * ChunkSizeX), + 0, + float64(c.Z * ChunkSizeZ), + } + + for _, check := range checkViewOffset { + ok := off.Add(check).Add(from.Negative()).Dot(facing) > 0 + if ok { + return true + } + } + return false +} + func (c *Chunk) Render() { + if !c.checkView(io.ViewPos, io.ViewDir) || !c.checkView(io.RenderPos, io.RenderDir) { + return + } + c.Bind() c.updateRender() gl.DrawArrays(gl.TRIANGLES, 0, int32(c.vbolen))