Skip to content

Commit 53b7013

Browse files
author
Kurt Yoder
authored
Merge pull request #30 from SergioRAgostinho/pyglet-opt
Made Pyglet an optional dependency
2 parents af26325 + 1b3b5ce commit 53b7013

File tree

12 files changed

+163
-55
lines changed

12 files changed

+163
-55
lines changed

README.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ PyWavefront
33

44
This python module allows you to read Wavefront 3D object files
55
(`something.obj` and `something.mtl`) and use them as Python objects.
6-
Currently Pyglet is required to render and display these objects.
6+
If you optionally want to render and display these objects, Pyglet is required.
77

88
Currently, only a subset of [the defined
99
specification](https://en.wikipedia.org/wiki/Wavefront_.obj_file) has
1010
been implemented.
1111

12-
Requirements
12+
Optional Dependencies
1313
------------
1414

1515
* [Pyglet](http://www.pyglet.org/)
@@ -19,14 +19,29 @@ Usage
1919

2020
### From Python
2121

22-
import pywavefront
23-
meshes = pywavefront.Wavefront('something.obj')
24-
meshes.draw()
22+
**Basic**
23+
24+
```python
25+
import pywavefront
26+
meshes = pywavefront.Wavefront('something.obj')
27+
```
28+
29+
**Visualization**
30+
31+
```python
32+
import pywavefront
33+
34+
[create a window and set up your OpenGl context]
35+
meshes = pywavefront.Wavefront('something.obj')
36+
37+
[inside your drawing loop]
38+
meshes.draw()
39+
```
2540

2641
### Example Script
2742

2843
There are two pyglet example scripts with included `.obj` and `.mtl` files in the `example` directory. To run them, change to the `example`
29-
directory and run either `./pyglet_demo.py` or `.pyglet_demo2.py`.
44+
directory and run either `./pyglet_demo.py` or `.pyglet_demo2.py`. Pyglet is required for these.
3045

3146
### Generating a Wavefront file with Blender
3247

example/pyglet_demo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import pyglet
88
from pyglet.gl import *
99

10-
import pywavefront
10+
import pywavefront.visualization
1111

1212
rotation = 0
1313

example/pyglet_demo2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pyglet.gl import *
1010

1111
from pywavefront import Wavefront
12+
import pywavefront.visualization
1213

1314
rotation = 0
1415

pywavefront/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
# POSSIBILITY OF SUCH DAMAGE.
3333
# ----------------------------------------------------------------------------
3434

35-
from pyglet.gl import *
36-
3735
import pywavefront.material
3836
import pywavefront.mesh
3937
import pywavefront.parser

pywavefront/material.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
# POSSIBILITY OF SUCH DAMAGE.
3333
# ----------------------------------------------------------------------------
3434

35-
from pyglet.gl import *
36-
3735
import pywavefront.parser as parser
3836
import pywavefront.texture as texture
3937

@@ -87,26 +85,12 @@ def unset_texture(self):
8785
self.texture = None
8886

8987
def gl_light(self, lighting):
90-
"""Return a GLfloat with length 4, containing the 4 lighting values."""
91-
return (GLfloat * 4)(*(lighting))
92-
93-
def draw(self, face=GL_FRONT_AND_BACK):
94-
if self.texture:
95-
self.texture.draw()
96-
else:
97-
glDisable(GL_TEXTURE_2D)
98-
99-
glMaterialfv(face, GL_DIFFUSE, self.gl_light(self.diffuse))
100-
glMaterialfv(face, GL_AMBIENT, self.gl_light(self.ambient))
101-
glMaterialfv(face, GL_SPECULAR, self.gl_light(self.specular))
102-
glMaterialfv(face, GL_EMISSION, self.gl_light(self.emissive))
103-
glMaterialf(face, GL_SHININESS, self.shininess)
104-
105-
if self.gl_floats is None:
106-
self.gl_floats = (GLfloat * len(self.vertices))(*self.vertices)
107-
self.triangle_count = len(self.vertices) / 8
108-
glInterleavedArrays(GL_T2F_N3F_V3F, 0, self.gl_floats)
109-
glDrawArrays(GL_TRIANGLES, 0, int(self.triangle_count))
88+
"""Method placeholder"""
89+
raise Exception("Please import pywavefront.visualization")
90+
91+
def draw(self, face=None):
92+
"""Method placeholder"""
93+
raise Exception("Please import pywavefront.visualization")
11094

11195

11296
class MaterialParser(parser.Parser):

pywavefront/mesh.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@
3232
# POSSIBILITY OF SUCH DAMAGE.
3333
# ----------------------------------------------------------------------------
3434

35-
from pyglet.gl import *
36-
37-
3835
class Mesh(object):
3936
"""This is a basic mesh for drawing using OpenGL. Interestingly, it does
4037
not contain its own vertices. These are instead drawn via materials."""
@@ -55,11 +52,5 @@ def add_material(self, material):
5552
self.materials.append(material)
5653

5754
def draw(self):
58-
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
59-
glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT)
60-
glEnable(GL_CULL_FACE)
61-
glCullFace(GL_BACK)
62-
for material in self.materials:
63-
material.draw()
64-
glPopAttrib()
65-
glPopClientAttrib()
55+
"""Method placeholder"""
56+
raise Exception("Please import pywavefront.visualization")

pywavefront/parser.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
# ----------------------------------------------------------------------------
3434

3535
import os
36-
import pyglet
3736
import logging
3837

3938
class Parser(object):

pywavefront/texture.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,23 @@
3232
# POSSIBILITY OF SUCH DAMAGE.
3333
# ----------------------------------------------------------------------------
3434

35-
from pyglet.gl import *
36-
35+
import os
3736

3837
class Texture(object):
3938
def __init__(self, path):
4039
self.image_name = path
41-
self.image = pyglet.image.load(self.image_name).texture
42-
self.verify_dimensions()
40+
self.image = None
41+
42+
if not os.path.exists(path):
43+
raise Exception("Requested file does not exist")
4344

4445
def draw(self):
45-
glEnable(self.image.target)
46-
glBindTexture(self.image.target, self.image.id)
47-
gl.glTexParameterf(self.image.target,
48-
gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP)
49-
gl.glTexParameterf(self.image.target,
50-
gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP)
46+
"""Method placeholder"""
47+
raise Exception("Please import pywavefront.visualization")
48+
49+
def load_image(self):
50+
"""Method placeholder"""
51+
raise Exception("Please import pywavefront.visualization")
5152

5253
def verify_dimensions(self):
5354
self.verify('width')

pywavefront/visualization.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# ----------------------------------------------------------------------------
2+
# PyWavefront
3+
# Copyright (c) 2018 Kurt Yoder
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions
8+
# are met:
9+
#
10+
# * Redistributions of source code must retain the above copyright
11+
# notice, this list of conditions and the following disclaimer.
12+
# * Redistributions in binary form must reproduce the above copyright
13+
# notice, this list of conditions and the following disclaimer in
14+
# the documentation and/or other materials provided with the
15+
# distribution.
16+
# * Neither the name of PyWavefront nor the names of its
17+
# contributors may be used to endorse or promote products
18+
# derived from this software without specific prior written
19+
# permission.
20+
#
21+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24+
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25+
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30+
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31+
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32+
# POSSIBILITY OF SUCH DAMAGE.
33+
# ----------------------------------------------------------------------------
34+
35+
from pyglet.gl import *
36+
37+
from pywavefront.material import Material
38+
from pywavefront.mesh import Mesh
39+
from pywavefront.texture import Texture
40+
41+
class _Material(object):
42+
"""
43+
Overrides the drawing routines for the Material class.
44+
This class should not be used directly. Its purpose is to shadow
45+
the methods to be overriden.
46+
"""
47+
48+
def gl_light(self, lighting):
49+
"""Return a GLfloat with length 4, containing the 4 lighting values."""
50+
return (GLfloat * 4)(*(lighting))
51+
52+
def draw(self, face=GL_FRONT_AND_BACK):
53+
if self.texture:
54+
self.texture.draw()
55+
else:
56+
glDisable(GL_TEXTURE_2D)
57+
58+
glMaterialfv(face, GL_DIFFUSE, self.gl_light(self.diffuse))
59+
glMaterialfv(face, GL_AMBIENT, self.gl_light(self.ambient))
60+
glMaterialfv(face, GL_SPECULAR, self.gl_light(self.specular))
61+
glMaterialfv(face, GL_EMISSION, self.gl_light(self.emissive))
62+
glMaterialf(face, GL_SHININESS, self.shininess)
63+
64+
if self.gl_floats is None:
65+
self.gl_floats = (GLfloat * len(self.vertices))(*self.vertices)
66+
self.triangle_count = len(self.vertices) / 8
67+
glInterleavedArrays(GL_T2F_N3F_V3F, 0, self.gl_floats)
68+
glDrawArrays(GL_TRIANGLES, 0, int(self.triangle_count))
69+
70+
71+
setattr(Material, "gl_light", _Material.gl_light)
72+
setattr(Material, "draw", _Material.draw)
73+
74+
75+
class _Mesh(object):
76+
"""
77+
Overrides the drawing routines for the Mesh class.
78+
This class should not be used directly. Its purpose is to shadow
79+
the methods to be overriden.
80+
"""
81+
def draw(self):
82+
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
83+
glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT)
84+
glEnable(GL_CULL_FACE)
85+
glCullFace(GL_BACK)
86+
for material in self.materials:
87+
material.draw()
88+
glPopAttrib()
89+
glPopClientAttrib()
90+
91+
setattr(Mesh, "draw", _Mesh.draw)
92+
93+
94+
class _Texture(object):
95+
96+
def init(self, path):
97+
self.image_name = path
98+
self.load_image()
99+
100+
def draw(self):
101+
if not self.image:
102+
self.load_image()
103+
104+
glEnable(self.image.target)
105+
glBindTexture(self.image.target, self.image.id)
106+
gl.glTexParameterf(self.image.target,
107+
gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP)
108+
gl.glTexParameterf(self.image.target,
109+
gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP)
110+
111+
def load_image(self):
112+
self.image = pyglet.image.load(self.image_name).texture
113+
self.verify_dimensions()
114+
115+
setattr(Texture, "__init__", _Texture.init)
116+
setattr(Texture, "draw", _Texture.draw)
117+
setattr(Texture, "load_image", _Texture.load_image)

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
pyglet

0 commit comments

Comments
 (0)