flip texture when drawing it pyopengl - python

I recently downloaded a batch rendering program that uses pyOpenGl and pygame. I want to edit the draw function such that there is the ability to flip a texture when drawing it. This is the code for the batch rendering program.
import numpy
from OpenGL import GL
from OpenGL.GL import shaders
import ctypes
def setup_shaders(vpMatrix):
global shaderProgram, vshader, fshader
global texCoordAttribute, positionAttribute
vshader = shaders.compileShader('''#version 440
layout (location = 0) uniform mat4 vpMatrix;
layout (location = 0) in vec2 vPosition;
layout (location = 1) in vec2 vTexCoord0;
//layout (location = 2) in vec4 vVertColor;
out vec2 texCoord0;
//out vec4 vertColor;
void main(void) {
gl_Position = vpMatrix * vec4(vPosition, 0.0, 1.0);
texCoord0 = vTexCoord0;
}''', GL.GL_VERTEX_SHADER)
fshader = shaders.compileShader('''#version 440
in vec2 texCoord0;
//in vec4 vertColor;
layout (location = 1) uniform sampler2D u_texture0;
void main(void) {
gl_FragColor = texture2D(u_texture0, texCoord0);
}''', GL.GL_FRAGMENT_SHADER)
shaderProgram = shaders.compileProgram(vshader, fshader)
texCoordAttribute = GL.glGetAttribLocation(shaderProgram, 'vTexCoord0')
positionAttribute = GL.glGetAttribLocation(shaderProgram, 'vPosition')
texUniform = GL.glGetUniformLocation(shaderProgram, 'u_texture0')
GL.glEnableVertexAttribArray(positionAttribute)
GL.glEnableVertexAttribArray(texCoordAttribute)
GL.glUseProgram(shaderProgram)
GL.glUniformMatrix4fv(
GL.glGetUniformLocation(shaderProgram, 'vpMatrix'),
1,
False,
vpMatrix,
)
GL.glUniform1i(texUniform, 0)
# TODO: cleanup (delete shaders)
class GLTexture:
def __init__(self, textureId, width, height):
self.texture = textureId
self.width, self.height = width, height
def delete(self):
GL.glDeleteTextures([self.texture])
class GLTextureRegion:
def __init__(self, glTexture, subX, subY, subWidth, subHeight):
self.texture = glTexture
#self.tx, self.ty = subX, subY
self.tw, self.th = subWidth, subHeight
self.normalizedCoords = (
subX / glTexture.width, subY / glTexture.height,
(subX + subWidth) / glTexture.width, subY / glTexture.height,
subX / glTexture.width, (subY + subHeight) / glTexture.height,
(subX + subWidth) / glTexture.width, (subY + subHeight) / glTexture.height,
)
def write_tex_coords(self, aj):
nc = self.normalizedCoords
aj[4 * 0 + 2] = nc[0]
aj[4 * 0 + 3] = nc[1]
aj[4 * 1 + 2] = nc[2]
aj[4 * 1 + 3] = nc[3]
aj[4 * 2 + 2] = nc[4]
aj[4 * 2 + 3] = nc[5]
aj[4 * 3 + 2] = nc[6]
aj[4 * 3 + 3] = nc[7]
def write_vertices(self, aj, x, y):
aj[4 * 0 + 0] = x
aj[4 * 0 + 1] = y
aj[4 * 1 + 0] = x + self.tw
aj[4 * 1 + 1] = y
aj[4 * 2 + 0] = x
aj[4 * 2 + 1] = y + self.th
aj[4 * 3 + 0] = x + self.tw
aj[4 * 3 + 1] = y + self.th
def update_array(self, vboData, arrayIndex, x, y):
aj = vboData[arrayIndex]
self.write_vertices(aj, x, y)
self.write_tex_coords(aj)
def delete(self):
'''deletes the underlying texture'''
self.texture.delete()
class Batch:
def __init__(self, maxQuads = 10000):
self.maxQuads = maxQuads
self.vboIndices = GL.glGenBuffers(1)
GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, self.vboIndices)
vidA = []
for i in range(maxQuads):
vidA.extend([
4 * i + 0,
4 * i + 2,
4 * i + 1,
4 * i + 2,
4 * i + 1,
4 * i + 3
])
self.vboIndexData = numpy.array(vidA, numpy.ushort)
del vidA
GL.glBufferData(
GL.GL_ELEMENT_ARRAY_BUFFER,
self.vboIndexData,
GL.GL_STATIC_DRAW,
)
self.vbo = GL.glGenBuffers(1) # texture coords & vertices
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo)
self.vboData = numpy.zeros((maxQuads, 4 * 4), numpy.float32)
GL.glBufferData(
GL.GL_ARRAY_BUFFER,
self.vboData,
GL.GL_DYNAMIC_DRAW
)
self.currentTexture = None
self.objectIndex = 0
def begin(self):
GL.glBindBuffer(
GL.GL_ARRAY_BUFFER,
self.vbo,
)
if self.currentTexture:
GL.glBindTexture(
GL.GL_TEXTURE_2D,
self.currentTexture.texture,
)
GL.glEnable(GL.GL_TEXTURE_2D)
GL.glActiveTexture(GL.GL_TEXTURE0)
def draw(self, textureRegion, x, y, flip = False):
if self.currentTexture != textureRegion.texture:
self.flush()
self.currentTexture = textureRegion.texture
GL.glBindTexture(
GL.GL_TEXTURE_2D,
textureRegion.texture.texture,
)
elif self.objectIndex >= self.maxQuads:
self.flush()
textureRegion.update_array(
self.vboData,
self.objectIndex,
x, y
)
self.objectIndex += 1
def end(self):
self.flush()
def flush(self):
if not self.objectIndex:
return
GL.glVertexAttribPointer(
texCoordAttribute,
2,
GL.GL_FLOAT,
GL.GL_TRUE,
4 * self.vboData.itemsize,
ctypes.c_void_p(2 * self.vboData.itemsize)
)
GL.glVertexAttribPointer(
positionAttribute,
2,
GL.GL_FLOAT,
GL.GL_FALSE,
4 * self.vboData.itemsize,
ctypes.c_void_p(0)
)
GL.glBufferSubData(
GL.GL_ARRAY_BUFFER,
0,
16 * self.objectIndex * self.vboData.itemsize,
self.vboData,
)
GL.glDrawElements(
GL.GL_TRIANGLES,
6 * self.objectIndex,
GL.GL_UNSIGNED_SHORT,
ctypes.c_void_p(0),
)
self.objectIndex = 0
def delete(self):
GL.glDeleteBuffers(1, [self.vbo])
GL.glDeleteBuffers(1, [self.vboIndices])
So far I have tried a few things involving glscalef(as suggested by answers I found on the internet) however I couldn't get it to have any effect on the textures. Is there a way to make this work within the draw function or do I have to try another method entirely?

You can "flip" y component of the texture coordinate in the vertex shader:
void main(void) {
// [...]
texCoord0 = vec2(vTexCoord0.x, 1.0 - vTexCoord0.y);
}
If you want to "flip" individually textures, then you to flip the texture coordinate attributes. Pass either True or False to the argument flipY:
class GLTextureRegion:
# [...]
def write_tex_coords(self, aj, flipY):
nc = self.normalizedCoords
for i in range(4):
aj[i*4 + 2] = nc[i*2]
aj[i*4 + 3] = 1-nc[i*2 + 1] if flipY else nc[i*2 + 1]
# [...]
def update_array(self, vboData, arrayIndex, x, y, flipY):
aj = vboData[arrayIndex]
self.write_vertices(aj, x, y)
self.write_tex_coords(aj, flipY)
Alternatively you can scale the texture coordinates by in the vertex shader by a uniform. Initialize the uniform by
layout (location = 0) uniform mat4 vpMatrix;
layout (location = 1) uniform vec2 uTexCoordScale;
layout (location = 0) in vec2 vPosition;
layout (location = 1) in vec2 vTexCoord0;
out vec2 texCoord0;
void main(void)
{
gl_Position = vpMatrix * vec4(vPosition, 0.0, 1.0);
texCoord0 = uTexCoordScale * vTexCoord0;
}
Initialize the uniform uTexCoordScale by (1.0, 1.0) and change it to (1.0, -1.0) if you want to "flip" the texture.

Related

Attribute Error while using PyQtgraph libraries in my python code

I'm running a code from github site and it has this error ( last lines) :
File "D:\Anaconda3\lib\site-packages\pyqtgraph\opengl\GLViewWidget.py", line 152, in viewMatrix
tr.translate(-center.x(), -center.y(), -center.z())
AttributeError: 'int' object has no attribute 'x'
I found that the error is related to pyqtgraph libraries and i didn't change their files
just install the last versions of PyOpenGl and PyQtGraph in spyder
can you please help me in this error?
We need more information,
The problem seems to be related to the fact that center is an int but you use it as an object with attributs
Can we see center's initialization ?
the GlViewWidget.py file :
from ..Qt import QtCore, QtGui, QtOpenGL, QT_LIB
from OpenGL.GL import *
import OpenGL.GL.framebufferobjects as glfbo
import numpy as np
from .. import Vector
from .. import functions as fn
##Vector = QtGui.QVector3D
ShareWidget = None
class GLViewWidget(QtOpenGL.QGLWidget):
"""
Basic widget for displaying 3D data
- Rotation/scale controls
- Axis/grid display
- Export options
High-DPI displays: Qt5 should automatically detect the correct resolution.
For Qt4, specify the ``devicePixelRatio`` argument when initializing the
widget (usually this value is 1-2).
"""
def __init__(self, parent=None, devicePixelRatio=None):
global ShareWidget
if ShareWidget is None:
## create a dummy widget to allow sharing objects (textures, shaders, etc) between views
ShareWidget = QtOpenGL.QGLWidget()
QtOpenGL.QGLWidget.__init__(self, parent, ShareWidget)
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.opts = {
'center': Vector(0,0,0), ## will always appear at the center of the widget
'distance': 10.0, ## distance of camera from center
'fov': 60, ## horizontal field of view in degrees
'elevation': 30, ## camera's angle of elevation in degrees
'azimuth': 45, ## camera's azimuthal angle in degrees
## (rotation around z-axis 0 points along x-axis)
'viewport': None, ## glViewport params; None == whole widget
'devicePixelRatio': devicePixelRatio,
}
self.setBackgroundColor('k')
self.items = []
self.noRepeatKeys = [QtCore.Qt.Key_Right, QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown]
self.keysPressed = {}
self.keyTimer = QtCore.QTimer()
self.keyTimer.timeout.connect(self.evalKeyState)
self.makeCurrent()
def addItem(self, item):
self.items.append(item)
if hasattr(item, 'initializeGL'):
self.makeCurrent()
try:
item.initializeGL()
except:
self.checkOpenGLVersion('Error while adding item %s to GLViewWidget.' % str(item))
item._setView(self)
#print "set view", item, self, item.view()
self.update()
def removeItem(self, item):
self.items.remove(item)
item._setView(None)
self.update()
def initializeGL(self):
self.resizeGL(self.width(), self.height())
def setBackgroundColor(self, *args, **kwds):
"""
Set the background color of the widget. Accepts the same arguments as
pg.mkColor() and pg.glColor().
"""
self.opts['bgcolor'] = fn.glColor(*args, **kwds)
self.update()
def getViewport(self):
vp = self.opts['viewport']
dpr = self.devicePixelRatio()
if vp is None:
return (0, 0, int(self.width() * dpr), int(self.height() * dpr))
else:
return tuple([int(x * dpr) for x in vp])
def devicePixelRatio(self):
dpr = self.opts['devicePixelRatio']
if dpr is not None:
return dpr
if hasattr(QtOpenGL.QGLWidget, 'devicePixelRatio'):
return QtOpenGL.QGLWidget.devicePixelRatio(self)
else:
return 1.0
def resizeGL(self, w, h):
pass
#glViewport(*self.getViewport())
#self.update()
def setProjection(self, region=None):
m = self.projectionMatrix(region)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
a = np.array(m.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
def projectionMatrix(self, region=None):
if region is None:
dpr = self.devicePixelRatio()
region = (0, 0, self.width() * dpr, self.height() * dpr)
x0, y0, w, h = self.getViewport()
dist = self.opts['distance']
fov = self.opts['fov']
nearClip = dist * 0.001
farClip = dist * 1000.
r = nearClip * np.tan(fov * 0.5 * np.pi / 180.)
t = r * h / w
## Note that X0 and width in these equations must be the values used in viewport
left = r * ((region[0]-x0) * (2.0/w) - 1)
right = r * ((region[0]+region[2]-x0) * (2.0/w) - 1)
bottom = t * ((region[1]-y0) * (2.0/h) - 1)
top = t * ((region[1]+region[3]-y0) * (2.0/h) - 1)
tr = QtGui.QMatrix4x4()
tr.frustum(left, right, bottom, top, nearClip, farClip)
return tr
def setModelview(self):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
m = self.viewMatrix()
a = np.array(m.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
def viewMatrix(self):
tr = QtGui.QMatrix4x4()
tr.translate( 0.0, 0.0, -self.opts['distance'])
tr.rotate(self.opts['elevation']-90, 1, 0, 0)
tr.rotate(self.opts['azimuth']+90, 0, 0, -1)
center = self.opts['center']
tr.translate(-center.x(), -center.y(), -center.z())
return tr
def itemsAt(self, region=None):
"""
Return a list of the items displayed in the region (x, y, w, h)
relative to the widget.
"""
region = (region[0], self.height()-(region[1]+region[3]), region[2], region[3])
#buf = np.zeros(100000, dtype=np.uint)
buf = glSelectBuffer(100000)
try:
glRenderMode(GL_SELECT)
glInitNames()
glPushName(0)
self._itemNames = {}
self.paintGL(region=region, useItemNames=True)
finally:
hits = glRenderMode(GL_RENDER)
items = [(h.near, h.names[0]) for h in hits]
items.sort(key=lambda i: i[0])
return [self._itemNames[i[1]] for i in items]
def paintGL(self, region=None, viewport=None, useItemNames=False):
"""
viewport specifies the arguments to glViewport. If None, then we use self.opts['viewport']
region specifies the sub-region of self.opts['viewport'] that should be rendered.
Note that we may use viewport != self.opts['viewport'] when exporting.
"""
if viewport is None:
glViewport(*self.getViewport())
else:
glViewport(*viewport)
self.setProjection(region=region)
self.setModelview()
bgcolor = self.opts['bgcolor']
glClearColor(*bgcolor)
glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
self.drawItemTree(useItemNames=useItemNames)
def drawItemTree(self, item=None, useItemNames=False):
if item is None:
items = [x for x in self.items if x.parentItem() is None]
else:
items = item.childItems()
items.append(item)
items.sort(key=lambda a: a.depthValue())
for i in items:
if not i.visible():
continue
if i is item:
try:
glPushAttrib(GL_ALL_ATTRIB_BITS)
if useItemNames:
glLoadName(i._id)
self._itemNames[i._id] = i
i.paint()
except:
from .. import debug
debug.printExc()
msg = "Error while drawing item %s." % str(item)
ver = glGetString(GL_VERSION)
if ver is not None:
ver = ver.split()[0]
if int(ver.split(b'.')[0]) < 2:
print(msg + " The original exception is printed above; however, pyqtgraph requires OpenGL version 2.0 or greater for many of its 3D features and your OpenGL version is %s. Installing updated display drivers may resolve this issue." % ver)
else:
print(msg)
finally:
glPopAttrib()
else:
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
try:
tr = i.transform()
a = np.array(tr.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
self.drawItemTree(i, useItemNames=useItemNames)
finally:
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
def setCameraPosition(self, pos=None, distance=None, elevation=None, azimuth=None):
if pos is not None:
self.opts['center'] = pos
if distance is not None:
self.opts['distance'] = distance
if elevation is not None:
self.opts['elevation'] = elevation
if azimuth is not None:
self.opts['azimuth'] = azimuth
self.update()
def cameraPosition(self):
"""Return current position of camera based on center, dist, elevation, and azimuth"""
center = self.opts['center']
dist = self.opts['distance']
elev = self.opts['elevation'] * np.pi/180.
azim = self.opts['azimuth'] * np.pi/180.
pos = Vector(
center.x() + dist * np.cos(elev) * np.cos(azim),
center.y() + dist * np.cos(elev) * np.sin(azim),
center.z() + dist * np.sin(elev)
)
return pos
def orbit(self, azim, elev):
"""Orbits the camera around the center position. *azim* and *elev* are given in degrees."""
self.opts['azimuth'] += azim
self.opts['elevation'] = np.clip(self.opts['elevation'] + elev, -90, 90)
self.update()
def pan(self, dx, dy, dz, relative='global'):
"""
Moves the center (look-at) position while holding the camera in place.
============== =======================================================
**Arguments:**
*dx* Distance to pan in x direction
*dy* Distance to pan in y direction
*dx* Distance to pan in z direction
*relative* String that determines the direction of dx,dy,dz.
If "global", then the global coordinate system is used.
If "view", then the z axis is aligned with the view
direction, and x and y axes are inthe plane of the
view: +x points right, +y points up.
If "view-upright", then x is in the global xy plane and
points to the right side of the view, y is in the
global xy plane and orthogonal to x, and z points in
the global z direction.
============== =======================================================
Distances are scaled roughly such that a value of 1.0 moves
by one pixel on screen.
Prior to version 0.11, *relative* was expected to be either True (x-aligned) or
False (global). These values are deprecated but still recognized.
"""
# for backward compatibility:
relative = {True: "view-upright", False: "global"}.get(relative, relative)
if relative == 'global':
self.opts['center'] += QtGui.QVector3D(dx, dy, dz)
elif relative == 'view-upright':
cPos = self.cameraPosition()
cVec = self.opts['center'] - cPos
dist = cVec.length() ## distance from camera to center
xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) ## approx. width of view at distance of center point
xScale = xDist / self.width()
zVec = QtGui.QVector3D(0,0,1)
xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized()
yVec = QtGui.QVector3D.crossProduct(xVec, zVec).normalized()
self.opts['center'] = self.opts['center'] + xVec * xScale * dx + yVec * xScale * dy + zVec * xScale * dz
elif relative == 'view':
# pan in plane of camera
elev = np.radians(self.opts['elevation'])
azim = np.radians(self.opts['azimuth'])
fov = np.radians(self.opts['fov'])
dist = (self.opts['center'] - self.cameraPosition()).length()
fov_factor = np.tan(fov / 2) * 2
scale_factor = dist * fov_factor / self.width()
z = scale_factor * np.cos(elev) * dy
x = scale_factor * (np.sin(azim) * dx - np.sin(elev) * np.cos(azim) * dy)
y = scale_factor * (np.cos(azim) * dx + np.sin(elev) * np.sin(azim) * dy)
self.opts['center'] += QtGui.QVector3D(x, -y, z)
else:
raise ValueError("relative argument must be global, view, or view-upright")
self.update()
def pixelSize(self, pos):
"""
Return the approximate size of a screen pixel at the location pos
Pos may be a Vector or an (N,3) array of locations
"""
cam = self.cameraPosition()
if isinstance(pos, np.ndarray):
cam = np.array(cam).reshape((1,)*(pos.ndim-1)+(3,))
dist = ((pos-cam)**2).sum(axis=-1)**0.5
else:
dist = (pos-cam).length()
xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.)
return xDist / self.width()
def mousePressEvent(self, ev):
self.mousePos = ev.pos()
def mouseMoveEvent(self, ev):
diff = ev.pos() - self.mousePos
self.mousePos = ev.pos()
if ev.buttons() == QtCore.Qt.LeftButton:
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.pan(diff.x(), diff.y(), 0, relative='view')
else:
self.orbit(-diff.x(), diff.y())
elif ev.buttons() == QtCore.Qt.MidButton:
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.pan(diff.x(), 0, diff.y(), relative='view-upright')
else:
self.pan(diff.x(), diff.y(), 0, relative='view-upright')
def mouseReleaseEvent(self, ev):
pass
# Example item selection code:
#region = (ev.pos().x()-5, ev.pos().y()-5, 10, 10)
#print(self.itemsAt(region))
## debugging code: draw the picking region
#glViewport(*self.getViewport())
#glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
#region = (region[0], self.height()-(region[1]+region[3]), region[2], region[3])
#self.paintGL(region=region)
#self.swapBuffers()
def wheelEvent(self, ev):
delta = 0
if QT_LIB in ['PyQt4', 'PySide']:
delta = ev.delta()
else:
delta = ev.angleDelta().x()
if delta == 0:
delta = ev.angleDelta().y()
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.opts['fov'] *= 0.999**delta
else:
self.opts['distance'] *= 0.999**delta
self.update()
def keyPressEvent(self, ev):
if ev.key() in self.noRepeatKeys:
ev.accept()
if ev.isAutoRepeat():
return
self.keysPressed[ev.key()] = 1
self.evalKeyState()
def keyReleaseEvent(self, ev):
if ev.key() in self.noRepeatKeys:
ev.accept()
if ev.isAutoRepeat():
return
try:
del self.keysPressed[ev.key()]
except:
self.keysPressed = {}
self.evalKeyState()
def evalKeyState(self):
speed = 2.0
if len(self.keysPressed) > 0:
for key in self.keysPressed:
if key == QtCore.Qt.Key_Right:
self.orbit(azim=-speed, elev=0)
elif key == QtCore.Qt.Key_Left:
self.orbit(azim=speed, elev=0)
elif key == QtCore.Qt.Key_Up:
self.orbit(azim=0, elev=-speed)
elif key == QtCore.Qt.Key_Down:
self.orbit(azim=0, elev=speed)
elif key == QtCore.Qt.Key_PageUp:
pass
elif key == QtCore.Qt.Key_PageDown:
pass
self.keyTimer.start(16)
else:
self.keyTimer.stop()
def checkOpenGLVersion(self, msg):
## Only to be called from within exception handler.
ver = glGetString(GL_VERSION).split()[0]
if int(ver.split(b'.')[0]) < 2:
from .. import debug
debug.printExc()
raise Exception(msg + " The original exception is printed above; however, pyqtgraph requires OpenGL version 2.0 or greater for many of its 3D features and your OpenGL version is %s. Installing updated display drivers may resolve this issue." % ver)
else:
raise
def readQImage(self):
"""
Read the current buffer pixels out as a QImage.
"""
w = self.width()
h = self.height()
self.repaint()
pixels = np.empty((h, w, 4), dtype=np.ubyte)
pixels[:] = 128
pixels[...,0] = 50
pixels[...,3] = 255
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels)
# swap B,R channels for Qt
tmp = pixels[...,0].copy()
pixels[...,0] = pixels[...,2]
pixels[...,2] = tmp
pixels = pixels[::-1] # flip vertical
img = fn.makeQImage(pixels, transpose=False)
return img
def renderToArray(self, size, format=GL_BGRA, type=GL_UNSIGNED_BYTE, textureSize=1024, padding=256):
w,h = map(int, size)
self.makeCurrent()
tex = None
fb = None
try:
output = np.empty((w, h, 4), dtype=np.ubyte)
fb = glfbo.glGenFramebuffers(1)
glfbo.glBindFramebuffer(glfbo.GL_FRAMEBUFFER, fb )
glEnable(GL_TEXTURE_2D)
tex = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tex)
texwidth = textureSize
data = np.zeros((texwidth,texwidth,4), dtype=np.ubyte)
## Test texture dimensions first
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, texwidth, texwidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
if glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH) == 0:
raise Exception("OpenGL failed to create 2D texture (%dx%d); too large for this hardware." % shape[:2])
## create teture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texwidth, texwidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.transpose((1,0,2)))
self.opts['viewport'] = (0, 0, w, h) # viewport is the complete image; this ensures that paintGL(region=...)
# is interpreted correctly.
p2 = 2 * padding
for x in range(-padding, w-padding, texwidth-p2):
for y in range(-padding, h-padding, texwidth-p2):
x2 = min(x+texwidth, w+padding)
y2 = min(y+texwidth, h+padding)
w2 = x2-x
h2 = y2-y
## render to texture
glfbo.glFramebufferTexture2D(glfbo.GL_FRAMEBUFFER, glfbo.GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0)
self.paintGL(region=(x, h-y-h2, w2, h2), viewport=(0, 0, w2, h2)) # only render sub-region
glBindTexture(GL_TEXTURE_2D, tex) # fixes issue #366
## read texture back to array
data = glGetTexImage(GL_TEXTURE_2D, 0, format, type)
data = np.fromstring(data, dtype=np.ubyte).reshape(texwidth,texwidth,4).transpose(1,0,2)[:, ::-1]
output[x+padding:x2-padding, y+padding:y2-padding] = data[padding:w2-padding, -(h2-padding):-padding]
finally:
self.opts['viewport'] = None
glfbo.glBindFramebuffer(glfbo.GL_FRAMEBUFFER, 0)
glBindTexture(GL_TEXTURE_2D, 0)
if tex is not None:
glDeleteTextures([tex])
if fb is not None:
glfbo.glDeleteFramebuffers([fb])
return output
Somewhere in your code you are setting the center to be an int. setCameraPosition, maybe? It needs to be a Vector object, instead.

Is it possible to create physics simulation (with Bullet Physics Engine and Panda3D) without the `render` object

Is it possible to create physics simulation without the render object?
I tried but it prints the identity matrices:
from panda3d.bullet import BulletWorld
from panda3d.core import Vec3
from panda3d.bullet import BulletBoxShape
from panda3d.bullet import BulletRigidBodyNode
world = BulletWorld()
world.setGravity(Vec3(0, 0, -9.81))
shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
node = BulletRigidBodyNode('Box')
node.setMass(1.0)
node.addShape(shape)
world.attachRigidBody(node)
for i in range(10):
world.doPhysics(0.016)
print(node.getShapeTransform(0))
Solution:
from panda3d.bullet import BulletWorld
from panda3d.core import TransformState, Vec3, Quat, Point3
from panda3d.bullet import BulletBoxShape
from panda3d.bullet import BulletRigidBodyNode
world = BulletWorld()
world.setGravity(Vec3(0, 0, -9.81))
shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
node = BulletRigidBodyNode('Box')
node.setMass(1.0)
p = Point3(1, 0, 0)
q = Quat.identQuat()
s = Vec3(2, 2, 2)
transform = TransformState.make_pos_quat_scale(p, q, s)
node.setTransform(transform)
node.addShape(shape)
world.attachRigidBody(node)
for i in range(10):
world.doPhysics(0.016)
print(node.getTransform())
Output:
T:(pos 1 0 0 scale 2)
T:(pos 1 0 -0.002507 scale 2)
T:(pos 1 0 -0.007521 scale 2)
T:(pos 1 0 -0.015042 scale 2)
T:(pos 1 0 -0.02507 scale 2)
T:(pos 1 0 -0.037605 scale 2)
T:(pos 1 0 -0.052647 scale 2)
T:(pos 1 0 -0.070196 scale 2)
T:(pos 1 0 -0.090252 scale 2)
T:(pos 1 0 -0.112815 scale 2)
I wrote an interesting example. I use:
the Panda3D Bullet wrapper for Physics
PyQt5 (and PySide2) for creating a window
OpenGL 3.3 for rendering
I created a textured cube with Blender and GIMP. I exported the cube to dae (COLLADA) and imported it to my program with built-in Qt XML parser.
Source:
PyQt5: https://rextester.com/MKV35748
PySide2: https://rextester.com/RAHNB58294
PyQt5:
import sys
import numpy as np
from OpenGL import GL as gl
from PyQt5.QtWidgets import QApplication, QOpenGLWidget
from PyQt5.QtGui import QOpenGLShaderProgram, QOpenGLShader, QOpenGLBuffer
from PyQt5.QtGui import QOpenGLTexture, QImage
from PyQt5.QtGui import QMatrix4x4, QVector3D, QQuaternion
from PyQt5.QtXml import QDomDocument, QDomElement
from PyQt5.QtCore import Qt, QFile, QIODevice
from PyQt5.QtCore import QTimer, QElapsedTimer
from panda3d.bullet import BulletWorld
from panda3d.core import TransformState, Vec3, Quat, Point3
from panda3d.bullet import BulletBoxShape
from panda3d.bullet import BulletRigidBodyNode
# Assets:
# Cube Texture: https://dl.dropboxusercontent.com/s/tply9ubx3n3ycvv/cube.png
# Cube Model: https://dl.dropboxusercontent.com/s/0aktc37c3nx9iq3/cube.dae
# Plane Texture: https://dl.dropboxusercontent.com/s/3iibsnvyw0vupby/plane.png
# Plane Model: https://dl.dropboxusercontent.com/s/e0wktg69ec3w8pq/plane.dae
class VertexBuffers:
vertex_pos_buffer = None
normal_buffer = None
tex_coord_buffer = None
amount_of_vertices = None
class Locations:
mvp_matrix_location = None
model_matrix_location = None
normal_matrix_location = None
class Object3D:
position = QVector3D(0, 0, 0)
rotation = QVector3D(0, 0, 0)
scale = QVector3D(1, 1, 1)
mvp_matrix = QMatrix4x4()
model_matrix = QMatrix4x4()
normal_matrix = QMatrix4x4()
def __init__(self, vert_buffers, locations, texture, world, mass, pos):
self.vert_pos_buffer = vert_buffers.vert_pos_buffer
self.normal_buffer = vert_buffers.normal_buffer
self.tex_coord_buffer = vert_buffers.tex_coord_buffer
self.amount_of_vertices = vert_buffers.amount_of_vertices
self.mvp_matrix_location = locations.mvp_matrix_location
self.model_matrix_location = locations.model_matrix_location
self.normal_matrix_location = locations.normal_matrix_location
self.texture = texture
self.shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
self.node = BulletRigidBodyNode('Box')
self.position = pos
self.mass = mass
self.node.setMass(self.mass)
p = Point3(self.position.x(), self.position.y(), self.position.z())
q = Quat.identQuat()
s = Vec3(1, 1, 1)
self.transform = TransformState.make_pos_quat_scale(p, q, s)
self.node.setTransform(self.transform)
self.node.addShape(self.shape)
self.world = world
self.world.attachRigidBody(self.node)
def draw(self, program, proj_view_matrix):
program.bind()
self.vert_pos_buffer.bind()
program.setAttributeBuffer(0, gl.GL_FLOAT, 0, 3)
program.enableAttributeArray(0)
self.normal_buffer.bind()
program.setAttributeBuffer(1, gl.GL_FLOAT, 0, 3)
program.enableAttributeArray(1)
self.tex_coord_buffer.bind()
program.setAttributeBuffer(2, gl.GL_FLOAT, 0, 2)
program.enableAttributeArray(2)
self.position.setX(self.node.getTransform().pos.x)
self.position.setY(self.node.getTransform().pos.y)
self.position.setZ(self.node.getTransform().pos.z)
hpr = self.node.getTransform().getHpr()
pandaQuat = Quat()
pandaQuat.setHpr(hpr)
quat = QQuaternion(pandaQuat.getX(), pandaQuat.getY(), pandaQuat.getZ(), pandaQuat.getW())
self.model_matrix.setToIdentity()
self.model_matrix.translate(self.position)
self.model_matrix.rotate(quat)
self.model_matrix.scale(self.scale)
self.mvp_matrix = proj_view_matrix * self.model_matrix
self.normal_matrix = self.model_matrix.inverted()
self.normal_matrix = self.normal_matrix[0].transposed()
program.bind()
program.setUniformValue(self.mvp_matrix_location, self.mvp_matrix)
program.setUniformValue(self.model_matrix_location, self.model_matrix)
program.setUniformValue(self.normal_matrix_location, self.normal_matrix)
self.texture.bind()
gl.glDrawArrays(gl.GL_TRIANGLES, 0, self.amount_of_vertices)
class Window(QOpenGLWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Bullet Physics")
self.resize(268, 268)
def initializeGL(self):
gl.glClearColor(0.2, 0.2, 0.2, 1)
gl.glEnable(gl.GL_DEPTH_TEST)
vertShaderSrc = """
#version 330 core
in vec4 aPosition;
in vec4 aNormal;
in vec2 aTexCoord;
uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;
uniform mat4 uNormalMatrix;
out vec3 vPosition;
out vec3 vNormal;
out vec2 vTexCoord;
void main()
{
gl_Position = uMvpMatrix * aPosition;
vPosition = vec3(uModelMatrix * aPosition);
vNormal = normalize(vec3(uNormalMatrix * aNormal));
vTexCoord = aTexCoord;
}
"""
fragShaderSrc = """
#version 330 core
const vec3 lightColor = vec3(0.8, 0.8, 0.8);
const vec3 lightPosition = vec3(5.0, 7.0, 2.0);
const vec3 ambientLight = vec3(0.3, 0.3, 0.3);
uniform sampler2D uSampler;
in vec3 vPosition;
in vec3 vNormal;
in vec2 vTexCoord;
void main()
{
vec4 color = texture2D(uSampler, vTexCoord);
vec3 normal = normalize(vNormal);
vec3 lightDirection = normalize(lightPosition - vPosition);
float nDotL = max(dot(lightDirection, normal), 0.0);
vec3 diffuse = lightColor * color.rgb * nDotL;
vec3 ambient = ambientLight * color.rgb;
gl_FragColor = vec4(diffuse + ambient, color.a);
}
"""
self.program = QOpenGLShaderProgram()
self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertShaderSrc)
self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragShaderSrc)
self.program.link()
self.program.bind()
self.program.bindAttributeLocation("aPosition", 0)
self.program.bindAttributeLocation("aNormal", 1)
self.program.bindAttributeLocation("aTexCoord", 2)
locations = Locations()
self.program.bind()
locations.mvp_matrix_location = self.program.uniformLocation("uMvpMatrix")
locations.model_matrix_location = self.program.uniformLocation("uModelMatrix")
locations.normal_matrix_location = self.program.uniformLocation("uNormalMatrix")
self.vert_buffers = self.initVertexBuffers("assets/cube.dae")
self.proj_view_matrix = QMatrix4x4()
self.proj_matrix = QMatrix4x4()
self.view_matrix = QMatrix4x4()
self.view_matrix.lookAt(
QVector3D(2, 3, 5),
QVector3D(0, 0, 0),
QVector3D(0, 1, 0))
self.texture = QOpenGLTexture(QOpenGLTexture.Target2D)
self.texture.create()
self.texture.setData(QImage("assets/cube.png"))
self.texture.setMinMagFilters(QOpenGLTexture.Linear, QOpenGLTexture.Linear)
self.texture.setWrapMode(QOpenGLTexture.ClampToEdge)
self.world = BulletWorld()
self.world.setGravity(Vec3(0, -9.81, 0))
self.obj = Object3D(self.vert_buffers, locations, self.texture, self.world, mass=0, pos=QVector3D(0, -3, 0))
self.obj2 = Object3D(self.vert_buffers, locations, self.texture, self.world, mass=1, pos=QVector3D(0.8, 3, 0))
#self.move_dir = 1 # move direction: 1 - up, -1 - down
#self.move_speed = 0.002
self.timer = QTimer()
self.timer.timeout.connect(self.animationLoop)
self.elapsed_timer = QElapsedTimer()
self.elapsed_timer.start()
self.delta_time = 0
self.timer.start(1000/60)
def animationLoop(self):
self.delta_time = self.elapsed_timer.elapsed()
self.elapsed_timer.restart()
self.world.doPhysics(self.delta_time / 1000)
self.update()
def paintGL(self):
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
self.proj_view_matrix = self.proj_matrix * self.view_matrix
self.obj.draw(self.program, self.proj_view_matrix)
self.obj2.draw(self.program, self.proj_view_matrix)
def resizeGL(self, w, h):
gl.glViewport(0, 0, w, h)
self.proj_matrix.setToIdentity()
self.proj_matrix.perspective(50, float(w) / float(h), 0.1, 100)
def initVertexBuffers(self, path):
xml_doc = QDomDocument()
file = QFile(path)
if not file.open(QIODevice.ReadOnly):
print("Failed to open the file: " + path)
xml_doc.setContent(file)
file.close()
vert_pos_array = []
normal_array = []
tex_coord_array = []
index_array = []
root = xml_doc.documentElement()
dae_elem = root.firstChildElement()
while not dae_elem.isNull():
if dae_elem.tagName() == "library_geometries":
geom_elem = dae_elem.firstChildElement()
if geom_elem.tagName() == "geometry":
mesh_elem = geom_elem.firstChildElement()
if mesh_elem.tagName() == "mesh":
mesh_child_elem = mesh_elem.firstChildElement()
while not mesh_child_elem.isNull():
float_array_elem = mesh_child_elem.firstChildElement()
str_array = float_array_elem.firstChild().toText().data().split(" ")
if mesh_child_elem.attribute("id").endswith("-mesh-positions"):
vert_pos_array = list(map(float, str_array))
if mesh_child_elem.attribute("id").endswith("-mesh-normals"):
normal_array = list(map(float, str_array))
if mesh_child_elem.attribute("id").endswith("-mesh-map-0"):
tex_coord_array = list(map(float, str_array))
if mesh_child_elem.tagName() == "triangles" or mesh_child_elem.tagName() == "polylist":
p_child_elem = mesh_child_elem.firstChildElement()
while not p_child_elem.isNull():
if p_child_elem.tagName() == "p":
str_indices = p_child_elem.firstChild().toText().data().split(" ")
index_array = list(map(int, str_indices))
p_child_elem = p_child_elem.nextSiblingElement()
mesh_child_elem = mesh_child_elem.nextSiblingElement()
dae_elem = dae_elem.nextSiblingElement()
# print(vert_pos_array)
# print(normal_array)
# print(tex_coord_array)
# print(index_array)
num_of_attributes = 3
vert_positions = []
normals = []
tex_coords = []
for i in range(0, len(index_array), num_of_attributes):
vert_pos_index = index_array[i + 0]
vert_positions.append(vert_pos_array[vert_pos_index * 3 + 0])
vert_positions.append(vert_pos_array[vert_pos_index * 3 + 1])
vert_positions.append(vert_pos_array[vert_pos_index * 3 + 2])
normal_index = index_array[i + 1]
normals.append(normal_array[normal_index * 3 + 0])
normals.append(normal_array[normal_index * 3 + 1])
normals.append(normal_array[normal_index * 3 + 2])
tex_coord_index = index_array[i + 2]
tex_coords.append(tex_coord_array[tex_coord_index * 2 + 0])
tex_coords.append(tex_coord_array[tex_coord_index * 2 + 1])
# print(vert_positions)
# print(normals)
# print(tex_coords)
output = {}
vert_positions = np.array(vert_positions, dtype=np.float32)
vert_pos_buffer = QOpenGLBuffer()
vert_pos_buffer.create()
vert_pos_buffer.bind()
vert_pos_buffer.allocate(vert_positions, len(vert_positions) * 4)
normals = np.array(normals, dtype=np.float32)
normal_buffer = QOpenGLBuffer()
normal_buffer.create()
normal_buffer.bind()
normal_buffer.allocate(normals, len(normals) * 4)
tex_coords = np.array(tex_coords, dtype=np.float32)
tex_coord_buffer = QOpenGLBuffer()
tex_coord_buffer.create()
tex_coord_buffer.bind()
tex_coord_buffer.allocate(tex_coords, len(tex_coords) * 4)
vert_buffers = VertexBuffers()
vert_buffers.vert_pos_buffer = vert_pos_buffer
vert_buffers.normal_buffer = normal_buffer
vert_buffers.tex_coord_buffer = tex_coord_buffer
vert_buffers.amount_of_vertices = int(len(index_array) / 3)
return vert_buffers
def main():
QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
PySide2:
import sys
import numpy as np
from OpenGL import GL as gl
from PySide2.QtWidgets import QApplication, QOpenGLWidget
from PySide2.QtGui import QOpenGLShaderProgram, QOpenGLShader, QOpenGLBuffer
from PySide2.QtGui import QOpenGLTexture, QImage
from PySide2.QtGui import QMatrix4x4, QVector3D, QQuaternion
from PySide2.QtXml import QDomDocument, QDomElement
from PySide2.QtCore import Qt, QFile, QIODevice
from PySide2.QtCore import QTimer, QElapsedTimer
from panda3d.bullet import BulletWorld
from panda3d.core import TransformState, Vec3, Quat, Point3
from panda3d.bullet import BulletBoxShape
from panda3d.bullet import BulletRigidBodyNode
# Assets:
# Cube Texture: https://dl.dropboxusercontent.com/s/tply9ubx3n3ycvv/cube.png
# Cube Model: https://dl.dropboxusercontent.com/s/0aktc37c3nx9iq3/cube.dae
# Plane Texture: https://dl.dropboxusercontent.com/s/3iibsnvyw0vupby/plane.png
# Plane Model: https://dl.dropboxusercontent.com/s/e0wktg69ec3w8pq/plane.dae
class VertexBuffers:
vertex_pos_buffer = None
normal_buffer = None
tex_coord_buffer = None
amount_of_vertices = None
class Locations:
mvp_matrix_location = None
model_matrix_location = None
normal_matrix_location = None
class Object3D:
position = QVector3D(0, 0, 0)
rotation = QVector3D(0, 0, 0)
scale = QVector3D(1, 1, 1)
mvp_matrix = QMatrix4x4()
model_matrix = QMatrix4x4()
normal_matrix = QMatrix4x4()
def __init__(self, vert_buffers, locations, texture, world, mass, pos):
self.vert_pos_buffer = vert_buffers.vert_pos_buffer
self.normal_buffer = vert_buffers.normal_buffer
self.tex_coord_buffer = vert_buffers.tex_coord_buffer
self.amount_of_vertices = vert_buffers.amount_of_vertices
self.mvp_matrix_location = locations.mvp_matrix_location
self.model_matrix_location = locations.model_matrix_location
self.normal_matrix_location = locations.normal_matrix_location
self.texture = texture
self.shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
self.node = BulletRigidBodyNode('Box')
self.position = pos
self.mass = mass
self.node.setMass(self.mass)
p = Point3(self.position.x(), self.position.y(), self.position.z())
q = Quat.identQuat()
s = Vec3(1, 1, 1)
self.transform = TransformState.make_pos_quat_scale(p, q, s)
self.node.setTransform(self.transform)
self.node.addShape(self.shape)
self.world = world
self.world.attachRigidBody(self.node)
def draw(self, program, proj_view_matrix):
program.bind()
self.vert_pos_buffer.bind()
program.setAttributeBuffer(0, gl.GL_FLOAT, 0, 3)
program.enableAttributeArray(0)
self.normal_buffer.bind()
program.setAttributeBuffer(1, gl.GL_FLOAT, 0, 3)
program.enableAttributeArray(1)
self.tex_coord_buffer.bind()
program.setAttributeBuffer(2, gl.GL_FLOAT, 0, 2)
program.enableAttributeArray(2)
self.position.setX(self.node.getTransform().pos.x)
self.position.setY(self.node.getTransform().pos.y)
self.position.setZ(self.node.getTransform().pos.z)
hpr = self.node.getTransform().getHpr()
pandaQuat = Quat()
pandaQuat.setHpr(hpr)
quat = QQuaternion(pandaQuat.getX(), pandaQuat.getY(), pandaQuat.getZ(), pandaQuat.getW())
self.model_matrix.setToIdentity()
self.model_matrix.translate(self.position)
self.model_matrix.rotate(quat)
self.model_matrix.scale(self.scale)
self.mvp_matrix = proj_view_matrix * self.model_matrix
self.normal_matrix = self.model_matrix.inverted()
self.normal_matrix = self.normal_matrix[0].transposed()
program.bind()
program.setUniformValue(self.mvp_matrix_location, self.mvp_matrix)
program.setUniformValue(self.model_matrix_location, self.model_matrix)
program.setUniformValue(self.normal_matrix_location, self.normal_matrix)
self.texture.bind()
gl.glDrawArrays(gl.GL_TRIANGLES, 0, self.amount_of_vertices)
class Window(QOpenGLWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Bullet Physics")
self.resize(268, 268)
def initializeGL(self):
gl.glClearColor(0.2, 0.2, 0.2, 1)
gl.glEnable(gl.GL_DEPTH_TEST)
vertShaderSrc = """
#version 330 core
in vec4 aPosition;
in vec4 aNormal;
in vec2 aTexCoord;
uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;
uniform mat4 uNormalMatrix;
out vec3 vPosition;
out vec3 vNormal;
out vec2 vTexCoord;
void main()
{
gl_Position = uMvpMatrix * aPosition;
vPosition = vec3(uModelMatrix * aPosition);
vNormal = normalize(vec3(uNormalMatrix * aNormal));
vTexCoord = aTexCoord;
}
"""
fragShaderSrc = """
#version 330 core
const vec3 lightColor = vec3(0.8, 0.8, 0.8);
const vec3 lightPosition = vec3(5.0, 7.0, 2.0);
const vec3 ambientLight = vec3(0.3, 0.3, 0.3);
uniform sampler2D uSampler;
in vec3 vPosition;
in vec3 vNormal;
in vec2 vTexCoord;
void main()
{
vec4 color = texture2D(uSampler, vTexCoord);
vec3 normal = normalize(vNormal);
vec3 lightDirection = normalize(lightPosition - vPosition);
float nDotL = max(dot(lightDirection, normal), 0.0);
vec3 diffuse = lightColor * color.rgb * nDotL;
vec3 ambient = ambientLight * color.rgb;
gl_FragColor = vec4(diffuse + ambient, color.a);
}
"""
self.program = QOpenGLShaderProgram()
self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertShaderSrc)
self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragShaderSrc)
self.program.link()
self.program.bind()
self.program.bindAttributeLocation("aPosition", 0)
self.program.bindAttributeLocation("aNormal", 1)
self.program.bindAttributeLocation("aTexCoord", 2)
locations = Locations()
self.program.bind()
locations.mvp_matrix_location = self.program.uniformLocation("uMvpMatrix")
locations.model_matrix_location = self.program.uniformLocation("uModelMatrix")
locations.normal_matrix_location = self.program.uniformLocation("uNormalMatrix")
self.vert_buffers = self.initVertexBuffers("assets/cube.dae")
self.proj_view_matrix = QMatrix4x4()
self.proj_matrix = QMatrix4x4()
self.view_matrix = QMatrix4x4()
self.view_matrix.lookAt(
QVector3D(2, 3, 5),
QVector3D(0, 0, 0),
QVector3D(0, 1, 0))
self.texture = QOpenGLTexture(QOpenGLTexture.Target2D)
self.texture.create()
self.texture.setData(QImage("assets/cube.png"))
self.texture.setMinMagFilters(QOpenGLTexture.Linear, QOpenGLTexture.Linear)
self.texture.setWrapMode(QOpenGLTexture.ClampToEdge)
self.world = BulletWorld()
self.world.setGravity(Vec3(0, -9.81, 0))
self.obj = Object3D(self.vert_buffers, locations, self.texture, self.world, mass=0, pos=QVector3D(0, -3, 0))
self.obj2 = Object3D(self.vert_buffers, locations, self.texture, self.world, mass=1, pos=QVector3D(0.8, 3, 0))
#self.move_dir = 1 # move direction: 1 - up, -1 - down
#self.move_speed = 0.002
self.timer = QTimer()
self.timer.timeout.connect(self.animationLoop)
self.elapsed_timer = QElapsedTimer()
self.elapsed_timer.start()
self.delta_time = 0
self.timer.start(1000/60)
def animationLoop(self):
self.delta_time = self.elapsed_timer.elapsed()
self.elapsed_timer.restart()
self.world.doPhysics(self.delta_time / 1000)
self.update()
def paintGL(self):
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
self.proj_view_matrix = self.proj_matrix * self.view_matrix
self.obj.draw(self.program, self.proj_view_matrix)
self.obj2.draw(self.program, self.proj_view_matrix)
def resizeGL(self, w, h):
gl.glViewport(0, 0, w, h)
self.proj_matrix.setToIdentity()
self.proj_matrix.perspective(50, float(w) / float(h), 0.1, 100)
def initVertexBuffers(self, path):
xml_doc = QDomDocument()
file = QFile(path)
if not file.open(QIODevice.ReadOnly):
print("Failed to open the file: " + path)
xml_doc.setContent(file)
file.close()
vert_pos_array = []
normal_array = []
tex_coord_array = []
index_array = []
root = xml_doc.documentElement()
dae_elem = root.firstChildElement()
while not dae_elem.isNull():
if dae_elem.tagName() == "library_geometries":
geom_elem = dae_elem.firstChildElement()
if geom_elem.tagName() == "geometry":
mesh_elem = geom_elem.firstChildElement()
if mesh_elem.tagName() == "mesh":
mesh_child_elem = mesh_elem.firstChildElement()
while not mesh_child_elem.isNull():
float_array_elem = mesh_child_elem.firstChildElement()
str_array = float_array_elem.firstChild().toText().data().split(" ")
if mesh_child_elem.attribute("id").endswith("-mesh-positions"):
vert_pos_array = list(map(float, str_array))
if mesh_child_elem.attribute("id").endswith("-mesh-normals"):
normal_array = list(map(float, str_array))
if mesh_child_elem.attribute("id").endswith("-mesh-map-0"):
tex_coord_array = list(map(float, str_array))
if mesh_child_elem.tagName() == "triangles" or mesh_child_elem.tagName() == "polylist":
p_child_elem = mesh_child_elem.firstChildElement()
while not p_child_elem.isNull():
if p_child_elem.tagName() == "p":
str_indices = p_child_elem.firstChild().toText().data().split(" ")
index_array = list(map(int, str_indices))
p_child_elem = p_child_elem.nextSiblingElement()
mesh_child_elem = mesh_child_elem.nextSiblingElement()
dae_elem = dae_elem.nextSiblingElement()
# print(vert_pos_array)
# print(normal_array)
# print(tex_coord_array)
# print(index_array)
num_of_attributes = 3
vert_positions = []
normals = []
tex_coords = []
for i in range(0, len(index_array), num_of_attributes):
vert_pos_index = index_array[i + 0]
vert_positions.append(vert_pos_array[vert_pos_index * 3 + 0])
vert_positions.append(vert_pos_array[vert_pos_index * 3 + 1])
vert_positions.append(vert_pos_array[vert_pos_index * 3 + 2])
normal_index = index_array[i + 1]
normals.append(normal_array[normal_index * 3 + 0])
normals.append(normal_array[normal_index * 3 + 1])
normals.append(normal_array[normal_index * 3 + 2])
tex_coord_index = index_array[i + 2]
tex_coords.append(tex_coord_array[tex_coord_index * 2 + 0])
tex_coords.append(tex_coord_array[tex_coord_index * 2 + 1])
# print(vert_positions)
# print(normals)
# print(tex_coords)
output = {}
vert_positions = np.array(vert_positions, dtype=np.float32)
vert_pos_buffer = QOpenGLBuffer()
vert_pos_buffer.create()
vert_pos_buffer.bind()
vert_pos_buffer.allocate(vert_positions, len(vert_positions) * 4)
normals = np.array(normals, dtype=np.float32)
normal_buffer = QOpenGLBuffer()
normal_buffer.create()
normal_buffer.bind()
normal_buffer.allocate(normals, len(normals) * 4)
tex_coords = np.array(tex_coords, dtype=np.float32)
tex_coord_buffer = QOpenGLBuffer()
tex_coord_buffer.create()
tex_coord_buffer.bind()
tex_coord_buffer.allocate(tex_coords, len(tex_coords) * 4)
vert_buffers = VertexBuffers()
vert_buffers.vert_pos_buffer = vert_pos_buffer
vert_buffers.normal_buffer = normal_buffer
vert_buffers.tex_coord_buffer = tex_coord_buffer
vert_buffers.amount_of_vertices = int(len(index_array) / 3)
return vert_buffers
def main():
QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

How do I use roll, pitch and yaw to fuse in with my 3-D points points to make a 3-D surface?

I have a array of 3-D points in a way like that each 24 set of 3-D points are on a same plane and they form roughly a circle. I am trying to add the data of roll, pitch and yaw to the dataset such that each 24 set of point after applying the rotation angles about the axis formulates the circle in such a way that it appears to be in a tube form which is bending. The code that I am using formulates this into a surface using OpenGL. Data collected is from a IMU travelled inside a pipe.
The problem I am facing is as I apply this data to create this tube diagrammed, until rotation along Z-axis(which is roll) everything seems to be fine but when I multiply the rotation matrix along x and y axis the output is skewed and completely wrong. Can anyone point what I am doing wrong in my code
This is the code I have made for rotation
import numpy as np
roll = np.load("tube_roll.npy")
pitch = np.load("tube_pitch.npy")
yaw = np.load("tube_yaw.npy")
data = np.load("three_d.npy")
def matrix_angle(angle, a, b, c, d, e):
cos = np.cos(angle)
sin = np.sin(angle)
zer = np.zeros((angle.shape[0], 3,3))
zer[:, a[0], a[1]] = 1
zer[:, b[0], b[1]] = cos
zer[:, c[0], c[1]] = -sin
zer[:, d[0], d[1]] = sin
zer[:, e[0], e[1]] = cos
return zer
rot_along_x = matrix_angle(yaw, [0, 0], [1, 1], [1, 2], [2, 1], [2, 2])
rot_along_y = matrix_angle(pitch, [1, 1], [0, 0], [2, 0], [0, 2], [2, 2])
rot_along_z = matrix_angle(roll, [2, 2], [0, 0], [0, 1], [1, 0], [1, 1])
a = []
for j in range(0, len(rot_along_z)):
b = []
for i in range(0, 24):
dd = np.dot(rot_along_z[j], data[j, i, :])
dd = np.dot(rot_along_y[j], dd)
dd = np.dot(rot_along_x[j], dd)
b.append(dd)
a.append(b)
a = np.array(a)
np.save("three_data.npy", a)
And this is what I am using for making the 3-D view.
import OpenGL.GL as gl
import OpenGL.arrays.vbo as glvbo
from PyQt5.Qt import *
import numpy as np
import math
import sys
VS = '''
attribute vec3 position;
attribute vec3 a_Color;
uniform mat4 u_proj;
uniform mat4 u_view;
uniform mat4 u_model;
out vec3 g_color;
void main() {
gl_Position = u_proj * u_view * u_model * vec4(position, 1.0);
g_color = a_Color;
}
'''
FS = '''
#version 450
in vec3 g_color;
out vec4 outColor;
void main()
{
float d = 1.0 - gl_FragCoord.z;
outColor = vec4(g_color * d, 1.0);
}
'''
class CreateTubeWindow(QMainWindow):
def __init__(self, *args):
super(CreateTubeWindow, self).__init__()
self.setWindowTitle('3-D Pipeline View')
self.plot = GLPlotWidget3D(self)
self.setCentralWidget(self.plot)
[_, w] = Sb.screen_size()
self.setGeometry(w[2]/2-50, w[3]/2-50, w[2]/2-50, w[3]/2-50)
self.plot.show()
self.show()
class GLPlotWidget3D(QGLWidget):
def __init__(self, *args):
# QGLWidget.__init__(self)
super(GLPlotWidget3D, self).__init__()
self.parent_s = args[0]
self.parent_s = self.parent_s
self.width, self.height = 100, 100
self.right, self.left, self.top, self.bottom = 21000, -21000, 10, -10
self.data = np.zeros((3, 10, 2))
self.vbo = glvbo.VBO(self.data)
self.x = 0
self.y = 0
self.vxb, self.vyb = 0, 0
self.count = 0
# self.showMaximized()
# self.show()
def initializeGL(self):
vs = Sb.compile_vertex_shader(VS)
fs = Sb.compile_fragment_shader(FS)
self.shaders_program_tube = Sb.link_shader_program(vs, fs)
self.attrib = {a: gl.glGetAttribLocation(self.shaders_program_tube, a) for a in ['position', 'a_Color']}
self.uniform = {u: gl.glGetUniformLocation(self.shaders_program_tube, u) for u in
['u_model', 'u_view', 'u_proj']}
self.roll = np.load('tube_roll.npy')
self.pitch = np.load('tube_pitch.npy')
self.yaw = np.load('tube_yaw.npy')
self.e = np.load("three_data.npy")
self.e = np.array(self.e, dtype=np.float32)
self.color = np.zeros((self.e.shape[0]*self.e.shape[1], 3), dtype=np.float32)
self.color.fill(1.0)
self.elems = self.elements_array(self.e.shape[0], self.e.shape[1])
self.elems = np.array(self.elems, dtype=np.int32)
self.vertexbuffer = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexbuffer)
gl.glBufferData(gl.GL_ARRAY_BUFFER, self.e, gl.GL_DYNAMIC_DRAW)
self.elementbuffer = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.elementbuffer)
gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, self.elems, gl.GL_DYNAMIC_DRAW)
self.colorbuffer = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.colorbuffer)
gl.glBufferData(gl.GL_ARRAY_BUFFER, self.color, gl.GL_DYNAMIC_DRAW)
self.scaleZ = 30/self.e[:, :, 2].max()
self.right, self.left, self.top, self.bottom, self.far, self.near = self.e[:, :, 1].min(), self.e[:, :,
1].max(), self.e[:,
:,
0].min(), self.e[
:,
:,
0].max(), self.e[
:,
:,
2].max(), self.e[
:,
:,
2].min()
self.far *= self.scaleZ
self.near *= self.scaleZ
self.angle = 0.962
self.angle_y = 133.544
self.zoom = True
self.zoom_c = 90.0
self.zoom_cv = 0.0
def perspective_view(self):
# projection matrix
aspect, ta, near, far = self.width / self.height, np.tan(np.radians(self.zoom_c) / 2), 10, 50
proj = np.matrix(((1 / ta / aspect, 0, 0, 0), (0, 1 / ta, 0, 0), (0, 0, -(far + near) / (far - near), -1),
(0, 0, -2 * far * near / (far - near), 0)), np.float32)
# view matrix
view = np.matrix(((1, 0, 0, 0), (0, 0, -1, 0), (0, 1, 0, 0), (0, 0, -30, 1)), np.float32)
# model matrix
c, s = math.cos(self.angle), math.sin(self.angle)
cy, sy = math.cos(self.angle_y), math.sin(self.angle_y)
scale = np.matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, self.scaleZ, 0), (0, 0, 0, 1)), np.float32)
rotZ = np.array(((c, s, 0, 0), (-s, c, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)), np.float32)
rotY = np.matrix(((cy, 0, sy, 0), (0, 1, 0, 0), (-sy, 0, cy, 0), (0, 0, 0, 1)), np.float32)
trans = np.matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, (self.near - self.far) / 2, 1)), np.float32)
model = scale * trans * rotY * rotZ
gl.glUniformMatrix4fv(self.uniform['u_proj'], 1, gl.GL_FALSE, proj)
gl.glUniformMatrix4fv(self.uniform['u_view'], 1, gl.GL_FALSE, view)
gl.glUniformMatrix4fv(self.uniform['u_model'], 1, gl.GL_FALSE, model)
def paintGL(self):
self.resizeGL(self.width, self.height)
gl.glClearColor(0.2, 0.2, 0.2, 0)
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
gl.glEnable(gl.GL_DEPTH_TEST)
gl.glUseProgram(self.shaders_program_tube)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexbuffer)
stride = 0 # 3*self.e.itemsize
offset = None # ctypes.c_void_p(0)
loc = self.attrib['position']
gl.glEnableVertexAttribArray(loc)
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.elementbuffer)
self.perspective_view()
loc = self.attrib['a_Color']
gl.glEnableVertexAttribArray(loc)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.colorbuffer)
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)
gl.glDrawElements(gl.GL_TRIANGLE_STRIP, self.elems.size, gl.GL_UNSIGNED_INT, None)
def resizeGL(self, width, height):
self.width, self.height = width, height
gl.glViewport(0, 0, width, height)
def mouseMoveEvent(self, e):
if self.zoom:
v = self.x-e.x()
if v > 2:
self.angle -= 0.01
elif v < -2:
self.angle += 0.01
self.x = e.x()
w = self.y-e.y()
if w > 2:
self.angle_y += 0.01
elif w < -2:
self.angle_y -= 0.01
self.y = e.y()
else:
if e.x() > self.zoom_cv:
self.zoom_c -= 1
else:
self.zoom_c += 1
self.zoom_cv = e.x()
self.update()
def keyPressEvent(self, k):
if k.key() == 16777249:
self.zoom = False
def keyReleaseEvent(self, k):
if k.key() == 16777249:
self.zoom = True
def wheelEvent(self, w):
e = w.angleDelta().y()
if e > 0:
self.zoom_c -= 1
else:
self.zoom_c += 1
self.update()
def elements_array(self, a, b):
ring_c = b
slice_c = a
my_si = np.arange(slice_c - 1)
my_elems = np.repeat(ring_c * my_si, 2)
temp = np.empty(2 * my_si.size, my_si.dtype)
temp[0::2] = my_si
temp[1::2] = my_si + 1
my_ri = np.arange(ring_c + 1)
my_ie = np.expand_dims(my_ri % ring_c, axis=1)
my_si = temp * ring_c + my_ie
my_elems = np.vstack((my_elems, my_si))
N = my_elems.shape[1] // (slice_c - 1)
return my_elems.reshape((my_elems.shape[0], -1, N)).swapaxes(0, 1).reshape(
(np.prod(my_elems.shape), -1)).squeeze()
if __name__ == '__main__':
app = QApplication(sys.argv)
editor = CreateTubeWindow()
sys.exit(app.exec_())
What I feel is I am doing something wrong with my rotation matrix as the output of rotation along Z-axis seems wrong as it's initially in a sort of 2-D plane but as we go further matrix multiplication increased the value of x and y coordinate also which should not happen.
Also below are the data file used
https://drive.google.com/file/d/177knYTSmlWcC1RQjcXf-CKICWr09MU_i/view?usp=sharing
Either use numpy.matmul for matrix multiplications:
rot_mat = np.matmul(rot_along_x, np.matmul(rot_along_y, rot_along_z))
or use the matrix multiplication (#) operator (see array ):
rot_mat = rot_along_z # rot_along_y # rot_along_x
Use numpy.dot to transform the vertices:
a = np.array([[np.dot(v, rot_mat) for v in row] for row in data])

What is the lookat matrix multiplication function in python for a dragon superbible opengl example?

I mostly ported over the dragon example from SB OpenGL. The output gif of the program is below the code.
My question what is what's the lookat function in python?
Supporting files: dragon.zip simply put pydragon.py into the folder 'dragon' and run
Source code of pydragon.py
#!/usr/bin/python3
import sys
import time
sys.path.append("./shared")
from sbmloader import SBMObject # location of sbm file format loader
from ktxloader import KTXObject # location of ktx file format loader
from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44
fullscreen = True
import numpy.matlib
import numpy as np
import math
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
clear_program = GLuint(0)
append_program = GLuint(0)
resolve_program = GLuint(0)
class textures:
color = GLuint(0)
normals = GLuint(0)
class uniforms_block:
mv_matrix = (GLfloat * 16)(*identityMatrix)
view_matrix = (GLfloat * 16)(*identityMatrix)
proj_matrix = (GLfloat * 16)(*identityMatrix)
uniforms_buffer = GLuint(0)
class uniforms:
mvp = GLuint(0)
fragment_buffer = GLuint(0)
head_pointer_image = GLuint(0)
atomic_counter_buffer = GLuint(0)
dummy_vao = GLuint(0)
uniform = uniforms()
myobject = SBMObject()
def length(v):
return math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])
def normalize(v):
l = length(v)
#if (v[0] == 0 and v[1] == 0 and v[2] ==0):
# return [0.0, 1/3, 0.0]
return [v[0]/l, v[1]/l, v[2]/l]
def dot(v0, v1):
return v0[0]*v1[0]+v0[1]*v1[1]+v0[2]*v1[2]
def cross(v0, v1):
return [
v0[1]*v1[2]-v1[1]*v0[2],
v0[2]*v1[0]-v1[2]*v0[0],
v0[0]*v1[1]-v1[0]*v0[1]]
def m3dLookAt(eye, target, up):
mz = normalize( (eye[0]-target[0], eye[1]-target[1], eye[2]-target[2]) ) # inverse line of sight
mx = normalize( cross( up, mz ) )
my = normalize( cross( mz, mx ) )
tx = dot( mx, eye )
ty = dot( my, eye )
tz = -dot( mz, eye )
return np.array([mx[0], my[0], mz[0], 0, mx[1], my[1], mz[1], 0, mx[2], my[2], mz[2], 0, tx, ty, tz, 1])
def scale(s):
return [s,0,0,0, 0,s,0,0, 0,0,s,0, 0,0,0,1]
def link_from_shaders(shaders, shader_count, delete_shaders, check_errors=False):
program = GLuint(0)
program = glCreateProgram()
for i in range(0, shader_count):
glAttachShader(program, shaders[i]);
glLinkProgram(program);
if (delete_shaders):
for i in range(0, shader_count):
glDeleteShader(shaders[i]);
return program
def shader_load(filename, shader_type):
result = GLuint(0)
with open ( filename, "rb") as data:
result = glCreateShader(shader_type)
glShaderSource(result, data.read() )
glCompileShader(result)
return result
def load_shaders():
global clear_program
global append_program
global resolve_program
global uniform
shaders = [GLuint(0), GLuint(0)]
shaders[0] = shader_load("fragmentlist_shaders/clear.vs.glsl", GL_VERTEX_SHADER);
shaders[1] = shader_load("fragmentlist_shaders/clear.fs.glsl", GL_FRAGMENT_SHADER);
if (clear_program):
glDeleteProgram(clear_program);
clear_program = link_from_shaders(shaders, 2, True);
shaders[0] = shader_load("fragmentlist_shaders/append.vs.glsl", GL_VERTEX_SHADER);
shaders[1] = shader_load("fragmentlist_shaders/append.fs.glsl", GL_FRAGMENT_SHADER);
if (append_program):
glDeleteProgram(append_program);
append_program = link_from_shaders(shaders, 2, True);
uniform.mvp = glGetUniformLocation(append_program, "mvp");
shaders[0] = shader_load("fragmentlist_shaders/resolve.vs.glsl", GL_VERTEX_SHADER);
shaders[1] = shader_load("fragmentlist_shaders/resolve.fs.glsl", GL_FRAGMENT_SHADER);
if (resolve_program):
glDeleteProgram(resolve_program)
resolve_program = link_from_shaders(shaders, 2, True);
class Scene:
def __init__(self, width, height):
global uniforms_buffer
global fragment_buffer
global atomic_counter_buffer
global head_pointer_image
global dummy_vao
global myobject
self.width = width
self.height = height
load_shaders()
glGenBuffers(1, uniforms_buffer)
glBindBuffer(GL_UNIFORM_BUFFER, uniforms_buffer)
glBufferData(GL_UNIFORM_BUFFER, sizeof(GLfloat * 16 *3), None, GL_DYNAMIC_DRAW)
myobject.load("dragon.sbm")
glGenBuffers(1, fragment_buffer)
glBindBuffer(GL_SHADER_STORAGE_BUFFER, fragment_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, 1024 * 1024 * 16, None, GL_DYNAMIC_COPY)
glGenBuffers(1, atomic_counter_buffer);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomic_counter_buffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, 4, None, GL_DYNAMIC_COPY);
head_pointer_image = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, head_pointer_image);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1024, 1024);
glGenVertexArrays(1, dummy_vao);
glBindVertexArray(dummy_vao);
def display(self):
green = [ 0.0, 0.1, 0.0, 0.0 ]
currentTime = time.time()
f = currentTime
zeros = [ 0.0, 0.0, 0.0, 0.0 ]
gray = [ 0.1, 0.1, 0.1, 0.0 ]
ones = [ 1.0 ]
glViewport(0, 0, self.width , self.height);
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT);
glUseProgram(clear_program);
glBindVertexArray(dummy_vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glUseProgram(append_program)
model_matrix = (GLfloat * 16)(*identityMatrix)
model_matrix = scale(6.0)
view_matrix = (GLfloat * 16)(*identityMatrix)
view_matrix = m3dLookAt([math.cos(f * 0.35) * 120.0, math.cos(f * 0.4) * 30.0, math.sin(f * 0.35) * 120.0],
[0.0, -20.0, 0.0],
[0.0, 1, 0.0])
mv_matrix = (GLfloat * 16)(*identityMatrix)
mv_matrix = m3dMultiply(view_matrix , model_matrix)
proj_matrix = (GLfloat * 16)(*identityMatrix)
proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 0.1, 1000.0)
glUniformMatrix4fv(uniform.mvp, 1, GL_FALSE, m3dMultiply(proj_matrix , mv_matrix))
zero = 0;
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomic_counter_buffer)
# next line not working ????
#glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sys.getsizeof(zero), zero);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, fragment_buffer)
glBindImageTexture(0, head_pointer_image, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI)
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT)
myobject.render()
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT)
glUseProgram(resolve_program)
glBindVertexArray(dummy_vao)
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT)
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
print('done')
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
start = time.time()
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - Fragment List')
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
many_cubes = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
The output is supposed to appear like the following:
Ported from fragmentlist.cpp found there from the Superbible OpenGL 7th ed.
Current Question:
Any ideas why the texture rendered on the dragon is not translucent as the expected output has it?
The view space is the local system which is defined by the point of view onto the scene.
The position of the view, the line of sight and the upwards direction of the view, define a coordinate system relative to the world coordinate system. The objects of a scene have to be drawn in relation to the view coordinate system, to be "seen" from the viewing position. The inverse matrix of the view coordinate system is named the view matrix. This matrix transforms from world coordinates to view coordinates.
The code below defines a matrix that exactly encapsulates the steps necessary to calculate a look at the scene:
Converting model coordinates into view system coordinates.
Rotation, to look in the direction of the view.
Movement to the eye position.
Euclidean length of a vector:
def length(v):
return math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])
Unit vector:
def normalize(v):
l = length(v)
return [v[0]/l, v[1]/l, v[2]/l]
Dot product:
def dot(v0, v1):
return v0[0]*v1[0]+v0[1]*v1[1]+v0[2]*v1[2]
Cross product:
def cross(v0, v1):
return [
v0[1]*v1[2]-v1[1]*v0[2],
v0[2]*v1[0]-v1[2]*v0[0],
v0[0]*v1[1]-v1[0]*v0[1]]
The following code does the same as gluLookAt or glm::lookAt does:
The parameter eye is the point of view, target is the point which is looked at and up is the upwards direction.
def m3dLookAt(eye, target, up):
mz = normalize( (eye[0]-target[0], eye[1]-target[1], eye[2]-target[2]) ) # inverse line of sight
mx = normalize( cross( up, mz ) )
my = normalize( cross( mz, mx ) )
tx = dot( mx, eye )
ty = dot( my, eye )
tz = -dot( mz, eye )
return np.array([mx[0], my[0], mz[0], 0, mx[1], my[1], mz[1], 0, mx[2], my[2], mz[2], 0, tx, ty, tz, 1])
Use it like this:
view_matrix = m3dLookAt([0, 0, 20], [0, 0, 0], [0, 1, 0])

How to get clip distances and textures on green dragon rendering

The source is of a green dragon rendering. My question is how to get the clip distances working on it? Also, the textures are not appearing as the expected output. Any ideas what can be modified in the source code to render the program as expected?
Update: With the excellent help and superb answers of Rabbid76 the clip distance is working and texture loading is working! Thank You.
Bonus example: clipdistance_torus_package.zip a clip distance example with a torus and textures!
Expected output:
Files to run: clipdistance_dragon.zip
#!/usr/bin/python3
import sys
import time
import ctypes
fullscreen = True
sys.path.append("./shared")
from sbmloader import SBMObject # location of sbm file format loader
from ktxloader import KTXObject
from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44, \
scale, m3dLookAt, normalize
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
import numpy as np
from math import cos, sin
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
myobject = SBMObject()
render_program = GLuint(0)
paused = False
class uniforms:
proj_matrix = GLint(0)
mv_matrix = GLint(0)
clip_plane = GLint(0)
clip_sphere = GLint(0)
uniform = uniforms()
def shader_load(filename, shader_type):
result = GLuint(0)
with open ( filename, "rb") as data:
result = glCreateShader(shader_type)
glShaderSource(result, data.read() )
glCompileShader(result)
if not glGetShaderiv(result, GL_COMPILE_STATUS):
print( 'compile error:' )
print( glGetShaderInfoLog(result) )
return result
def link_from_shaders(shaders, shader_count, delete_shaders, check_errors=False):
program = GLuint(0)
program = glCreateProgram()
for i in range(0, shader_count):
glAttachShader(program, shaders[i])
glLinkProgram(program)
if not glGetProgramiv(program, GL_LINK_STATUS):
print( 'link error:' )
print( glGetProgramInfoLog(program) )
if (delete_shaders):
for i in range(0, shader_count):
glDeleteShader(shaders[i])
return program
def load_shaders():
global render_program
global uniform
if (render_program):
glDeleteProgram(render_program);
shaders = [
shader_load("render.vs.glsl", GL_VERTEX_SHADER),
shader_load("render.fs.glsl", GL_FRAGMENT_SHADER)
]
render_program = link_from_shaders(shaders, 2, True)
uniform.proj_matrix = glGetUniformLocation(render_program, "proj_matrix");
uniform.mv_matrix = glGetUniformLocation(render_program, "mv_matrix");
uniform.clip_plane = glGetUniformLocation(render_program, "clip_plane");
uniform.clip_sphere = glGetUniformLocation(render_program, "clip_sphere");
tex_dragon=None
class Scene:
def __init__(self, width, height):
global myobject, tex_dragon
myobject.load("dragon.sbm");
load_shaders()
ktxobj = KTXObject()
tex_dragon = ktxobj.ktx_load("pattern1.ktx")
def display(self):
global paused
currentTime = time.time()
black = [ 0.0, 0.0, 0.0, 0.0 ]
one = 1.0
last_time = 0.0
total_time = 0.0
if (not paused):
total_time += (currentTime - last_time)
last_time = currentTime
f = total_time
glClearBufferfv(GL_COLOR, 0, black)
glClearBufferfv(GL_DEPTH, 0, one)
glUseProgram(render_program)
proj_matrix = (GLfloat * 16)(*identityMatrix)
proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 0.1, 1000.0)
T1 = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(T1, 0.0, 0.0, -15.0)
RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, f * 0.34, 0.0, 1.0, 0.0)
T2 = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(T2, 0.0, -4.0, 0.0)
mv_matrix = (GLfloat * 16)(*identityMatrix)
mv_matrix = m3dMultiply(T1, m3dMultiply(RY, T2))
RX = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RX, f * 6.0, 1.0, 0.0, 0.0)
RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, f * 7.3, 0.0, 1.0, 0.0)
plane_matrix = (GLfloat * 16)(*identityMatrix)
plane_matrix = m3dMultiply(RX , RY )
plane = plane_matrix[0:4]
plane[3] = 0
plane = normalize(plane)
clip_sphere = [sin(f * 0.7) * 3.0, cos(f * 1.9) * 3.0, sin(f * 0.1) * 3.0, cos(f * 1.7) + 2.5]
glUniformMatrix4fv(uniform.proj_matrix, 1, GL_FALSE, proj_matrix)
glUniformMatrix4fv(uniform.mv_matrix, 1, GL_FALSE, mv_matrix)
glUniform4fv(uniform.clip_plane, 1, plane)
glUniform4fv(uniform.clip_sphere, 1, clip_sphere)
glEnable(GL_DEPTH_TEST)
glEnable(GL_CLIP_DISTANCE0)
glEnable(GL_CLIP_DISTANCE1)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, tex_dragon)
myobject.render()
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
global paused
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
elif key == b'p' or key == b'P':
paused = not paused
elif key == b'r' or key == b'R':
pass
print('done')
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
start = time.time()
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - Clip Distance')
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
ported into python from clipdistance.cpp , source of sbmobject.cpp just in case sbmloader.py is in question for the texture issue.
The C++ code from the example
vmath::mat4 plane_matrix = vmath::rotate(f * 6.0f, 1.0f, 0.0f, 0.0f) *
vmath::rotate(f * 7.3f, 0.0f, 1.0f, 0.0f);
corresponds to the following python code
RX = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RX, f * 6.0, 1.0, 0.0, 0.0)
RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, f * 7.3, 0.0, 1.0, 0.0)
plane_matrix = (GLfloat * 16)(*identityMatrix)
plane_matrix = m3dMultiply(RX , RY)
Note, you've to swap RX and RY in the matrix multiplication.
Your functions length and normalize can only deal with vectors which have 3 components (x, y, z). In compare the C++ function vmath::normalize from the example can handle vectors with 4 components (x, y, z, w), too.
Further the "division by zero" handling is missing in normalize.
Adapt the functions normalize and length, to deal with any vector size.
If the length of a vector is 0, then all its components are 0. There is no correct solution for that, so just return a copy of the vector itself.
def length(v):
sum_sq = sum([s*s for s in v])
return math.sqrt(sum_sq)
def normalize(v):
l = length(v)
if l == 0.0:
return v[:]
return [s/l for s in v]
Now you can port the C++ code from the example
vmath::vec4 plane = plane_matrix[0];
plane[3] = 0.0f;
plane = vmath::normalize(plane);
very straight to python:
plane = plane_matrix[0:4]
plane[3] = 0
plane = normalize(plane)
Further, there is an issue in the sbmloader module.
Only the array of vertex coordinates and texture coordinates is specified. The normal vectors are skipped.
Just skip the line
if attrib.name=='position' or attrib.name=='map1':
to fix the issue:
for attrib_i, attrib in enumerate(vertex_attrib_chunk.attrib_data):
#if attrib.name=='position' or attrib.name=='map1':
glVertexAttribPointer(attrib_i,
attrib.size, attrib.type,
GL_TRUE if (attrib.flags & SB6M_VERTEX_ATTRIB_FLAG_NORMALIZED) != 0 else GL_FALSE,
attrib.stride, ctypes.c_void_p(int(attrib.data_offset)))
glEnableVertexAttribArray(attrib_i)
If you additionally want to wrap a texture to the model, then you've to add the texture coordinate attribute to the vertex shader:
layout (location = 2) in vec2 tc;
And to pass it by an output to the next shader stage
out VS_OUT
{
vec3 N;
vec3 L;
vec3 V;
vec2 T;
} vs_out;
void main()
{
// ...
vs_out.T = tc;
// ...
}
In the fragment shader you've to add the texture sampler uniform
layout (binding = 0) uniform sampler2D tex;
Read the color form the texture
vec4 texColor = texture(tex, fs_in.T);
Multiply the output color by the texture color
color = vec4(diffuse + specular + rim, 1.0) * texColor;
Fragment shader (note, I changed diffuse_albedo):
#version 420 core
// Output
layout (location = 0) out vec4 color;
// Input from vertex shader
in VS_OUT
{
vec3 N;
vec3 L;
vec3 V;
vec2 T;
} fs_in;
// Material properties
uniform vec3 diffuse_albedo = vec3(0.5);
uniform vec3 specular_albedo = vec3(0.7);
uniform float specular_power = 128.0;
uniform vec3 rim_color = vec3(0.1, 0.2, 0.2);
uniform float rim_power = 5.0;
layout (binding = 0) uniform sampler2D tex;
vec3 calculate_rim(vec3 N, vec3 V)
{
float f = 1.0 - dot(N, V);
f = smoothstep(0.0, 1.0, f);
f = pow(f, rim_power);
return f * rim_color;
}
void main(void)
{
// Normalize the incoming N, L and V vectors
vec3 N = normalize(fs_in.N);
vec3 L = normalize(fs_in.L);
vec3 V = normalize(fs_in.V);
// Calculate R locally
vec3 R = reflect(-L, N);
// Compute the diffuse and specular components for each fragment
vec3 diffuse = max(dot(N, L), 0.0) * diffuse_albedo;
vec3 specular = pow(max(dot(R, V), 0.0), specular_power) * specular_albedo;
vec3 rim = calculate_rim(N, V);
// read color from the texture
vec4 texColor = texture(tex, fs_in.T);
// Write final color to the framebuffer
color = vec4(diffuse + specular + rim, 1.0) * texColor;
}
I recommend to add shader compile and link error logging:
glCompileShader(result)
if not glGetShaderiv(result, GL_COMPILE_STATUS):
print( 'compile error:' )
print( glGetShaderInfoLog(result) )
glLinkProgram(program)
if not glGetProgramiv(program, GL_LINK_STATUS):
print( 'link error:' )
print( glGetProgramInfoLog(program) )
Read a texture at the initialization of the application
class Scene:
def __init__(self, width, height):
global myobject, tex_dragon
myobject.load("dragon.sbm")
load_shaders()
ktxobj = KTXObject()
tex_dragon = ktxobj.ktx_load("texture_file_name.ktx")
Bind the texture before drawing the model
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, tex_dragon)
myobject.render()

Categories