zacharycarter / zengine

2D | 3D Game development library
157 stars 13 forks source link

debug draw for Entity3D #41

Closed define-private-public closed 6 years ago

define-private-public commented 6 years ago

It would be nice if I could have a proc that will render some debug-drawing of Entity3D. Mostly something to visualize the position and orientation values.

I'm also not 100% sure if I set the orientation to the correct value. I wanted it to be pointing down towards the user.

define-private-public commented 6 years ago

I'm working on this right now (it will help with debugging stuff down the road). Currently I'm trying to work on that "line segment," part. This is what I've got right now:

https://github.com/define-private-public/zengine/blob/cda15c5fa3cbdc36847db6ee57b63812673a39d8/src/zengine/main.nim#L8

But it doesn't seem to be working right. The debug code I have should be rotating that blue box (which points in Z+) 15 degrees about the Y+ axis. It's doing nothing. What am I doing wrong here?

zacharycarter commented 6 years ago

I need to push up a commit to fix this. Please ping me in gitter / irc and we can talk through the fix.

define-private-public commented 6 years ago

I saw your latest commit. Is this good now?

define-private-public commented 6 years ago

Okay, I can now rotate the cube, but that's only using the tuple parameter. But why Can't I push matricies on top first before drawing the cube? They don't seem to be applied to the model.

define-private-public commented 6 years ago

The same seems to happen w/ zglTranslatef() calls. I've tried putting them before and after the drawCube call.

I'm not that familiar with the old fixed function pipeline for OpenGL. I understand that ZGL is trying to emulate this.

zacharycarter commented 6 years ago

Sorry I'm battling a cold.

drawCubes and other primitive drawing operations push matrices onto the stack already, so you shouldn't push a matrix onto the stack and then call one of these operations.

The basic idea is you push a matrix onto the stack, make whatever transformation calls you want and then issue draw calls for that matrix, then pop it off. When you call zglEnd, the matrix will be applied to all the draw calls.

Here's an example -

# Minimal example to open up a zengine window

import zengine, sdl2, opengl, glm

type
  Entity3D = object
    position: Vec3f
    origin: Vec3f
    orientation: Quatf

var rot = 0.0

proc newEntity3D(): Entity3D =
  result = Entity3D()

# TODO colors (default)
# Does a debug drawi of the `Entity3D` object's parameters
proc debugDraw(self: Entity3D; levelOfDetail: int=10)=
  # TODO is the origin and position stuff correct?
  drawsphereWires(self.position, 0.15, levelOfDetail, levelOfDetail, WHITE)
  drawsphereWires(self.position + self.origin, 0.1, levelOfDetail, levelOfDetail, RED)

  # drawCube(self.position, (rot, 0.0, 1.0, 0.0), 0.1, 0.1, 1.0, BLUE) # don't call this here as you already pushed a matrix onto the stack

  let 
    x = self.position.x
    y = self.position.y
    z = self.position.z
    width, height, length = 1.0
    color = BLUE

  zglPushMatrix() # push a matrix onto the stack

  # drawCube(self.position, (rot, 0.0, 1.0, 0.0), 0.1, 0.1, 1.0, BLUE) # don't call this here as you already pushed a matrix onto the stack - it does the same thing

  # perform whatever transformations you want here
  zglTranslatef(0.5, 1.5, 0.5)
  zglRotatef(rot, 0, 1, -1)

  zglBegin(DrawMode.ZGLTriangles)
  zglColor4ub(color.r, color.g, color.b, color.a)

  # ZGL Draw calls here
  # Front Face
  zglVertex3f(x-width/2, y-height/2, z+length/2)
  zglVertex3f(x+width/2, y-height/2, z+length/2)
  zglVertex3f(x-width/2, y+height/2, z+length/2)

  zglVertex3f(x+width/2, y+height/2, z+length/2)
  zglVertex3f(x-width/2, y+height/2, z+length/2)
  zglVertex3f(x+width/2, y-height/2, z+length/2)

  # Back Face
  zglVertex3f(x-width/2, y-height/2, z-length/2)
  zglVertex3f(x-width/2, y+height/2, z-length/2)
  zglVertex3f(x+width/2, y-height/2, z-length/2)

  zglVertex3f(x+width/2, y+height/2, z-length/2)
  zglVertex3f(x+width/2, y-height/2, z-length/2)
  zglVertex3f(x-width/2, y+height/2, z-length/2)

  # Top Face
  zglVertex3f(x-width/2, y+height/2, z-length/2)
  zglVertex3f(x-width/2, y+height/2, z+length/2)
  zglVertex3f(x+width/2, y+height/2, z+length/2)

  zglVertex3f(x+width/2, y+height/2, z-length/2)
  zglVertex3f(x-width/2, y+height/2, z-length/2)
  zglVertex3f(x+width/2, y+height/2, z+length/2)

  # Bottom Face
  zglVertex3f(x-width/2, y-height/2, z-length/2)
  zglVertex3f(x+width/2, y-height/2, z+length/2)
  zglVertex3f(x-width/2, y-height/2, z+length/2)

  zglVertex3f(x+width/2, y-height/2, z-length/2)
  zglVertex3f(x+width/2, y-height/2, z+length/2)
  zglVertex3f(x-width/2, y-height/2, z-length/2)

  # Right Face
  zglVertex3f(x+width/2, y-height/2, z-length/2)
  zglVertex3f(x+width/2, y+height/2, z-length/2)
  zglVertex3f(x+width/2, y+height/2, z+length/2)

  zglVertex3f(x+width/2, y-height/2, z+length/2)
  zglVertex3f(x+width/2, y-height/2, z-length/2)
  zglVertex3f(x+width/2, y+height/2, z+length/2)

  # Left Face
  zglVertex3f(x-width/2, y-height/2, z-length/2)
  zglVertex3f(x-width/2, y+height/2, z+length/2)
  zglVertex3f(x-width/2, y+height/2, z-length/2)

  zglVertex3f(x-width/2, y-height/2, z+length/2)
  zglVertex3f(x-width/2, y+height/2, z+length/2)
  zglVertex3f(x-width/2, y-height/2, z-length/2)

  # This call is where the current matrix is applied to all of the above calls
  zglEnd()

  # Pop the matrix off the stack since we're done with it
  zglPopMatrix()

# Constants
const
  ScreenWidth = 960
  ScreenHeight = 540

# Init zengine
zengine.init(ScreenWidth, ScreenHeight, "DebugDraw for Entity3D test")
zengine.gui.init()

# State variables
var
  # Window control
  evt = sdl2.defaultEvent
  running = true

  # Camera control
  camera = Camera(
    position: vec3f(0, 1, 2),
    target: vec3f(0, 1, 0),
    up: vec3f(0, 1, 0),
    fovY: 60
  )
  mouseXRel: int
  mouseYRel: int

  obj = newEntity3D()

# Set the location
obj.position.y += 1
#obj.orientation *= quatf(vec3f(0, 1, 0), 45)
obj.orientation = quatf(vec3f(0, 0, 1), 45)

# Use a first person camera
camera.setMode(CameraMode.FirstPerson)

var clock = Timer()
clock.start()

# Main Game loop
while running:
  # Reset
  mouseXRel = 0
  mouseYRel = 0
  clock.tick()

  rot += 45.0 * clock.deltaTime()

  # Check for new input
  pollInput()

  # Poll for events
  while sdl2.pollEvent(evt):
    case evt.kind:
      # Shutdown if X button clicked
      of QuitEvent:
        running = false

      of KeyUp:
        let keyEvent = cast[KeyboardEventPtr](addr evt)
        # Shutdown if ESC pressed
        if keyEvent.keysym.sym == K_ESCAPE:
          running = false

        # Get some info about the camera state
        if keyEvent.keysym.sym == K_C:
            echo("camera.position=" & $camera.position)
            echo("camera.target=" & $camera.target)
            echo("camera.up=" & $camera.up)

      # Update camera if mouse moved
      of MouseMotion:
        let mouseMoveEvent = cast[MouseMotionEventPtr](addr evt)
        mouseXRel = mouseMoveEvent.xrel
        mouseYRel = mouseMoveEvent.yrel

      else:
        discard

  # Update the camera's position
#  camera.update(0, -mouseXrel, -mouseYRel)

  # Start drawing
  beginDrawing()
  clearBackground(BLACK)

  begin3dMode(camera)
  drawPlane(vec3f(0, 0, 0), vec2f(32, 32), GRAY)
  debugDraw(obj)
  end3dMode()

  # done with drawing, display the screen
  endDrawing()
  swapBuffers()

# Shutdown
zengine.core.shutdown()

Hopefully that helps - if not please ping me in gitter and I'll reply asap.

define-private-public commented 6 years ago

Okay, I think I understand this a bit better, but this leads me to believe that ZGL's pushMatrix() might be incorrect when it comes to emulating old OpenGL. Take for example this snippet:

proc zglPushMatrix*() =
  if stackCounter == MATRIX_STACK_SIZE - 1:
    error("Stack Buffer Overflow (MAX $1 Matrix)" % $MATRIX_STACK_SIZE)

  stack[stackCounter] = currentMatrix[]
  zglLoadIdentity()
  inc(stackCounter)

  if currentMatrixMode == MatrixMode.ZGLModelView:
    useTempBuffer = true

And take a look at this link: https://stackoverflow.com/a/23971843/778628

That answer says the the previous matrix is cloned when a new matrix is pushed onto the stack. Where as zglPushMatrix is loading the identity matrix. This is why things didn't work like I expected them to.