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!
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()