在Pygame中是否有一种方法可以更改导入的.obj文件的位置和大小?

huangapple go评论141阅读模式
英文:

Is there a way to change the location and size of an imported .obj file in Pygame?

问题

我使用Blender创建了一个.obj文件,并使用OBJ文件加载器加载到Pygame中,如sugested by skrx在以下链接中:
https://stackoverflow.com/questions/47660370/error-in-objfileloader

是否有一种简单的方法在导入Pygame后更改导入的.obj文件的位置、高度和宽度?例如,如果您创建了一个tree.obj文件,要能够以不同的大小将相同的树放在不同的位置?

按照下面的代码,可能像这样:

object_obj = OBJ("Tree.obj", swapyz=False)

object_obj.setX = 0

object_obj.setWidth = 3

object_obj.setHeight = 10

或者通过向obj加载器发送额外的参数,改变顶点?

OBJ加载器:

import pygame
from OpenGL.GL import *

def MTL(filename):
    filename = 'OBJ/' + filename
    contents = {}
    mtl = None
    for line in open(filename, "r"):
        if line.startswith('#'): continue
        values = line.split()
        if not values: continue
        if values[0] == 'newmtl':
            mtl = contents[values[1]] = {}
        elif mtl is None:
            raise ValueError("mtl file doesn't start with newmtl stmt")
        elif values[0] == 'map_Kd':
            # load the texture referred to by this declaration
            mtl[values[0]] = values[1]
            surf = pygame.image.load(mtl['map_Kd'])
            image = pygame.image.tostring(surf, 'RGBA', 1)
            ix, iy = surf.get_rect().size
            texid = mtl['texture_Kd'] = glGenTextures(1)
            glBindTexture(GL_TEXTURE_2D, texid)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                            GL_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                            GL_LINEAR)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE, image)
        else:
            mtl[values[0]] = list(map(float, values[1:]))
    return contents

class OBJ:
    def __init__(self, filename, swapyz=False):
        """Loads a Wavefront OBJ file."""
        self.vertices = []
        self.normals = []
        self.texcoords = []
        self.faces = []

        material = None
        for line in open(filename, "r"):
            if line.startswith('#'): continue
            values = line.split()
            if not values: continue
            if values[0] == 'v':
                v = list(map(float, values[1:4]))
                if swapyz:
                    v = v[0], v[2], v[1]
                self.vertices.append(v)
            elif values[0] == 'vn':
                v = list(map(float, values[1:4]))
                if swapyz:
                    v = v[0], v[2], v[1]
                self.normals.append(v)
            elif values[0] == 'vt':
                self.texcoords.append(list(map(float, values[1:3])))
            elif values[0] in ('usemtl', 'usemat'):
                material = values[1]
            elif values[0] == 'mtllib':
                self.mtl = MTL(values[1])
            elif values[0] == 'f':
                face = []
                texcoords = []
                norms = []
                for v in values[1:]:
                    w = v.split('/')
                    face.append(int(w[0]))
                    if len(w) >= 2 and len(w[1]) > 0:
                        texcoords.append(int(w[1]))
                    else:
                        texcoords.append(0)
                    if len(w) >= 3 and len(w[2]) > 0:
                        norms.append(int(w[2]))
                    else:
                        norms.append(0)
                self.faces.append((face, norms, texcoords, material))

        self.gl_list = glGenLists(1)
        glNewList(self.gl_list, GL_COMPILE)
        glEnable(GL_TEXTURE_2D)
        glFrontFace(GL_CCW)
        for face in self.faces:
            vertices, normals, texture_coords, material = face

            mtl = self.mtl[material]
            if 'texture_Kd' in mtl:
                # use diffuse texmap
                glBindTexture(GL_TEXTURE_2D, mtl['texture_Kd'])
            else:
                # just use diffuse colour
                glColor(*mtl['Kd'])

            glBegin(GL_POLYGON)
            for i in range(len(vertices)):
                if normals[i] > 0:
                    glNormal3fv(self.normals[normals[i] - 1])
                if texture_coords[i] > 0:
                    glTexCoord2fv(self.texcoords[texture_coords[i] - 1])
                glVertex3fv(self.vertices[vertices[i] - 1])
            glEnd()
        glDisable(GL_TEXTURE_2D)
        glEndList()

在Pygame中显示.OBJ文件:

# Basic OBJ file viewer. needs objloader from:
#  http://www.pygame.org/wiki/OBJFileLoader
# LMB + move: rotate
# RMB + move: pan
# Scroll wheel: zoom in/out
import sys, pygame
from pygame.locals import *
from pygame.constants import *
from OpenGL.GL import *
from OpenGL.GLU import *

# IMPORT OBJECT LOADER
from OBJ_Loader import *

pygame.init()
viewport = (800,600)
hx = viewport[0]/2
hy = viewport[1]/2
srf = pygame.display.set_mode(viewport, OPENGL | DOUBLEBUF)

glLightfv(GL_LIGHT0, GL_POSITION,  (-40, 200, 100, 0.0))
glLightfv(GL_LIGHT0, GL_AMBIENT, (0.2, 0.2, 0.2, 1.0))
glLightfv(GL_LIGHT0, GL_DIFFUSE, (0.5, 0.5, 0.5, 1.0))
glEnable(GL_LIGHT0)
glEnable(GL_LIGHTING)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)           # most obj files expect to be smooth-shaded

# LOAD OBJECT AFTER PYGAME INIT
object_obj = OBJ("Tree.obj", swapyz=False)
clock = pygame.time.Clock()

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
width, height = viewport
gluPerspective(90, 1, 0.001, 1000.0)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_MODELVIEW)

rx, ry = (0,0)
tx, ty = (0,0)
zpos = 1
rotate = move = False
while 1:
    clock.tick(30)
    for e in pygame.event.get():
        if e.type == QUIT:
            sys.exit()
        elif e.type == KEYDOWN and e.key == K_ESCAPE:
            sys.exit()
        elif

<details>
<summary>英文:</summary>

I created an .obj file using blender and loaded into Pygame using the OBJfileloader as sugested by skrx in:
[https://stackoverflow.com/questions/47660370/error-in-objfileloader][1]


  [1]: https://stackoverflow.com/questions/47660370/error-in-objfileloader

Is there an easy way to change the location, height and width of the imported .obj file after importing it into Pygame? As an example, if you create a tree.obj file, to be able to place that same tree in different places with different sizes? 

Following the code below, maybe something like:

```py
object_obj = OBJ(&quot;Tree.obj&quot;, swapyz=False)

object_obj.setX = 0

object_obj.setWidth = 3

object_obj.setHeight =10

Or by sending extra parameters to the obj loader, changing the vertices?

OBJ Loader:

import pygame
from OpenGL.GL import *
def MTL(filename):
filename = &#39;OBJ/&#39;+filename
contents = {}
mtl = None
for line in open(filename, &quot;r&quot;):
if line.startswith(&#39;#&#39;): continue
values = line.split()
if not values: continue
if values[0] == &#39;newmtl&#39;:
mtl = contents[values[1]] = {}
elif mtl is None:
raise ValueError(&quot;mtl file doesn&#39;t start with newmtl stmt&quot;)
elif values[0] == &#39;map_Kd&#39;:
# load the texture referred to by this declaration
mtl[values[0]] = values[1]
surf = pygame.image.load(mtl[&#39;map_Kd&#39;])
image = pygame.image.tostring(surf, &#39;RGBA&#39;, 1)
ix, iy = surf.get_rect().size
texid = mtl[&#39;texture_Kd&#39;] = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texid)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image)
else:
mtl[values[0]] = list(map(float, values[1:]))
return contents
class OBJ:
def __init__(self, filename, swapyz=False):
&quot;&quot;&quot;Loads a Wavefront OBJ file. &quot;&quot;&quot;
self.vertices = []
self.normals = []
self.texcoords = []
self.faces = []
material = None
for line in open(filename, &quot;r&quot;):
if line.startswith(&#39;#&#39;): continue
values = line.split()
if not values: continue
if values[0] == &#39;v&#39;:
v = list(map(float, values[1:4]))
if swapyz:
v = v[0], v[2], v[1]
self.vertices.append(v)
elif values[0] == &#39;vn&#39;:
v = list(map(float, values[1:4]))
if swapyz:
v = v[0], v[2], v[1]
self.normals.append(v)
elif values[0] == &#39;vt&#39;:
self.texcoords.append(list(map(float, values[1:3])))
elif values[0] in (&#39;usemtl&#39;, &#39;usemat&#39;):
material = values[1]
elif values[0] == &#39;mtllib&#39;:
self.mtl = MTL(values[1])
elif values[0] == &#39;f&#39;:
face = []
texcoords = []
norms = []
for v in values[1:]:
w = v.split(&#39;/&#39;)
face.append(int(w[0]))
if len(w) &gt;= 2 and len(w[1]) &gt; 0:
texcoords.append(int(w[1]))
else:
texcoords.append(0)
if len(w) &gt;= 3 and len(w[2]) &gt; 0:
norms.append(int(w[2]))
else:
norms.append(0)
self.faces.append((face, norms, texcoords, material))
self.gl_list = glGenLists(1)
glNewList(self.gl_list, GL_COMPILE)
glEnable(GL_TEXTURE_2D)
glFrontFace(GL_CCW)
for face in self.faces:
vertices, normals, texture_coords, material = face
mtl = self.mtl[material]
if &#39;texture_Kd&#39; in mtl:
# use diffuse texmap
glBindTexture(GL_TEXTURE_2D, mtl[&#39;texture_Kd&#39;])
else:
# just use diffuse colour
glColor(*mtl[&#39;Kd&#39;])
glBegin(GL_POLYGON)
for i in range(len(vertices)):
if normals[i] &gt; 0:
glNormal3fv(self.normals[normals[i] - 1])
if texture_coords[i] &gt; 0:
glTexCoord2fv(self.texcoords[texture_coords[i] - 1])
glVertex3fv(self.vertices[vertices[i] - 1])
glEnd()
glDisable(GL_TEXTURE_2D)
glEndList()

Displaying .OBJ file in Pygame:

# Basic OBJ file viewer. needs objloader from:
#  http://www.pygame.org/wiki/OBJFileLoader
# LMB + move: rotate
# RMB + move: pan
# Scroll wheel: zoom in/out
import sys, pygame
from pygame.locals import *
from pygame.constants import *
from OpenGL.GL import *
from OpenGL.GLU import *
# IMPORT OBJECT LOADER
from OBJ_Loader import *
pygame.init()
viewport = (800,600)
hx = viewport[0]/2
hy = viewport[1]/2
srf = pygame.display.set_mode(viewport, OPENGL | DOUBLEBUF)
glLightfv(GL_LIGHT0, GL_POSITION,  (-40, 200, 100, 0.0))
glLightfv(GL_LIGHT0, GL_AMBIENT, (0.2, 0.2, 0.2, 1.0))
glLightfv(GL_LIGHT0, GL_DIFFUSE, (0.5, 0.5, 0.5, 1.0))
glEnable(GL_LIGHT0)
glEnable(GL_LIGHTING)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)           # most obj files expect to be smooth-shaded
# LOAD OBJECT AFTER PYGAME INIT
object_obj = OBJ(&quot;Tree.obj&quot;, swapyz=False)
clock = pygame.time.Clock()
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
width, height = viewport
gluPerspective(90, 1, 0.001, 1000.0)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_MODELVIEW)
rx, ry = (0,0)
tx, ty = (0,0)
zpos = 1
rotate = move = False
while 1:
clock.tick(30)
for e in pygame.event.get():
if e.type == QUIT:
sys.exit()
elif e.type == KEYDOWN and e.key == K_ESCAPE:
sys.exit()
elif e.type == MOUSEBUTTONDOWN:
if e.button == 4: zpos = max(1, zpos-1)
elif e.button == 5: zpos += 1
elif e.button == 1: rotate = True
elif e.button == 3: move = True
elif e.type == MOUSEBUTTONUP:
if e.button == 1: rotate = False
elif e.button == 3: move = False
elif e.type == MOUSEMOTION:
i, j = e.rel
if rotate:
rx += i
ry += j
if move:
tx += i
ty -= j
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
# RENDER OBJECT
glTranslate(tx/20., ty/20., - zpos)
glRotate(ry, 1, 0, 0)
glRotate(rx, 0, 1, 0)
glCallList(obj.gl_list)
pygame.display.flip()

答案1

得分: 3

&lt;kbd&gt;[![][1] repl.it/@Rabbid76/pygame-opengl-wavefront-obj](https://replit.com/@Rabbid76/pygame-opengl-wavefront-obj#main.py)&lt;/kbd&gt;

不要更改顶点坐标。使用 [`glTranslate`](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml) 和 [`glScale`](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glScale.xml) 来定义模型变换。

在读取顶点坐标时计算 [轴对齐边界框](https://en.wikipedia.org/wiki/Bounding_volume):

```py
class OBJ:
    def __init__(self, filename, swapyz=False):
        &quot;&quot;&quot;加载 Wavefront OBJ 文件。&quot;&quot;&quot;
        self.vertices = []
        self.normals = []
        self.texcoords = []
        self.faces = []

        self.min_v = [float(&quot;inf&quot;), float(&quot;inf&quot;), float(&quot;inf&quot;)]
        self.max_v = [-float(&quot;inf&quot;), -float(&quot;inf&quot;), -float(&quot;inf&quot;)]

        material = None
        for line in open(filename, &quot;r&quot;):
            if line.startswith(&#39;#&#39;): continue
            values = line.split()
            if not values: continue
            if values[0] == &#39;v&#39;:
                v = list(map(float, values[1:4]))
                if swapyz:
                    v = v[0], v[2], v[1]

                for i in range(3):
                    self.min_v[i] = min(self.min_v[i], v[i])
                    self.max_v[i] = max(self.max_v[i], v[i])

                self.vertices.append(v)
            # [...]
        
        self.size = [self.max_v[i]-self.min_v[i] for i in range(3)]
        # [...]

在绘制对象时进行缩放和平移。使用 glPushMatrix / glPopMatrix 来保存和恢复当前矩阵,在变换前后使用:

# 渲染对象
glTranslate(tx/20., ty/20., - zpos)
glRotate(ry, 1, 0, 0)
glRotate(rx, 0, 1, 0)

pos = [0, 0, -5]
size = [3, 3, 10]
scale = [size[i]/obj.size[i] for i in range(3)]

glPushMatrix()
glTranslate(*pos)
glScale(*scale)
glCallList(obj.gl_list)
glPopMatrix()

或者也可以在显示列表中完成:

self.gl_list = glGenLists(1)
glNewList(self.gl_list, GL_COMPILE)
glEnable(GL_TEXTURE_2D)
glFrontFace(GL_CCW)

glPushMatrix()
glTranslate(*pos)
glScale(*scale)

for face in self.faces:
    vertices, normals, texture_coords, material = face
    # [...]
    
glDisable(GL_TEXTURE_2D)

glPopMatrix()
glEndList()

<details>
<summary>英文:</summary>
&lt;kbd&gt;[![][1] repl.it/@Rabbid76/pygame-opengl-wavefront-obj](https://replit.com/@Rabbid76/pygame-opengl-wavefront-obj#main.py)&lt;/kbd&gt;
Do not change the vertex coordinates. Use [`glTranslate`](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml) and [`glScale`](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glScale.xml) to define a model transformation.
Compute the [Axis Aligned Bounding Box](https://en.wikipedia.org/wiki/Bounding_volume), when the vertex coordinates are read:
```py
class OBJ:
def __init__(self, filename, swapyz=False):
&quot;&quot;&quot;Loads a Wavefront OBJ file. &quot;&quot;&quot;
self.vertices = []
self.normals = []
self.texcoords = []
self.faces = []
self.min_v = [float(&quot;inf&quot;), float(&quot;inf&quot;), float(&quot;inf&quot;)]
self.max_v = [-float(&quot;inf&quot;), -float(&quot;inf&quot;), -float(&quot;inf&quot;)]
material = None
for line in open(filename, &quot;r&quot;):
if line.startswith(&#39;#&#39;): continue
values = line.split()
if not values: continue
if values[0] == &#39;v&#39;:
v = list(map(float, values[1:4]))
if swapyz:
v = v[0], v[2], v[1]
for i in range(3):
self.min_v[i] = min(self.min_v[i], v[i])
self.max_v[i] = max(self.max_v[i], v[i])
self.vertices.append(v)
# [...]
self.size = [self.max_v[i]-self.min_v[i] for i in range(3)]
# [...]

Scale and translate the object when it is drawn. Use glPushMatrix / glPopMatrix to save and restore the current matrix before and after the transformations:

# RENDER OBJECT
glTranslate(tx/20., ty/20., - zpos)
glRotate(ry, 1, 0, 0)
glRotate(rx, 0, 1, 0)

pos = [0, 0, -5]
size = [3, 3, 10]
scale = [size[i]/obj.size[i] for i in range(3)]

glPushMatrix()
glTranslate(*pos)
glScale(*scale)
glCallList(obj.gl_list)
glPopMatrix()

Alternatively that can be done in the display list, too:

self.gl_list = glGenLists(1)
glNewList(self.gl_list, GL_COMPILE)
glEnable(GL_TEXTURE_2D)
glFrontFace(GL_CCW)

glPushMatrix()
glTranslate(*pos)
glScale(*scale)

for face in self.faces:
    vertices, normals, texture_coords, material = face
    # [...]
    
glDisable(GL_TEXTURE_2D)

glPopMatrix()
glEndList()

huangapple
  • 本文由 发表于 2020年1月6日 17:47:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/59609837.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定