Sending a pointer to glVertexAttributePointer in Pyrthon - python

I need to pass an offset pointer to glVertexAttribPointer in Python. Currently I have coming out of pythons console window when an instance of Box class is created:
C:\Users\phill\PycharmProjects\texturebox\venv\Scripts\python.exe C:\Users\phill\PycharmProjects\texturebox\main.py
Traceback (most recent call last):
File "C:\Users\phill\PycharmProjects\texturebox\main.py", line 29, in <module>
main()
File "C:\Users\phill\PycharmProjects\texturebox\main.py", line 18, in main
m_box = Box()
^^^^^
File "C:\Users\phill\PycharmProjects\texturebox\Box.py", line 60, in __init__
array_colors_offset(*vertices_list)
IndexError: invalid index
Process finished with exit code 1
Does anyone know how an offset pointer might be passed in Python?
The corresponding C code for the (void *) looks like this:
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
Box.py:
from OpenGL import GL as GL
import ctypes
class Box:
def __init__(self):
#3 floats for vertices x,y,z 3 floats for color R G B 2 floats for tex co-ords
vertices_list = [
0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0,
0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0,
-0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0
]
indices_list = [
0, 1, 3,
1, 2, 3
]
self.EBO = None
self.VBO = None
self.VAO = None
self.VAO = GL.glGenVertexArrays(1)
self.VBO = GL.glGenBuffers(1)
self.EBO = GL.glGenBuffers(1)
GL.glBindVertexArray(self.VAO)
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.VBO)
array_type_vertices = (GL.GLfloat * len(vertices_list))
GL.glBufferData(GL.GL_ARRAY_BUFFER, len(vertices_list) * ctypes.sizeof(ctypes.c_float),
array_type_vertices(*vertices_list), GL.GL_STATIC_DRAW)
GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, self.EBO)
array_type_indices = (GL.GLint * len(indices_list))
GL.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, len(indices_list) * ctypes.sizeof(ctypes.c_ulong),
array_type_indices(*indices_list), GL.GL_STATIC_DRAW)
GL.glVertexAttribPointer(
0,
3,
GL.GL_FLOAT,
False,
8 * ctypes.sizeof(ctypes.c_float),
None
)
GL.glEnableVertexAttribArray(0)
# attempting to pass the pointer
array_colors_offset = (GL.GLfloat * 3)
GL.glVertexAttribPointer(1,
3,
GL.GL_FLOAT,
False,
8 * ctypes.sizeof(ctypes.c_float),
array_colors_offset(*vertices_list)
)
GL.glEnableVertexAttribArray(1)
array_tex_offset = (GL.GLfloat * 6)
GL.glVertexAttribPointer(2,
2,
GL.GL_FLOAT,
GL.GL_FALSE,
8 * ctypes.sizeof(ctypes.c_float),
array_tex_offset(*vertices_list)
)
print("Box()")
So I started with code from gitlabs opengl python examples. The basic triangle does not seem to enumerate how to pass a pointer to glVertexAtribPointer. So there is
an offset to pass as a (void *) according to the C code I have that I am moving over into Python.

(GL.GLfloat * 6)(*vertices_list) does not do what you expect. It creates an array with 6 elements.
See pyopengl, glVertexAttribPointer. The argument must be a void pointer. You can use the ctypes.c_void_p to create a void pointer, e.g.:
GL.glVertexAttribPointer(1,
3,
GL.GL_FLOAT,
False,
8 * ctypes.sizeof(ctypes.c_float),
ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))
)

Related

Why PyQt6 won't properly update paintGL after adding updated array through glBufferSubData?

When I press left or right key to rotate 4 points around origin, the screen won't update correctly causing broken rendering. The vertex array is properly introduced to buffer, but I have not found relevant information regarding why this won't render properly.
Here are the relevant codes:
import Shader
from Math_3d import Vector3f
from OpenGL.GL import *
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
from PyQt6.QtCore import Qt
class GLWidget(QOpenGLWidget):
def __init__(self):
super().__init__()
# Vertex Buffer Object
# Create point vertex data
self.v3f = Vector3f([[0.5, 0.5, 0.0],
[-0.5, -0.5, 0.0],
[0.5, -0.5, 0.0],
[-0.5, 0.5, 0.0]])
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
def initializeGL(self):
program = glCreateProgram()
buffer = glGenBuffers(1)
# Request program and shader slots from the GPU
vertex = glCreateShader(GL_VERTEX_SHADER)
fragment = glCreateShader(GL_FRAGMENT_SHADER)
# Set shader sources
glShaderSource(vertex, Shader.vertex_code)
glShaderSource(fragment, Shader.fragment_code)
# Compile shaders and check that they have compiled
glCompileShader(vertex)
glCompileShader(fragment)
if not glGetShaderiv(vertex, GL_COMPILE_STATUS):
report_shader = glGetShaderInfoLog(vertex)
print(report_shader)
raise RuntimeError("Vertex shader compilation error")
if not glGetShaderiv(fragment, GL_COMPILE_STATUS):
report_frag = glGetShaderInfoLog(fragment)
print(report_frag)
raise RuntimeError("Fragment shader compilation error")
# Link shaders to program
glAttachShader(program, vertex)
glAttachShader(program, fragment)
glLinkProgram(program)
if not glGetProgramiv(program, GL_LINK_STATUS):
print(glGetProgramInfoLog(program))
raise RuntimeError('Linking error')
# Get rid of shaders
glDetachShader(program, vertex)
glDetachShader(program, fragment)
# Make default program to run
glUseProgram(program)
# Make this buffer the default one
glBindBuffer(GL_ARRAY_BUFFER, buffer)
# Vertex Array Buffer
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
strides = int(self.v3f.buffdata.nbytes/self.v3f.buffdata.itemsize)
loc = glGetAttribLocation(program, 'position')
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, 3, GL_FLOAT, False, strides, None)
glBufferData(GL_ARRAY_BUFFER, self.v3f.buffdata.nbytes, self.v3f.buffdata, GL_DYNAMIC_DRAW)
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT)
glPointSize(20)
glDrawArrays(GL_POINTS, 0, self.v3f.buffdata.itemsize)
def keyPressEvent(self, e):
if e.key() == Qt.Key.Key_Left:
self.v3f.Rotate(-0.1)
print("Left")
elif e.key() == Qt.Key.Key_Right:
self.v3f.Rotate(0.1)
print("Right")
elif e.key() == Qt.Key.Key_Escape or Qt.Key.Key_Q:
exit()
self.update()
Other module:
from OpenGL.GL import *
import numpy as np
np.set_printoptions(precision=3, suppress=True)
class Vector3f:
def __init__(self, ndarray):
self.data = np.array([ndarray], dtype=np.float32)
# Convert n-dimension arrays to one long array
self.buffdata = self.data.ravel()
self.orig = self.data
self.__rval = 0.0
self.__sval = 0.0
def Rotate(self, val): # Rotation Matrix
self.__rval += val # Create a memory value and add to that value
m1 = np.asmatrix(self.data) # Convert n-dimension array to matrix
rot2 = np.matrix([[np.cos(self.__rval), np.sin(-self.__rval), 0.0], # Create rotation matrix
[np.sin(self.__rval), np.cos(self.__rval), 0.0],
[0.0, 0.0, 0.0]], dtype=np.float32)
rotator = m1 * rot2 # Matrix multiplication
self.buffdata = rotator.ravel() # Convert result to array to be used on GPU buffer
glBufferSubData(GL_ARRAY_BUFFER, 0, self.buffdata.data.nbytes, self.buffdata.data)
For any OpenGL instruction a current and valid OpenGL Context is required. QOpenGLWidget creates an OpenGL context, but the context is only made current before the initializeGL, paintGL, or resizeGL callback.
Therefore you cannot use OpenGL instructions outside the scope of these functions and this is the reason why the call to glBufferSubData fails when called through keyPressEvent.

content[0][2] must be an attribute not NoneType moderngl

I am trying to test the following simple example wherein I pass something to the vertex shader and read its output to print.
import moderngl
import numpy as np
import struct
vertex_shader_source = '''
#version 330
in vec3 attPosition;
out vec2 varScreenCoord;
void main ()
{
varScreenCoord = vec2(1.0, 1.5);
}
'''
ctx = moderngl.create_standalone_context()
prog = ctx.program(vertex_shader=vertex_shader_source, varyings=['varScreenCoord'])
# input
verts = np.array([[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0]], dtype='f4')
verts_buf = ctx.buffer(verts.tobytes())
# output
n, c = verts.shape
varScreenCoord = np.zeros((n, c-1)).astype(np.float32)
varScreenCoord_buf = ctx.buffer(varScreenCoord.tobytes())
vao = ctx.vertex_array(prog, content=[
(verts_buf, '3f', 'attPosition')
])
vao.transform(varScreenCoord_buf, vertices=n)
data = struct.unpack("{}f".format(n*2), varScreenCoord_buf.read())
for i in range(0, n*2):
print("value = {}".format(data[i]))
I get the following error when I run this
Traceback (most recent call last):
File "/somepath/moderngl_sample.py", line 32, in <module>
vao = ctx.vertex_array(prog, content=[
File "/somepath/lib/python3.9/site-packages/moderngl/context.py", line 1140, in vertex_array
return self._vertex_array(*args, **kwargs)
File "/somepath/lib/python3.9/site-packages/moderngl/context.py", line 1169, in _vertex_array
res.mglo, res._glo = self.mglo.vertex_array(program.mglo, content, index_buffer_mglo,
moderngl.error.Error: content[0][2] must be an attribute not NoneType
I have checked the buffer format here and it seems to be correct. This indicates that the name might not be referenced in the shader, but my shader clearly has that vertex attribute.
What could be causing this issue?
Thanks.
After using the attPosition value in the shader, this issue was fixed. For example, the shader must be changed as follows
#version 330
in vec3 attPosition;
out vec2 varScreenCoord;
void main ()
{
varScreenCoord = vec2(1.0, 1.5) * attPosition.xy;
}

Appending to dictonary with class?

class Clinic:
def __init__(self, medicationList):
self._medicationList = {}
def addMedication(self, medication):
self._medicationList.append(medication)
def main():
m1 = ('CP12', 'Chloro-6 pheniramine-X', 0.08, 4.0, 3)
m2 = ('DM01', 'Dex-2 trimethorphan-0', 0.25, 15.0, 2)
m3 = ('LH03', 'Lyso-X Hydrochloride', 1.00, 10.0, 1)
cl = Clinic()
cl.addMedication('CP12', 'Chloro-6 pheniramine-X', 0.08, 4.0, 3)
print(c1)
main()
I am trying to append the medicine into the _medicationList. How do I go about doing it? So the dictionary will be something like
{m1 : ['CP12', 'Chloro-6 pheniramine-X', 0.08, 4.0, 3 ] , m2 : ['DM01', 'Dex-2 trimethorphan-0', 0.25, 15.0, 2]}`
It looks like you want to be using a list instead of a dictionary. Maybe something like this?
from typing import List, Tuple
Medication = Tuple[str, str, float, float, int]
class Clinic:
def __init__(self):
self._medications: List[Medication] = []
def add_medication(self, medication: Medication) -> None:
self._medications.append(medication)
def __str__(self) -> str:
return str(self._medications)
def main() -> None:
m1 = ('CP12', 'Chloro-6 pheniramine-X', 0.08, 4.0, 3)
m2 = ('DM01', 'Dex-2 trimethorphan-0', 0.25, 15.0, 2)
m3 = ('LH03', 'Lyso-X Hydrochloride', 1.00, 10.0, 1)
cl = Clinic()
for m in m1, m2, m3:
cl.add_medication(m)
print(cl)
if __name__ == '__main__':
main()
If you want it to be a dict instead of a list, maybe something more like this?
from typing import Dict, Tuple
Medication = Tuple[str, str, float, float, int]
class Clinic:
def __init__(self):
self._medications: Dict[str, Medication] = {}
def add_medication(self, name: str, medication: Medication) -> None:
self._medications[name] = medication
def __str__(self) -> str:
return str(self._medications)
def main() -> None:
m1 = ('CP12', 'Chloro-6 pheniramine-X', 0.08, 4.0, 3)
m2 = ('DM01', 'Dex-2 trimethorphan-0', 0.25, 15.0, 2)
m3 = ('LH03', 'Lyso-X Hydrochloride', 1.00, 10.0, 1)
cl = Clinic()
cl.add_medication("m1", m1)
cl.add_medication("m2", m2)
cl.add_medication("m3", m3)
print(cl)
if __name__ == '__main__':
main()
class Clinic:
def __init__(self):
self._medication_list = {}
def add_medication(self, medication):
self._medication_list[medication[0]] = medication[1:]
def get_medication_list(self):
return self._medication_list
def main():
m1 = ('m1','CP12', 'Chloro-6 pheniramine-X', 0.08, 4.0, 3)
m2 = ('m2','DM01', 'Dex-2 trimethorphan-0', 0.25, 15.0, 2)
m3 = ('m3','LH03', 'Lyso-X Hydrochloride', 1.00, 10.0, 1)
lst = [m1,m2,m3]
cl = Clinic()
for m in lst:
cl.add_medication(m)
print(cl.get_medication_list())
main()
is this what you wanted?
1.Your self._medicationList is not a list first (I think you show assign to an empty dictionary instead)
Your addMedication() function takes only one parameter, you've provided 6, I guess you should use '*'
Here is the code that works, try running it:
class Clinic:
def __init__(self):
self.medicationList = dict()
def addMedication(self, name, *medication):
self.medicationList.update({name: list(medication)})
def main():
m1 = ('CP12', 'Chloro-6 pheniramine-X', 0.08, 4.0, 3)
m2 = ('DM01', 'Dex-2 trimethorphan-0', 0.25, 15.0, 2)
m3 = ('LH03', 'Lyso-X Hydrochloride', 1.00, 10.0, 1)
cl = Clinic()
cl.addMedication('CP12', 'Chloro-6 pheniramine-X', 0.08, 4.0, 3)
print(cl.medicationList)
main()

pyOpenGL Triangle isn't being drawn

I just started learning pyOpenGL and ran into an issue, my first project is very simple: I'm trying to open a window and draw a simple triangle using shaders. I'm using glfw to create the window and everything compiles properly, but the Triangle isn't being drawn.
Is something wrong with my main loop or my use of a vertex buffer? My shaders and program objects (seem to) work.
Any help would be greatly appreciated.
import OpenGL.GL
import OpenGL
import glfw
import numpy
def createAndCompileShader(type, source):
shader = OpenGL.GL.glCreateShader(type)
OpenGL.GL.glShaderSource(shader, source)
OpenGL.GL.glCompileShader(shader)
result = OpenGL.GL.glGetShaderiv(shader, OpenGL.GL.GL_COMPILE_STATUS)
if result != 1:
raise Exception("Shader didn't compile properly\nShader compilation Log:\n" + str(OpenGL.GL.glGetShaderInfoLog(shader)))
return shader
def createProgramWithShaders(shaders):
ProgramIdentification = OpenGL.GL.glCreateProgram()
for s in shaders:
OpenGL.GL.glAttachShader(ProgramIdentification, s)
OpenGL.GL.glLinkProgram(ProgramIdentification)
linkStatus = OpenGL.GL.glGetProgramiv(ProgramIdentification, OpenGL.GL.GL_LINK_STATUS)
infoLogLength = OpenGL.GL.glGetProgramiv(ProgramIdentification, OpenGL.GL.GL_INFO_LOG_LENGTH)
for s in shaders:
OpenGL.GL.glDetachShader(ProgramIdentification, s)
for s in shaders:
OpenGL.GL.glDeleteShader(s)
return ProgramIdentification
glfw.init()
glfw.window_hint(glfw.SAMPLES, 4)
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, OpenGL.GL.GL_TRUE)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
window = glfw.create_window(1000,800,"TESTING GLFW", None, None)
glfw.make_context_current(window)
glfw.set_input_mode(window,glfw.STICKY_KEYS, OpenGL.GL.GL_TRUE)
VAO = OpenGL.GL.glGenVertexArrays(1)
OpenGL.GL.glBindVertexArray(VAO)
vektoren = numpy.array([-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0], dtype='float32')
vertexBuffer = OpenGL.GL.glGenBuffers(1)
OpenGL.GL.glBindBuffer(OpenGL.GL.GL_ARRAY_BUFFER, vertexBuffer)
OpenGL.GL.glBufferData(OpenGL.GL.GL_ARRAY_BUFFER,len(vektoren), vektoren, OpenGL.GL.GL_STATIC_DRAW)
VertexSource = open("simpleVertexShader.glsl")
FragmentSource = open("simpleFragmentShader.glsl")
shaders = [createAndCompileShader(OpenGL.GL.GL_VERTEX_SHADER, VertexSource.read()), createAndCompileShader(OpenGL.GL.GL_FRAGMENT_SHADER, FragmentSource.read())]
VertexSource.close()
FragmentSource.close()
ProgramID = createProgramWithShaders(shaders)
OpenGL.GL.glClearColor(0.0,1.0,0.0,1.0)
while not glfw.window_should_close(window) and glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS:
OpenGL.GL.glClear( OpenGL.GL.GL_COLOR_BUFFER_BIT)
OpenGL.GL.glUseProgram(ProgramID)
OpenGL.GL.glEnableVertexAttribArray(0)
OpenGL.GL.glBindBuffer(OpenGL.GL.GL_ARRAY_BUFFER, vertexBuffer)
OpenGL.GL.glVertexAttribPointer(0, 3, OpenGL.GL.GL_FLOAT, OpenGL.GL.GL_FALSE, 0, None)
OpenGL.GL.glDrawArrays(OpenGL.GL.GL_TRIANGLES, 0, 3)
OpenGL.GL.glDisableVertexAttribArray(0)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
simpleVertexShader.glsl:
#version 330 core
layout(location = 0) in vec3 vertexposition_modelspace;
void main(){
gl_Position.xyz = vertexposition_modelspace;
gl_Position.w = 1.0;
}
simpleFragmentShader.glsl:
#version 330 core
out vec3 color;
void main(){
color = vec3(1,0,0);
}
The 2nd parameter glBufferData has to be the size of the buffer in bytes and not the number of elements of the array:
Calculate the size of the buffer in bytes, to solve your issue:
buffer_size = len(vektoren) * vektoren.itemsize
OpenGL.GL.glBufferData(OpenGL.GL.GL_ARRAY_BUFFER, buffer_size, vektoren, OpenGL.GL.GL_STATIC_DRAW)
Note, the array can be passed to glBufferData without setting the size explicitly:
OpenGL.GL.glBufferData(OpenGL.GL.GL_ARRAY_BUFFER, vektoren, OpenGL.GL.GL_STATIC_DRAW)

Assign multiple shaders to imported .OBJ files in Maya

I need to import multiple files into Maya and assign multiple materials to each one.
I have the following code in Python:
import maya.cmds as cmds
import glob
def importFile(i):
cmds.file(i, i=True, groupReference=True, groupName="myobj")
def moveFile():
cmds.select("myobj")
cmds.scale(1,1,1, r=True)
cmds.move (0, 14, 0, r=True)
cmds.rotate (-90, 0, 0, r=True)
def materialFile():
cmds.select("myobj")
myMaterial = "blinn1"
cmds.sets( e=True, forceElement= myMaterial + 'SG' )
def renderFile(i):
cmds.setAttr("defaultRenderGlobals.imageFilePrefix", i, type="string")
cmds.render(batch=True)
def deleteFile():
cmds.select("myobj")
cmds.delete()
myglob = glob.glob("/The/path/of/your/Obj_files/*.obj")
for i in myglob:
importFile(i)
moveFile()
materialFile()
renderFile(i)
deleteFile()
With this code I can assign only one shader to the whole piece of geometry.
Is it possible to assign different shaders to the same piece of geometry?
Definitely, you can assign different shaders to the same piece of geometry as well as any accessible shader for multiple 3d models.
import maya.cmds as mat
d = 0
shader_node1 = mat.shadingNode( "anisotropic", asShader = True, n = 'ani' )
shader_node2 = mat.shadingNode( "phong", asShader = True, n = 'pho' )
shader_node3 = mat.shadingNode( "lambert", asShader = True, n = 'lam' )
mat.setAttr( (shader_node1 + '.color'), 1.0, 0.0, 0.0, type = 'double3' )
mat.setAttr( (shader_node2 + '.color'), 0.0, 1.0, 0.0, type = 'double3' )
mat.setAttr( (shader_node2 + '.transparency'), 0.25, 0.25, 0.25, type = 'double3' )
mat.setAttr( (shader_node3 + '.color'), 0.0, 0.0, 1.0, type = 'double3' )
shading_group1 = mat.sets( renderable = True, noSurfaceShader = True, empty = True )
shading_group2 = mat.sets( renderable = True, noSurfaceShader = True, empty = True )
shading_group3 = mat.sets( renderable = True, noSurfaceShader = True, empty = True )
mat.connectAttr( '%s.outColor' %shader_node1, '%s.surfaceShader' %shading_group1 )
mat.connectAttr( '%s.outColor' %shader_node2, '%s.surfaceShader' %shading_group2 )
mat.connectAttr( '%s.outColor' %shader_node3, '%s.surfaceShader' %shading_group3 )
mat.polySphere( radius = 5 )
mat.polySphere( radius = 7 )
mat.polySphere( radius = 3 )
mat.select( 'pSphere1' )
mat.move( 7, 6, 0, r = True )
mat.hyperShade( assign = shader_node1 )
mat.select( 'pSphere2' )
mat.hyperShade( assign = shader_node2 )
mat.select( 'pSphere3' )
mat.move( -7, -2, 0, r = True )
mat.hyperShade( assign = shader_node3 )
d += 1
Also you can access your shaders within an array using random function:
import random as random
myArray = [shader_node1, shader_node2, shader_node3]
for i in myArray :
mat.select( 'pSphere1' )
mat.move( 7, 6, 0, r = True )
mat.hyperShade( assign = myArray[int(random.uniform(0,3))] )
mat.select( 'pSphere2' )
mat.hyperShade( assign = myArray[int(random.uniform(0,3))] )
mat.select( 'pSphere3' )
mat.move( -7, -2, 0, r = True )
mat.hyperShade( assign = myArray[int(random.uniform(0,3))] )

Categories

Resources