Pyglet 3-D's rendering is putting extra pixels in an image - python
I have Python 3.8.2, and I'm trying to create my own minecraft armor renderer, and I've run into a problem that I just can't wrap my head around (Render). Obviously I don't want the top of the shoulder to be floating above the shoulder, but it isn't anything to do with the texture (An Example Texture which I got directly by saving the pyglet texture).
How do I fix this and how can I avoid this in the future? If you need me to give more information or more specific information, I can update this and respond. (I'm new to pyglet, so criticism and advice welcome.)
My two armor layers: (Armor Layer 1, Armor Layer 2)
The code I'm worried about:
from pyglet.window import key
import math, os, shutil
from PIL import Image, ImageOps
class Model():
def get_tex(self,file):
tex = pyglet.image.load(file).get_texture()
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
return pyglet.graphics.TextureGroup(tex)
def __init__(self,pos = (0,0,-1),size = (16,16,16),bottom='ancient_debris_top.png',top='ancient_debris_top.png',back='ancient_debris_side.png',front='ancient_debris_side.png',left='ancient_debris_side.png',right='ancient_debris_side.png'):
self.top = self.get_tex(top)
self.bottom = self.get_tex(bottom)
self.back = self.get_tex(back)
self.front = self.get_tex(front)
self.left = self.get_tex(left)
self.right = self.get_tex(right)
self.batch = pyglet.graphics.Batch()
tex_coords = ('t2f',(0,0, 1,0, 1,1, 0,1, ))
x,y,z = pos[0]*4, pos[1]*4, pos[2]*4
X,Y,Z = x+size[0]/4,y+size[1]/4,z+size[2]/4
self.batch.add(4,GL_QUADS,self.left,('v3f',(x,y,z, x,y,Z, x,Y,Z, x,Y,z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.right,('v3f',(X,y,Z, X,y,z, X,Y,z, X,Y,Z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.bottom,('v3f',(x,y,z, X,y,z, X,y,Z, x,y,Z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.top,('v3f',(x,Y,Z, X,Y,Z, X,Y,z, x,Y,z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.back,('v3f',(X,y,z, x,y,z, x,Y,z, X,Y,z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.front,('v3f',(x,y,Z, X,y,Z, X,Y,Z, x,Y,Z, )),tex_coords)
def draw(self):
self.batch.draw()
class Window(pyglet.window.Window):
def push(self,pos,rot): glPushMatrix(); glRotatef(-rot[0],1,0,0); glRotatef(-rot[1],0,1,0); glTranslatef(-pos[0],-pos[1],-pos[2],)
def Projection(self): glMatrixMode(GL_PROJECTION); glLoadIdentity()
def Model(self): glMatrixMode(GL_MODELVIEW); glLoadIdentity()
def set2d(self): self.Projection(); gluOrtho2D(0,self.width,0,self.height); self.Model()
def set3d(self): self.Projection(); gluPerspective(70,self.width/self.height,0.05,1000); self.Model()
def setLock(self,state): self.lock = state; self.set_exclusive_mouse(state)
lock = False; mouse_lock = property(lambda self:self.lock,setLock)
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.set_minimum_size(300,200)
self.keys = key.KeyStateHandler()
self.push_handlers(self.keys)
pyglet.clock.schedule(self.update)
self.model = Model(pos=(0,1,-1),size=(8,8,8),front='head/head_front.png',back='head/head_back.png',top='head/head_top.png',bottom='head/head_bottom.png',left='head/head_left.png',right='head/head_right.png')
self.model2 = Model(pos=(0,0.375,-0.875),size=(8,12,4),front='chest/chest_front.png',back='chest/chest_back.png',top='chest/chest_top.png',bottom='chest/chest_bottom.png',left='chest/chest_left.png',right='chest/chest_right.png')
self.model3 = Model(pos=(0.5,0.375,-0.875),size=(4,12,4),front='shoulder_right/shoulder_right_front.png',back='shoulder_right/shoulder_right_back.png',top='shoulder_right/shoulder_right_top.png',bottom='shoulder_right/shoulder_right_bottom.png',left='shoulder_right/shoulder_right_left.png',right='shoulder_right/shoulder_right_right.png')
self.model4 = Model(pos=(-0.25,0.375,-0.875),size=(4,12,4),front='shoulder_left/shoulder_left_front.png',back='shoulder_left/shoulder_left_back.png',top='shoulder_left/shoulder_left_top.png',bottom='shoulder_left/shoulder_left_bottom.png',left='shoulder_left/shoulder_left_left.png',right='shoulder_left/shoulder_left_right.png')
self.player = Player((0.5,1.5,1.5),(-30,0))
def on_mouse_motion(self,x,y,dx,dy):
if self.mouse_lock: self.player.mouse_motion(dx,dy)
def on_key_press(self,KEY,MOD):
if KEY == key.ESCAPE:
self.close()
rmallfolders()
elif KEY == key.E: self.mouse_lock = not self.mouse_lock
def update(self,dt):
self.player.update(dt,self.keys)
def on_draw(self):
self.clear()
self.set3d()
self.push(self.player.pos,self.player.rot)
self.model.draw()
self.model2.draw()
self.model3.draw()
self.model4.draw()
glPopMatrix()
My entire code:
from pyglet.window import key
import math, os, shutil
from PIL import Image, ImageOps
class Model():
def get_tex(self,file):
tex = pyglet.image.load(file).get_texture()
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
return pyglet.graphics.TextureGroup(tex)
def __init__(self,pos = (0,0,-1),size = (16,16,16),bottom='ancient_debris_top.png',top='ancient_debris_top.png',back='ancient_debris_side.png',front='ancient_debris_side.png',left='ancient_debris_side.png',right='ancient_debris_side.png'):
self.top = self.get_tex(top)
self.bottom = self.get_tex(bottom)
self.back = self.get_tex(back)
self.front = self.get_tex(front)
self.left = self.get_tex(left)
self.right = self.get_tex(right)
self.batch = pyglet.graphics.Batch()
tex_coords = ('t2f',(0,0, 1,0, 1,1, 0,1, ))
x,y,z = pos[0]*4, pos[1]*4, pos[2]*4
X,Y,Z = x+size[0]/4,y+size[1]/4,z+size[2]/4
self.batch.add(4,GL_QUADS,self.left,('v3f',(x,y,z, x,y,Z, x,Y,Z, x,Y,z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.right,('v3f',(X,y,Z, X,y,z, X,Y,z, X,Y,Z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.bottom,('v3f',(x,y,z, X,y,z, X,y,Z, x,y,Z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.top,('v3f',(x,Y,Z, X,Y,Z, X,Y,z, x,Y,z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.back,('v3f',(X,y,z, x,y,z, x,Y,z, X,Y,z, )),tex_coords)
self.batch.add(4,GL_QUADS,self.front,('v3f',(x,y,Z, X,y,Z, X,Y,Z, x,Y,Z, )),tex_coords)
def draw(self):
self.batch.draw()
class Player:
def __init__(self,pos=(0,0,0),rot=(0,0)):
self.pos = list(pos)
self.rot = list(rot)
def mouse_motion(self,dx,dy):
dx/=8; dy/=8; self.rot[0]+=dy; self.rot[1]-=dx
if self.rot[0]>90: self.rot[0] = 90
elif self.rot[0]<-90: self.rot[0] = -90
def update(self,dt,keys):
s = dt*10
rotY = -self.rot[1]/180*math.pi
dx,dz = s*math.sin(rotY),s*math.cos(rotY)
if keys[key.W]: self.pos[0]+=dx; self.pos[2]-=dz
if keys[key.S]: self.pos[0]-=dx; self.pos[2]+=dz
if keys[key.A]: self.pos[0]-=dz; self.pos[2]-=dx
if keys[key.D]: self.pos[0]+=dz; self.pos[2]+=dx
if keys[key.SPACE]: self.pos[1]+=s
if keys[key.LSHIFT]: self.pos[1]-=s
class Window(pyglet.window.Window):
def push(self,pos,rot): glPushMatrix(); glRotatef(-rot[0],1,0,0); glRotatef(-rot[1],0,1,0); glTranslatef(-pos[0],-pos[1],-pos[2],)
def Projection(self): glMatrixMode(GL_PROJECTION); glLoadIdentity()
def Model(self): glMatrixMode(GL_MODELVIEW); glLoadIdentity()
def set2d(self): self.Projection(); gluOrtho2D(0,self.width,0,self.height); self.Model()
def set3d(self): self.Projection(); gluPerspective(70,self.width/self.height,0.05,1000); self.Model()
def setLock(self,state): self.lock = state; self.set_exclusive_mouse(state)
lock = False; mouse_lock = property(lambda self:self.lock,setLock)
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.set_minimum_size(300,200)
self.keys = key.KeyStateHandler()
self.push_handlers(self.keys)
pyglet.clock.schedule(self.update)
self.model = Model(pos=(0,1,-1),size=(8,8,8),front='head/head_front.png',back='head/head_back.png',top='head/head_top.png',bottom='head/head_bottom.png',left='head/head_left.png',right='head/head_right.png')
self.model2 = Model(pos=(0,0.375,-0.875),size=(8,12,4),front='chest/chest_front.png',back='chest/chest_back.png',top='chest/chest_top.png',bottom='chest/chest_bottom.png',left='chest/chest_left.png',right='chest/chest_right.png')
self.model3 = Model(pos=(0.5,0.375,-0.875),size=(4,12,4),front='shoulder_right/shoulder_right_front.png',back='shoulder_right/shoulder_right_back.png',top='shoulder_right/shoulder_right_top.png',bottom='shoulder_right/shoulder_right_bottom.png',left='shoulder_right/shoulder_right_left.png',right='shoulder_right/shoulder_right_right.png')
self.model4 = Model(pos=(-0.25,0.375,-0.875),size=(4,12,4),front='shoulder_left/shoulder_left_front.png',back='shoulder_left/shoulder_left_back.png',top='shoulder_left/shoulder_left_top.png',bottom='shoulder_left/shoulder_left_bottom.png',left='shoulder_left/shoulder_left_left.png',right='shoulder_left/shoulder_left_right.png')
self.player = Player((0.5,1.5,1.5),(-30,0))
def on_mouse_motion(self,x,y,dx,dy):
if self.mouse_lock: self.player.mouse_motion(dx,dy)
def on_key_press(self,KEY,MOD):
if KEY == key.ESCAPE:
self.close()
rmallfolders()
elif KEY == key.E: self.mouse_lock = not self.mouse_lock
def update(self,dt):
self.player.update(dt,self.keys)
def on_draw(self):
self.clear()
self.set3d()
self.push(self.player.pos,self.player.rot)
self.model.draw()
self.model2.draw()
self.model3.draw()
self.model4.draw()
glPopMatrix()
def readfile1(filename):
rawimage=Image.open(filename)
#left, top, right, bottom
head_top = rawimage.crop((8,0,16,8)); head_left = rawimage.crop((0,8,8,16)); head_front = rawimage.crop((8,8,16,16)); head_right = rawimage.crop((16,8,24,16)); head_back = rawimage.crop((24,8,32,16)); head_bottom=Image.open("empty_bottom_head.png"); os.mkdir("head")
head_top.save('head/head_top.png'); head_left.save('head/head_left.png'); head_front.save('head/head_front.png'); head_right.save('head/head_right.png'); head_back.save('head/head_back.png'); head_bottom.save('head/head_bottom.png')
chest_top = Image.open("empty_bottom_head.png"); chest_front = rawimage.crop((20,20,28,32)); chest_left = rawimage.crop((16,20,20,32)); chest_right = rawimage.crop((28,20,32,32)); chest_back = rawimage.crop((32,20,40,32)); chest_bottom = Image.open("empty_bottom_head.png"); os.mkdir("chest")
chest_top.save('chest/chest_top.png'); chest_left.save('chest/chest_left.png'); chest_front.save('chest/chest_front.png'); chest_right.save('chest/chest_right.png'); chest_back.save('chest/chest_back.png'); chest_bottom.save('chest/chest_bottom.png')
shoulder_right_top = rawimage.crop((44,16,48,20)); shoulder_right_front = rawimage.crop((44,20,48,32)); shoulder_right_left = rawimage.crop((40,20,44,32)); shoulder_right_right = rawimage.crop((48,20,52,32)); shoulder_right_back = rawimage.crop((52,20,56,32)); shoulder_right_bottom = Image.open("empty_bottom_head.png"); os.mkdir("shoulder_right")
shoulder_right_top.save('shoulder_right/shoulder_right_top.png'); shoulder_right_left.save('shoulder_right/shoulder_right_left.png'); shoulder_right_front.save('shoulder_right/shoulder_right_front.png'); shoulder_right_right.save('shoulder_right/shoulder_right_right.png'); shoulder_right_back.save('shoulder_right/shoulder_right_back.png'); shoulder_right_bottom.save('shoulder_right/shoulder_right_bottom.png')
shoulder_left_top = ImageOps.mirror(shoulder_right_top); shoulder_left_bottom = ImageOps.mirror(shoulder_right_bottom); shoulder_left_right = ImageOps.mirror(shoulder_right_left); shoulder_left_left = ImageOps.mirror(shoulder_right_right); shoulder_left_front = ImageOps.mirror(shoulder_right_front); shoulder_left_back = ImageOps.mirror(shoulder_right_back); os.mkdir('shoulder_left')
shoulder_left_top.save('shoulder_left/shoulder_left_top.png'); shoulder_left_left.save('shoulder_left/shoulder_left_left.png'); shoulder_left_front.save('shoulder_left/shoulder_left_front.png'); shoulder_left_right.save('shoulder_left/shoulder_left_right.png'); shoulder_left_back.save('shoulder_left/shoulder_left_back.png'); shoulder_left_bottom.save('shoulder_left/shoulder_left_bottom.png')
def rmallfolders():
folders = ['head','chest','shoulder_right','shoulder_left']
for i in folders:
try:
shutil.rmtree(i)
except:
pass
if __name__ == '__main__':
rmallfolders()
readfile1('ruby_layer_1.png')
window = Window(width=854,height=480,caption='3-D Modeller',resizable=True)
glClearColor(0.5,0.7,1,1)
glEnable(GL_DEPTH_TEST)
#glEnable(GL_CULL_FACE)
pyglet.app.run()
Thanks so much!
Related
Pyglet textures are messed up
I have been following an Pyglet OpenGL tutorial on Youtube here and I followed it around a year or two ago. It worked quite well and I managed to make a Minecraft clone which included simple physics. Now, I've lost the code and I tried to recreate it. I followed the video, but it took too long so I just downloaded it from the link. Then, the only thing I replaced was the tex = pyglet.image.load(file).texture, because apparently I should use tex = pyglet.image.load(file).get_texture(). Then, when I ran it, the cube looked very distorted and the textures were all in the wrong place. Here are links to the cube pictures: The code is here: from pyglet.gl import * from pyglet.window import key import math class Model: def get_tex(self,file): tex = pyglet.image.load(file).get_texture() glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST) glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST) return pyglet.graphics.TextureGroup(tex) def __init__(self): self.top = self.get_tex('grass_top.png') self.side = self.get_tex('grass_side.png') self.bottom = self.get_tex('dirt.png') self.batch = pyglet.graphics.Batch() tex_coords = ('t2f',(0,0, 1,0, 1,1, 0,1, )) x,y,z = -1,-1,-1 X,Y,Z = x+2,y+2,z+2 self.batch.add(4, GL_QUADS, self.side, ('v3f',(x,y,z, x,y,Z, x,Y,Z, x,Y,z)), tex_coords) self.batch.add(4, GL_QUADS, self.side, ('v3f',(X,y,Z, X,y,z, X,Y,z, X,Y,Z)), tex_coords) self.batch.add(4, GL_QUADS, self.bottom, ('v3f',(x,y,z, X,y,z, X,y,Z, x,y,Z)), tex_coords) self.batch.add(4, GL_QUADS, self.top, ('v3f',(x,Y,Z, X,Y,Z, X,Y,z, x,Y,z)), tex_coords) self.batch.add(4, GL_QUADS, self.side, ('v3f',(X,y,z, x,y,z, x,Y,z, X,Y,z)), tex_coords) self.batch.add(4, GL_QUADS, self.side, ('v3f',(x,y,Z, X,y,Z, X,Y,Z, x,Y,Z)), tex_coords) def draw(self): self.batch.draw() class Player: def __init__(self,pos=(0,0,0),rot=(0,0)): self.pos = list(pos) self.rot = list(rot) def mouse_motion(self,dx,dy): dx/=8; dy/=8; self.rot[0]+=dy; self.rot[1]-=dx if self.rot[0]>90: self.rot[0] = 90 elif self.rot[0]<-90: self.rot[0] = -90 def update(self,dt,keys): s = dt*10 rotY = -self.rot[1]/180*math.pi dx,dz = s*math.sin(rotY),s*math.cos(rotY) if keys[key.W]: self.pos[0]+=dx; self.pos[2]-=dz if keys[key.S]: self.pos[0]-=dx; self.pos[2]+=dz if keys[key.A]: self.pos[0]-=dz; self.pos[2]-=dx if keys[key.D]: self.pos[0]+=dz; self.pos[2]+=dx if keys[key.SPACE]: self.pos[1]+=s if keys[key.LSHIFT]: self.pos[1]-=s class Window(pyglet.window.Window): def push(self,pos,rot): glPushMatrix(); glRotatef(-rot[0],1,0,0); glRotatef(-rot[1],0,1,0); glTranslatef(-pos[0],-pos[1],-pos[2],) def Projection(self): glMatrixMode(GL_PROJECTION); glLoadIdentity() def Model(self): glMatrixMode(GL_MODELVIEW); glLoadIdentity() def set2d(self): self.Projection(); gluOrtho2D(0,self.width,0,self.height); self.Model() def set3d(self): self.Projection(); gluPerspective(90,self.width/self.height,0.05,1000); self.Model() def setLock(self,state): self.lock = state; self.set_exclusive_mouse(state) lock = False; mouse_lock = property(lambda self:self.lock,setLock) def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs) self.set_minimum_size(300, 200) self.keys = key.KeyStateHandler() self.push_handlers(self.keys) pyglet.clock.schedule(self.update) self.model = Model() self.player = Player((0,0,5),(0,0)) def on_mouse_motion(self,x,y,dx,dy): if self.mouse_lock: self.player.mouse_motion(dx,dy) def on_key_press(self,KEY,MOD): if KEY == key.ESCAPE: self.close() elif KEY == key.E: self.mouse_lock = not self.mouse_lock def update(self,dt): self.player.update(dt,self.keys) def on_draw(self): self.clear() self.set3d() self.push(self.player.pos,self.player.rot) self.model.draw() glPopMatrix() if __name__ == '__main__': window = Window(width=854,height=480,caption='Minecraft',resizable=True) glClearColor(0.5,0.7,1,1) glEnable(GL_DEPTH_TEST) # glEnable(GL_CULL_FACE) pyglet.app.run() Note: I fiddled with the textures and when I replaced the batch adds with this: self.batch.add(4, GL_QUADS, self.top, ('v3f',(x,y,z, x,y,Z, x,Y,Z, x,Y,z)), tex_coords) self.batch.add(4, GL_QUADS, self.top, ('v3f',(X,y,Z, X,y,z, X,Y,z, X,Y,Z)), tex_coords) self.batch.add(4, GL_QUADS, self.side, ('v3f',(x,y,z, X,y,z, X,y,Z, x,y,Z)), tex_coords) self.batch.add(4, GL_QUADS, self.bottom, ('v3f',(x,Y,Z, X,Y,Z, X,Y,z, x,Y,z)), tex_coords) self.batch.add(4, GL_QUADS, self.top, ('v3f',(X,y,z, x,y,z, x,Y,z, X,Y,z)), tex_coords) self.batch.add(4, GL_QUADS, self.top, ('v3f',(x,y,Z, X,y,Z, X,Y,Z, x,Y,Z)), tex_coords) The cube was textured properly. Any ideas why? Also, when I only drew one side, it was in the right place but the texture was bending over the face, as if it were two triangles.
You have to enable the Depth Test: if __name__ == "__main__": window = Window(width = 400, height = 300, caption = "Minecraft", resizable = True) glClearColor(0.5, 0.7, 1, 1) glEnable(GL_DEPTH_TEST) # <---- pyglet.app.run() Note that the cube appears to be screwed up since the front of the cube doesn't cover the back. The depth test ensures that a geometry that has already been drawn is not covered by a new geometry that is behind it.
I found the answer. After testing the exact same code on a newer PC, it worked. Seems like I have an outdated GPU or graphics driver, just like Rabbid76 said.
Pyglet texture doesn't cover the square
Im programming a simple pyglet example in pyglet 1.3.0 but i have a problem. I have also tested other pyglet versions but the problem still there. No error is displayed but the texture only appears in a part of the square. I'm using python 3.7. Here is the code: from pyglet.gl import * from pyglet.window import key import resources import math class Model: def get_tex(self, file): tex = pyglet.image.load(file).texture glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) return pyglet.graphics.TextureGroup(tex) def __init__(self): self.top = self.get_tex("grass_tex.jpg") self.bottom = self.get_tex("grass_tex.jpg") self.side = self.get_tex("terrain_tex.jpg") self.batch = pyglet.graphics.Batch() tex_coords = ("t2f", (0,0, 1,0, 1,1, 0,1, )) #color = ("c3f", (1, 1, 1)*4) x, y, z = 0, 0, -1 X, Y, Z = x+1, y+1, z+1 self.batch.add(4, GL_QUADS, self.top, ("v3f", (x,y,z, X,y,z, X,Y,z, x,Y,z, )), tex_coords) def draw(self): self.batch.draw() class Player: def __init__(self): self.pos = [0, 0, 0] self.rot = [0, 0] def update(self, dt, keys): pass class Window(pyglet.window.Window): def Projection(self): glMatrixMode(GL_PROJECTION) glLoadIdentity() def Model(self): glMatrixMode(GL_MODELVIEW) glLoadIdentity() def set2d(self): self.Projection() gluOrtho2D(0, self.width, 0, self.height) self.Model() def set3d(self): self.Projection() gluPerspective(70, self.width/self.height, 0.05, 1000) self.Model() def setLock(self, state): self.lock = state self.set_exclusive_mouse(state) lock = False mouse_lock = property(lambda self:self.lock, setLock) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.model = Model() self.player = Player() self.keys = key.KeyStateHandler() self.push_handlers(self.keys) pyglet.clock.schedule(self.update) def on_key_press(self, KEY, MOD): if KEY == key.ESCAPE: self.close() elif KEY == key.SPACE: self.mouse_lock = not self.mouse_lock def update(self, dt): self.player.update(dt, self.keys) def on_draw(self): self.clear() self.set3d() self.model.draw() if __name__ == "__main__": window = Window(width=400, height=300, caption="gamelearn2", resizable=False) glClearColor(0.5, 0.7, 1, 1) pyglet.app.run() And this is the error:
When you use a perspective projection gluPerspective, then the (0, 0) is in the center of the viewport. You have to draw the quad with the texture from (-1, -1) to (1, 1) and at a depth of 1 (z = -1). x, y, z = -1, -1, -1 X, Y = 1, 1 self.batch.add(4, GL_QUADS, self.top, ("v3f", (x,y,z, X,y,z, X,Y,z, x,Y,z, )), tex_coords) Furthermore use a field of view angle of 90°: class Window(pyglet.window.Window): # [...] def set3d(self): self.Projection() gluPerspective(90, self.width/self.height, 0.05, 1000) self.Model()
Python: How do I generate minecraft style terrain with pyglet?
I'm currently trying to make a Minecraft clone using pyglet and python for fun, and I found a nice tutorial which I used to create a player class with movement, and a 3D block that generates in the scene. Now I want to create some sort of a terrain, I read about terrain generating and I stumbled upon a function called "noise function" which seems to fit nicely with one I'm trying to do. sadly I don't really know how to implement it. :( At first I tried to generate a flat terrain by creating a function in the Model class which contains the code that creates a cube, and then I create a loop that generates all the numbers between 1 and 20 for example, and use them as values to the cube function. but it didn't work so i had to remove it :( I do think that it might be too soon to implement a terrain with the noise function seeing the stage I'm currently in. so creating an endless flat terrain that do work will be good as well :D If you want to check the program, You'll need to press 'E' when you run the code to enable a mouse lock, which will let you to move the mouse and the player in the scene. Here is the code I have: from pyglet.gl import * from pyglet.window import key import math import random from random import * class Model: def get_tex(self, file): tex = pyglet.image.load(file).texture glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) return pyglet.graphics.TextureGroup(tex) def __init__(self): self.top = self.get_tex('grass_top.png') self.side = self.get_tex('grass_side.png') self.bottom = self.get_tex('dirt.png') self.batch = pyglet.graphics.Batch() tex_coords = ('t2f', (0, 0, 1, 0, 1, 1, 0, 1,)) x, y, z = randint(0, 5), randint(0, 5), -1 X, Y, Z = x+1, y+1, z+1 self.batch.add(4, GL_QUADS, self.side, ('v3f', (x, y, z, x, y, Z, x, Y, Z, x, Y, z,)), tex_coords) self.batch.add(4, GL_QUADS, self.side, ('v3f', (X, y, Z, X, y, z, X, Y, z, X, Y, Z,)), tex_coords) self.batch.add(4, GL_QUADS, self.bottom, ('v3f', (x, y, z, X, y, z, X, y, Z, x, y, Z,)), tex_coords) self.batch.add(4, GL_QUADS, self.top, ('v3f', (x, Y, Z, X, Y, Z, X, Y, z, x, Y, z, )), tex_coords) self.batch.add(4, GL_QUADS, self.side, ('v3f', (X, y, z, x, y, z, x, Y, z, X, Y, z, )), tex_coords) self.batch.add(4, GL_QUADS, self.side, ('v3f', (x, y, Z, X, y, Z, X, Y, Z, x, Y, Z, )), tex_coords) def draw(self): self.batch.draw() class Player: def __init__(self, pos=(0, 0, 0), rot=(0, 0)): self.pos = list(pos) self.rot = list(rot) def mouse_motion(self, dx, dy): dx /= 8 dy /= 8 self.rot[0] += dy self.rot[1] -= dx if self.rot[0] > 90: self.rot[0] = 90 elif self.rot[0] < -90: self.rot[0] = -90 def update(self, dt, keys): s = dt*10 rotation_y = -self.rot[1]/180*math.pi dx, dz = s*math.sin(rotation_y), s*math.cos(rotation_y) if keys[key.W]: self.pos[0] += dx self.pos[2] -= dz if keys[key.S]: self.pos[0] -= dx self.pos[2] += dz if keys[key.A]: self.pos[0] -= dz self.pos[2] -= dx if keys[key.D]: self.pos[0] += dz self.pos[2] += dx if keys[key.SPACE]: self.pos[1] += s if keys[key.LSHIFT]: self.pos[1] -= s class Window(pyglet.window.Window): def push(self, pos, rot): glPushMatrix() glRotatef(-rot[0], 1, 0, 0) glRotatef(-rot[1], 0, 1, 0) glTranslatef(-pos[0], -pos[1], -pos[2],) def Projection(self): glMatrixMode(GL_PROJECTION) glLoadIdentity() def Model(self): glMatrixMode(GL_MODELVIEW) glLoadIdentity() def set2d(self): self.Projection() gluOrtho2D(0, self.width, 0, self.height) self.Model() def set3d(self): self.Projection() gluPerspective(70, self.width / self.height, 0.05, 1000) self.Model() def setLock(self, state): self.lock = state; self.set_exclusive_mouse(state) lock = False; mouse_lock = property(lambda self: self.lock, setLock) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.set_minimum_size(300, 200) self.keys = key.KeyStateHandler() self.push_handlers(self.keys) pyglet.clock.schedule(self.update) self.model = Model() self.player = Player((0.5, 1.5, 1.5), (-30, 0)) def on_mouse_motion(self, x, y, dx, dy): if self.mouse_lock: self.player.mouse_motion(dx, dy) def on_key_press(self, KEY, MOD): if KEY == key.ESCAPE: self.close() elif KEY == key.E: self.mouse_lock = not self.mouse_lock def update(self, dt): self.player.update(dt, self.keys) def on_draw(self): self.clear() self.set3d() self.push(self.player.pos, self.player.rot) self.model.draw() glPopMatrix() if __name__ == '__main__': window = Window(width=854, height=480, caption='Minecraft', resizable=True) glClearColor(0.5, 0.7, 1, 1) glEnable(GL_DEPTH_TEST) # glEnable(GL_CULL_FACE) pyglet.app.run() My project use's some images to create the dirt texture. so here is a link to a website which has the images and the main program which you can see above you: http://www.mediafire.com/file/7iolhmh1hqj9516/Basic+Pyglet+Cube.rar I already tried to generate more then one block using a list and recalling a function
You could create a function to show a block at the given position and with the texture texture: def show_block(self, position, texture): top = texture[0] side = texture[1] bottom = texture[2] x, y, z = position X, Y, Z = x+1,y+1, z+1 tex_coords = ('t2f', (0, 0, 1, 0, 1, 1, 0, 1,)) self.batch.add(4, GL_QUADS, side, ('v3f', (x, y, z, x, y, Z, x, Y, Z, x, Y, z,)), tex_coords) self.batch.add(4, GL_QUADS, side, ('v3f', (X, y, Z, X, y, z, X, Y, z, X, Y, Z,)), tex_coords) self.batch.add(4, GL_QUADS, bottom, ('v3f', (x, y, z, X, y, z, X, y, Z, x, y, Z,)), tex_coords) self.batch.add(4, GL_QUADS, top, ('v3f', (x, Y, Z, X, Y, Z, X, Y, z, x, Y, z, )), tex_coords) self.batch.add(4, GL_QUADS, side, ('v3f', (X, y, z, x, y, z, x, Y, z, X, Y, z, )), tex_coords) self.batch.add(4, GL_QUADS, side, ('v3f', (x, y, Z, X, y, Z, X, Y, Z, x, Y, Z, )), tex_coords) Then you could use perlin to generate terrain: class Perlin: def __call__(self,x,y): return (self.noise(x*self.f,y*self.f)+1)/2 def __init__(self,seed=None): self.f = 15/512; self.m = 65535; p = list(range(self.m)) if seed: random.seed(seed) random.shuffle(p); self.p = p+p def fade(self,t): return t*t*t*(t*(t*6-15)+10) def lerp(self,t,a,b): return a+t*(b-a) def grad(self,hash,x,y,z): h = hash&15; u = y if h&8 else x v = (x if h==12 or h==14 else z) if h&12 else y return (u if h&1 else -u)+(v if h&2 else -v) def noise(self,x,y,z=0): p,fade,lerp,grad = self.p,self.fade,self.lerp,self.grad xf,yf,zf = math.floor(x),math.floor(y),math.floor(z) X,Y,Z = xf%self.m,yf%self.m,zf%self.m x-=xf; y-=yf; z-=zf u,v,w = fade(x),fade(y),fade(z) A = p[X ]+Y; AA = p[A]+Z; AB = p[A+1]+Z B = p[X+1]+Y; BA = p[B]+Z; BB = p[B+1]+Z return lerp(w,lerp(v,lerp(u,grad(p[AA],x,y,z),grad(p[BA],x-1,y,z)),lerp(u,grad(p[AB],x,y-1,z),grad(p[BB],x-1,y-1,z))), lerp(v,lerp(u,grad(p[AA+1],x,y,z-1),grad(p[BA+1],x-1,y,z-1)),lerp(u,grad(p[AB+1],x,y-1,z-1),grad(p[BB+1],x-1,y-1,z-1)))) After all these changes, your code should look somewhat like this: from pyglet.gl import * from pyglet.window import key,mouse from collections import deque import sys, os, time, math, random class Perlin: def __call__(self,x,y): return int(sum(self.noise(x*s,y*s)*h for s,h in self.perlins)*self.avg) def __init__(self): self.m = 65536; p = list(range(self.m)); random.shuffle(p); self.p = p+p p = self.perlins = tuple((1/i,i) for i in (16,20,22,31,32,64,512) for j in range(2)) self.avg = 8*len(p)/sum(f+i for f,i in p) def fade(self,t): return t*t*t*(t*(t*6-15)+10) def lerp(self,t,a,b): return a+t*(b-a) def grad(self,hash,x,y,z): h = hash&15; u = y if h&8 else x v = (x if h==12 or h==14 else z) if h&12 else y return (u if h&1 else -u)+(v if h&2 else -v) def noise(self,x,y,z=0): p,fade,lerp,grad = self.p,self.fade,self.lerp,self.grad xf,yf,zf = math.floor(x),math.floor(y),math.floor(z) X,Y,Z = xf%self.m,yf%self.m,zf%self.m x-=xf; y-=yf; z-=zf u,v,w = fade(x),fade(y),fade(z) A = p[X ]+Y; AA = p[A]+Z; AB = p[A+1]+Z B = p[X+1]+Y; BA = p[B]+Z; BB = p[B+1]+Z return lerp(w,lerp(v,lerp(u,grad(p[AA],x,y,z),grad(p[BA],x-1,y,z)),lerp(u,grad(p[AB],x,y-1,z),grad(p[BB],x-1,y-1,z))), lerp(v,lerp(u,grad(p[AA+1],x,y,z-1),grad(p[BA+1],x-1,y,z-1)),lerp(u,grad(p[AB+1],x,y-1,z-1),grad(p[BB+1],x-1,y-1,z-1)))) class Model: alpha_textures = 'leaves_oak','tall_grass' def load_textures(self): t = self.texture = {}; self.texture_dir = {}; dirs = ['textures'] while dirs: dir = dirs.pop(0); textures = os.listdir(dir) for file in textures: if os.path.isdir(dir+'/'+file): dirs+=[dir+'/'+file] else: n = file.split('.')[0]; self.texture_dir[n] = dir; image = pyglet.image.load(dir+'/'+file) transparent = n in self.alpha_textures texture = image.texture if transparent else image.get_mipmapped_texture() self.texture[n] = pyglet.graphics.TextureGroup(texture) if not transparent: glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_LINEAR) glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST) self.block = {}; self.ids = []; done = [] items = sorted(self.texture_dir.items(),key=lambda i:i[0]) for name,dir in items: n = name.split(' ')[0] if n in done: continue done+=[n] if dir.startswith('textures/blocks'): self.ids+=[n] if dir=='textures/blocks': self.block[n] = t[n],t[n],t[n],t[n],t[n],t[n] elif dir=='textures/blocks/tbs': self.block[n] = t[n+' s'],t[n+' s'],t[n+' b'],t[n+' t'],t[n+' s'],t[n+' s'] elif dir=='textures/blocks/ts': self.block[n] = t[n+' s'],t[n+' s'],t[n+' t'],t[n+' t'],t[n+' s'],t[n+' s'] self.ids+=['water'] flow,still = t['water_flow'],t['water_still'] self.block['water'] = flow,flow,still,still,flow,flow def draw(self): glEnable(GL_ALPHA_TEST); self.opaque.draw(); glDisable(GL_ALPHA_TEST) glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE); self.transparent.draw() glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); self.transparent.draw() def update(self,dt): self.cubes.water.update(dt) def __init__(self): self.opaque = pyglet.graphics.Batch() self.transparent = pyglet.graphics.Batch() self.load_textures() self.cubes = CubeHandler(self) perlin = Perlin() for x in range(64): for z in range(64): y = perlin(x,z) self.cubes.add((x,-y,-z),'grass') for i in range(1,3): self.cubes.add((x,-i-y,-z),'dirt') for cube in self.cubes.cubes.values(): self.cubes.update_cube(cube) class Water: def __init__(self,transparent): self.transparent = transparent self.time = {'still':TimeLoop(32),'flow':TimeLoop(32)} self.coords = {'still':[],'flow':[]}; self.still_faces = {}; self.flow_faces = {} for i in range(32-1,-1,-1): y0 = i/16; y1 = (i+1)/16; self.coords['still'] += [[0,y0, 1,y0, 1,y1, 0,y1]] y0 = i/32; y1 = (i+1)/32; self.coords['flow'] += [[0,y0, 1,y0, 1,y1, 0,y1]] a,b = self.time['still'],self.time['flow']; self.t = b,b,a,a,b,b a,b = self.coords['still'],self.coords['flow']; self.c = b,b,a,a,b,b def update(self,dt): if self.time['still'].update(dt*0.5): for face,i in self.still_faces.items(): face.tex_coords = self.c[i][self.t[i].int] if self.time['flow'].update(dt): for face,i in self.flow_faces.items(): face.tex_coords = self.c[i][self.t[i].int] def show(self,v,t,i): face = self.transparent.add(4,GL_QUADS,t,('v3f',v),('t2f',self.c[i][0])) faces = self.still_faces if i==2 or i==3 else self.flow_faces faces[face] = i; return face class CubeHandler: def __init__(self,model): self.model = model self.opaque,self.transparent = model.opaque,model.transparent self.block,self.alpha_textures = model.block,model.alpha_textures self.water = Water(self.transparent) self.cubes = {} def hit_test(self,p,vec,dist=256): if normalize(p) in self.cubes: return None,None m = 8; x,y,z = p; dx,dy,dz = vec dx/=m; dy/=m; dz/=m; prev = None for i in range(dist*m): key = normalize((x,y,z)) if key in self.cubes: return key,prev prev = key x,y,z = x+dx,y+dy,z+dz return None,None def show(self,v,t,i): return self.opaque.add(4,GL_QUADS,t,('v3f',v),('t2f',(0,0, 1,0, 1,1, 0,1))) def update_cube(self,cube): if not any(cube.shown.values()): return show = self.water.show if cube.name=='water' else self.show v = cube_vertices(cube.p) f = 'left','right','bottom','top','back','front' for i in (0,1,2,3,4,5): if cube.shown[f[i]] and not cube.faces[f[i]]: cube.faces[f[i]] = show(v[i],cube.t[i],i) def set_adj(self,cube,adj,state): x,y,z = cube.p; X,Y,Z = adj; d = X-x,Y-y,Z-z; f = 'left','right','bottom','top','back','front' for i in (0,1,2): if d[i]: j = i+i; a,b = [f[j+1],f[j]][::d[i]]; cube.shown[a] = state if not state and cube.faces[a]: cube.faces[a].delete(); face = cube.faces[a]; cube.faces[a] = None; self.remove_water(face) def add(self,p,t,now=False): if p in self.cubes: return cube = self.cubes[p] = Cube(t,p,self.block[t],'alpha' if t in self.alpha_textures else 'blend' if t=='water' else 'solid') for adj in adjacent(*cube.p): if adj not in self.cubes: self.set_adj(cube,adj,True) else: a,b = cube.type,self.cubes[adj].type if a==b and (a=='solid' or b=='blend'): self.set_adj(self.cubes[adj],cube.p,False) elif a!='blend' and b!='solid': self.set_adj(self.cubes[adj],cube.p,False); self.set_adj(cube,adj,True) if now: self.update_cube(cube) def remove_water(self,face): if face in self.water.still_faces: del self.water.still_faces[face] elif face in self.water.flow_faces: del self.water.flow_faces[face] def remove(self,p): if p not in self.cubes: return cube = self.cubes.pop(p) for side,face in cube.faces.items(): if face: face.delete() self.remove_water(face) for adj in adjacent(*cube.p): if adj in self.cubes: self.set_adj(self.cubes[adj],cube.p,True) self.update_cube(self.cubes[adj]) class Cube: def __init__(self,name,p,t,typ): self.name,self.p,self.t,self.type = name,p,t,typ self.shown = {'left':False,'right':False,'bottom':False,'top':False,'back':False,'front':False} self.faces = {'left':None,'right':None,'bottom':None,'top':None,'back':None,'front':None} class TimeLoop: def __init__(self,duration): self.unit = 0; self.int = 0; self.duration = duration; self.prev = 0 def update(self,dt): self.unit+=dt; self.unit-=int(self.unit); self.int = int(self.unit*self.duration) if self.prev!=self.int: self.prev = self.int; return True def cube_vertices(pos,n=0.5): x,y,z = pos; v = tuple((x+X,y+Y,z+Z) for X in (-n,n) for Y in (-n,n) for Z in (-n,n)) return tuple(tuple(k for j in i for k in v[j]) for i in ((0,1,3,2),(5,4,6,7),(0,4,5,1),(3,7,6,2),(4,0,2,6),(1,5,7,3))) def flatten(lst): return sum(map(list,lst),[]) def normalize(pos): x,y,z = pos; return round(x),round(y),round(z) def adjacent(x,y,z): for p in ((x-1,y,z),(x+1,y,z),(x,y-1,z),(x,y+1,z),(x,y,z-1),(x,y,z+1)): yield p class Player: WALKING_SPEED = 5 FLYING_SPEED = 15 GRAVITY = 20 JUMP_SPEED = (2*GRAVITY)**.5 TERMINAL_VELOCITY = 50 def push(self): glPushMatrix(); glRotatef(-self.rot[0],1,0,0); glRotatef(self.rot[1],0,1,0); glTranslatef(-self.pos[0],-self.pos[1],-self.pos[2]) def __init__(self,cubes,pos=(0,0,0),rot=(0,0)): self.cubes = cubes self.pos,self.rot = list(pos),list(rot) self.flying = True self.noclip = True self.dy = 0 def mouse_motion(self,dx,dy): dx/=8; dy/=8; self.rot[0]+=dy; self.rot[1]+=dx if self.rot[0]>90: self.rot[0] = 90 elif self.rot[0]<-90: self.rot[0] = -90 def jump(self): if not self.dy: self.dy = self.JUMP_SPEED def get_sight_vector(self): rotX,rotY = self.rot[0]/180*math.pi,self.rot[1]/180*math.pi dx,dz = math.sin(rotY),-math.cos(rotY) dy,m = math.sin(rotX),math.cos(rotX) return dx*m,dy,dz*m def update(self,dt,keys): DX,DY,DZ = 0,0,0; s = dt*self.FLYING_SPEED if self.flying else dt*self.WALKING_SPEED rotY = self.rot[1]/180*math.pi dx,dz = s*math.sin(rotY),s*math.cos(rotY) if self.flying: if keys[key.LSHIFT]: DY-=s if keys[key.SPACE]: DY+=s elif keys[key.SPACE]: self.jump() if keys[key.W]: DX+=dx; DZ-=dz if keys[key.S]: DX-=dx; DZ+=dz if keys[key.A]: DX-=dz; DZ-=dx if keys[key.D]: DX+=dz; DZ+=dx if dt<0.2: dt/=10; DX/=10; DY/=10; DZ/=10 for i in range(10): self.move(dt,DX,DY,DZ) def move(self,dt,dx,dy,dz): if not self.flying: self.dy -= dt*self.GRAVITY self.dy = max(self.dy,-self.TERMINAL_VELOCITY) dy += self.dy*dt x,y,z = self.pos self.pos = self.collide((x+dx,y+dy,z+dz)) def collide(self,pos): if self.noclip and self.flying: return pos pad = 0.25; p = list(pos); np = normalize(pos) for face in ((-1,0,0),(1,0,0),(0,-1,0),(0,1,0),(0,0,-1),(0,0,1)): for i in (0,1,2): if not face[i]: continue d = (p[i]-np[i])*face[i] if d<pad: continue for dy in (0,1): op = list(np); op[1]-=dy; op[i]+=face[i]; op = tuple(op) if op in self.cubes: p[i]-=(d-pad)*face[i] if face[1]: self.dy = 0 break return tuple(p) class Window(pyglet.window.Window): def set2d(self): glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,self.width,0,self.height) def set3d(self): glLoadIdentity(); gluPerspective(65,self.width/self.height,0.1,320); glMatrixMode(GL_MODELVIEW); glLoadIdentity() def on_resize(self,w,h): glViewport(0,0,w,h); self.load_vertex_lists(w,h) def setLock(self,state): self.set_exclusive_mouse(state); self.mouseLock = state mouseLock = False; mouse_lock = property(lambda self:self.mouseLock,setLock) def __init__(self,*args): super().__init__(*args) pyglet.clock.schedule(self.update) self.keys = pyglet.window.key.KeyStateHandler() self.push_handlers(self.keys) self.model = Model() self.player = Player(self.model.cubes.cubes) self.mouse_lock = True self.fps = pyglet.clock.ClockDisplay() self.reticle = None self.block = 0 def load_vertex_lists(self,w,h): x,y = w/2,h/2; m = 10 if self.reticle: self.reticle.delete() self.reticle = pyglet.graphics.vertex_list(4,('v2f',(x-m,y, x+m,y, x,y-m, x,y+m)),('c3f',(0,0,0, 0,0,0, 0,0,0, 0,0,0))) self.water = pyglet.graphics.vertex_list(4,('v2f',(0,0, w,0, w,h, 0,h)),('c4f',[0.15,0.3,1,0.5]*4)) def update(self,dt): self.player.update(dt,self.keys) self.model.update(dt) def on_mouse_motion(self,x,y,dx,dy): if self.mouse_lock: self.player.mouse_motion(dx,dy) def on_mouse_press(self,x,y,button,MOD): if button == mouse.LEFT: block = self.model.cubes.hit_test(self.player.pos,self.player.get_sight_vector())[0] if block: self.model.cubes.remove(block) elif button == mouse.RIGHT: block = self.model.cubes.hit_test(self.player.pos,self.player.get_sight_vector())[1] if block: self.model.cubes.add(block,self.model.ids[self.block],True) def on_key_press(self,KEY,MOD): if KEY == key.ESCAPE: self.dispatch_event('on_close') elif KEY == key.E: self.mouse_lock = not self.mouse_lock elif KEY == key.F: self.player.flying = not self.player.flying; self.player.dy = 0; self.player.noclip = True elif KEY == key.C: self.player.noclip = not self.player.noclip elif KEY == key.UP: self.block = (self.block-1)%len(self.model.ids) elif KEY == key.DOWN: self.block = (self.block+1)%len(self.model.ids) def on_draw(self): self.clear() self.set3d() self.player.push() self.model.draw() block = self.model.cubes.hit_test(self.player.pos,self.player.get_sight_vector())[0] if block: glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); glColor3d(0,0,0) pyglet.graphics.draw(24,GL_QUADS,('v3f/static',flatten(cube_vertices(block,0.52)))) glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); glColor3d(1,1,1) glPopMatrix() self.set2d() cubes = self.model.cubes.cubes; p = normalize(self.player.pos) if p in cubes and cubes[p].name=='water': self.water.draw(GL_POLYGON) self.fps.draw() self.reticle.draw(GL_LINES) def main(): window = Window(800,600,'Minecraft',True) glClearColor(0.5,0.7,1,1) glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glAlphaFunc(GL_GEQUAL,1) glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) pyglet.app.run() if __name__ == '__main__': main()
Stop GL_TRIANGLE_STRIP from connecting last and first vertex?
I have been trying to build a Terrain Visualizer from OpenGL and using a height map from simplex noise. I have the generator all sorted and it produces both colored and non-colored images, I only want to visualized the colored ones. However there is this weird bump thing, that I believe is a result of GL_TRIANGLE_STRIP. Picture of the artifact, oddities outlined in red: My best guess is GL_TRIANGLE_STRIP attaching the first and last vertex, but I do not know. Here is my code: import pyglet from pyglet.gl import * from pyglet.window import key import math from PIL import Image class Model: def get_points_in_list(self, fp): img = Image.open(fp) self.points = [[0 for x in range(img.width)] for y in range(img.height)] for y in range(img.height): for x in range(img.width): self.points[y][x] = ((x, img.getpixel((x, y))[3], y), img.getpixel((x, y))) def add_points_to_batch(self): ysiz = len(self.points) xsiz = len(self.points[0]) # color1[0], color1[1], color1[2], color2[0], color2[1], color2[2] for yy in range(ysiz-1): for xx in range(xsiz): pos = self.points[yy][xx][0] x, y, z = pos[0], pos[1], pos[2] pos1 = self.points[yy+1][xx][0] X, Y, Z = pos1[0], pos1[1], pos1[2] color1 = (self.points[yy][xx][1][0], self.points[yy][xx][1][1], self.points[yy][xx][1][2]) color2 = (self.points[yy+1][xx][1][0], self.points[yy+1][xx][1][1], self.points[yy+1][xx][1][2]) self.batch.add(2, GL_TRIANGLE_STRIP, None, ('v3f', (x, y, z, X, Y, Z)), ('c3B', (color1[0], color1[1], color1[2], color2[0], color2[1], color2[2])) ) def __init__(self): self.batch = pyglet.graphics.Batch() self.points = None self.get_points_in_list('Output_colored.png') self.add_points_to_batch() def draw(self): self.batch.draw() class Player: def __init__(self, pos=(0, 0, 0), rot=(0, 0)): self.pos = list(pos) self.rot = list(rot) def mouse_motion(self, dx, dy): dx /= 8 dy /= 8 self.rot[0] += dy self.rot[1] -= dx if self.rot[0]>90: self.rot[0] = 90 elif self.rot[0] < -90: self.rot[0] = -90 def update(self,dt,keys): sens = 1 s = dt*100 rotY = -self.rot[1]/180*math.pi dx, dz = s*math.sin(rotY), math.cos(rotY) if keys[key.W]: self.pos[0] += dx*sens self.pos[2] -= dz*sens if keys[key.S]: self.pos[0] -= dx*sens self.pos[2] += dz*sens if keys[key.A]: self.pos[0] -= dz*sens self.pos[2] -= dx*sens if keys[key.D]: self.pos[0] += dz*sens self.pos[2] += dx*sens if keys[key.SPACE]: self.pos[1] += s if keys[key.LSHIFT]: self.pos[1] -= s class Window(pyglet.window.Window): def push(self,pos,rot): glPushMatrix() rot = self.player.rot pos = self.player.pos glRotatef(-rot[0],1,0,0) glRotatef(-rot[1],0,1,0) glTranslatef(-pos[0], -pos[1], -pos[2]) def Projection(self): glMatrixMode(GL_PROJECTION) glLoadIdentity() def Model(self): glMatrixMode(GL_MODELVIEW) glLoadIdentity() def set2d(self): self.Projection() gluPerspective(0, self.width, 0, self.height) self.Model() def set3d(self): self.Projection() gluPerspective(70, self.width/self.height, 0.05, 1000) self.Model() def setLock(self, state): self.lock = state self.set_exclusive_mouse(state) lock = False mouse_lock = property(lambda self:self.lock, setLock) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.set_minimum_size(300,200) self.keys = key.KeyStateHandler() self.push_handlers(self.keys) pyglet.clock.schedule(self.update) self.model = Model() self.player = Player((0.5,1.5,1.5),(-30,0)) def on_mouse_motion(self,x,y,dx,dy): if self.mouse_lock: self.player.mouse_motion(dx,dy) def on_key_press(self, KEY, _MOD): if KEY == key.ESCAPE: self.close() elif KEY == key.E: self.mouse_lock = not self.mouse_lock def update(self, dt): self.player.update(dt, self.keys) def on_draw(self): self.clear() self.set3d() self.push(self.player.pos,self.player.rot) self.model.draw() glPopMatrix() if __name__ == '__main__': window = Window(width=400, height=300, caption='Terrain Viewer', resizable=True) glClearColor(0, 0, 0, 1) glEnable(GL_DEPTH_TEST) pyglet.app.run()
How to move Canvas Polygons around?
I want to move around objects in python tkinter, specifically polygons. The problem is in is_click function. I can't seem to figure out how to determine if I clicked the object. The code is not 100% complete yet, and moving around needs still need to be finished but I need to figure this out for now. I also have similar class where you can move around Circles and Rectangles, where is_click function is working, but as polygon has from 3 to 4 coordinates it is a bit more complicated. Run The classes for yourself to see what they are doing. My code for polygons: import tkinter class Polygon: def __init__(self, ax, ay, bx, by, cx, cy, dx=None, dy=None, color=None): self.ax = ax self.ay = ay self.bx = bx self.by = by self.cx = cx self.cy = cy self.dx = dx self.dy = dy self.color = color def is_click(self, event_x, event_y): pass def paint(self, g): self.g = g if self.dx is None: self.id = self.g.create_polygon(self.ax,self.ay, self.bx,self.by, self.cx,self.cy, fill=self.color) else: self.id = self.g.create_polygon(self.ax,self.ay, self.bx,self.by, self.cx,self.cy, self.dx,self.dy, fill=self.color) def move(self, d_ax=0, d_ay=0, d_bx=0, d_by=0, d_cx=0, d_cy=0, d_dx=None, d_dy=None): if d_dx is None: self.ax += d_ax self.ay += d_ay self.bx += d_bx self.by += d_by self.cx += d_cx self.cy += d_cy self.g.move(self.id, d_ax, d_ay, d_bx, d_by, d_cx, d_cy) else: self.ax += d_ax self.ay += d_ay self.bx += d_bx self.by += d_by self.cx += d_cx self.cy += d_cy self.dx += d_dx self.dy += d_dy self.g.move(self.id, d_ax, d_ay, d_bx, d_by, d_cx, d_cy, d_dx, d_dy) class Tangram: def __init__(self): self.array = [] self.g = tkinter.Canvas(width=800,height=800) self.g.pack() #all objects self.add(Polygon(500,300,625,175,750,300, color='SeaGreen')) self.add(Polygon(750,50,625,175,750,300, color='Tomato')) self.add(Polygon(500,175,562.6,237.5,500,300, color='SteelBlue')) self.add(Polygon(500,175,562.5,237.5,625,175,562.5,112.5, color='FireBrick')) self.add(Polygon(562.5,112.5,625,175,687.5,112.5, color='DarkMagenta')) self.add(Polygon(500,50,500,175,625,50, color='Gold')) self.add(Polygon(562.5,112.5,687.5,112.5,750,50,625,50, color='DarkTurquoise')) #end of all objects self.g.bind('<Button-1>', self.event_move_start) def add(self, Object): self.array.append(Object) Object.paint(self.g) def event_move_start(self, event): ix = len(self.array) - 1 while ix >= 0 and not self.array[ix].is_click(event.x, event.y): ix -= 1 if ix < 0: self.Object = None return self.Object = self.array[ix] self.ex, self.ey = event.x, event.y self.g.bind('<B1-Motion>', self.event_move) self.g.bind('<ButtonRelease-1>', self.event_release) def event_move(self): pass def event_release(self): pass Tangram() and code for Circle and Rectangle moving: import tkinter, random class Circle: def __init__(self, x, y, r, color='red'): self.x = x self.y = y self.r = r self.color = color def is_click(self, x, y): return (self.x-x)**2+(self.y-y)**2 < self.r**2 def paint(self, g): self.g = g self.id = self.g.create_oval(self.x-self.r,self.y-self.r, self.x+self.r,self.y+self.r, fill=self.color) def move(self, dx=0, dy=0): self.g.delete(self.id) self.x += dx self.y += dy self.paint(self.g) class Rectangle: def __init__(self, x, y, width, height, color='red'): self.x = x self.y = y self.width = width self.height = height self.color = color def is_click(self, x, y): return self.x<=x<self.x+self.width and self.y<=y<self.y+self.height def paint(self, g): self.g = g self.id = self.g.create_rectangle(self.x,self.y, self.x+self.width,self.y+self.height, fill=self.color) def move(self, dx=0, dy=0): self.x += dx self.y += dy self.g.move(self.id, dx, dy) class Program: def __init__(self): self.array = [] self.g = tkinter.Canvas(bg='white', width=400, height=400) self.g.pack() for i in range(20): if random.randrange(2): self.add(Circle(random.randint(50, 350),random.randint(50, 350), 20, 'blue')) else: self.add(Rectangle(random.randint(50, 350),random.randint(50, 350), 40, 30)) self.g.bind('<Button-1>', self.event_move_start) def add(self, Object): self.array.append(Object) Object.paint(self.g) def event_move_start(self, e): ix = len(self.array)-1 while ix >= 0 and not self.array[ix].is_click(e.x, e.y): ix -= 1 if ix < 0: self.Object = None return self.Object = self.array[ix] self.ex, self.ey = e.x, e.y self.g.bind('<B1-Motion>', self.event_move) self.g.bind('<ButtonRelease-1>', self.event_release) def event_move(self, e): self.Object.move(e.x-self.ex, e.y-self.ey) self.ex, self.ey = e.x, e.y def event_release(self, e): self.g.unbind('<B1-Motion>') self.g.unbind('<ButtonRelease-1>') self.Object = None Program()
This is not the full anwser to your questions, but its too long for a comment. One way, that I would centrally consider, is to change/amend your code so that it uses find_closes method. With this method, you can determine which widget (i.e. polygon) is clicked very easily. As a quick prof of concept, you can make the following changes in the Tangram and ploygon class: def event_move_start(self, event): ix = len(self.array) - 1 while ix >= 0 and not self.array[ix].is_click(event, self.g, event.x, event.y): # add event and canvas to argumetns In polygon: def is_click(self, event, g, event_x, event_y): widget_id = event.widget.find_closest(event.x, event.y) print(widget_id) g.move(widget_id, 1, 1) # just dummy move for a clicked widget pass This will print the ids of clicked widgets/polygons and slightly move the clicked polygon. This can be used to select which object was clicked, and having this id object, you can for moving or whatever. p.s. the full code that I used to test it is here. Hope this helps.