Skip to content

Commit c75887e

Browse files
author
Kurt Yoder
authored
Merge pull request #70 from einarf/missing-mat-attrs
Missing material properties + tests
2 parents 39fb5f6 + 8096d33 commit c75887e

File tree

6 files changed

+155
-35
lines changed

6 files changed

+155
-35
lines changed

.gitignore

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
.DS_Store
1+
2+
# build
3+
*.egg-info
4+
dist
25
*.pyc
36
*.pyo
47
build
58
MANIFEST
6-
dist
79
*.swp
8-
.idea
9-
*.egg-info
10+
11+
# virtualenvs
1012
.venv
1113
.venv2
1214
.venv3
15+
16+
# IDEs
17+
.idea
18+
.vscode
19+
20+
# osx
21+
.DS_Store

pywavefront/material.py

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,30 @@ def __init__(self, name, is_default=False):
5252
self.ambient = [.2, .2, .2, 1.]
5353
self.specular = [0., 0., 0., 1.]
5454
self.emissive = [0., 0., 0., 1.]
55+
self.transparency = 1.0
5556
self.shininess = 0.
56-
self.texture = None
57+
self.optical_density = 1.0
58+
# Multiple illumination models are available, per material. These are enumerated as follows:
59+
# 0. Color on and Ambient off
60+
# 1. Color on and Ambient on
61+
# 2. Highlight on
62+
# 3. Reflection on and Ray trace on
63+
# 4. Transparency: Glass on, Reflection: Ray trace on
64+
# 5. Reflection: Fresnel on and Ray trace on
65+
# 6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
66+
# 7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
67+
# 8. Reflection on and Ray trace off
68+
# 9. Transparency: Glass on, Reflection: Ray trace off
69+
# 10. Casts shadows onto invisible surfaces
70+
self.illumination_model = 0
71+
72+
self.texture = None # diffuse
73+
self.texture_ambient = None
74+
self.texture_specular_color = None
75+
self.texture_specular_highlight = None
76+
self.texture_alpha = None
77+
self.texture_bump = None
78+
5779
self.is_default = is_default
5880

5981
# Interleaved array of floats in GL_T2F_N3F_V3F format
@@ -62,11 +84,6 @@ def __init__(self, name, is_default=False):
6284

6385
self.gl_floats = None
6486

65-
@property
66-
def file(self):
67-
"""File with full path"""
68-
return os.path.join(self.path, self.name)
69-
7087
@property
7188
def has_normals(self):
7289
return "N3F" in self.vertex_format
@@ -95,7 +112,7 @@ def pad_light(self, values):
95112

96113
def set_alpha(self, alpha):
97114
"""Set alpha/last value on all four lighting attributes."""
98-
alpha = float(alpha)
115+
self.transparency = alpha
99116
self.diffuse[3] = alpha
100117
self.ambient[3] = alpha
101118
self.specular[3] = alpha
@@ -116,6 +133,21 @@ def set_emissive(self, values=None):
116133
def set_texture(self, path):
117134
self.texture = Texture(path)
118135

136+
def set_texture_ambient(self, path):
137+
self.texture_ambient = Texture(path)
138+
139+
def set_texture_specular_color(self, path):
140+
self.texture_specular_color = Texture(path)
141+
142+
def set_texture_specular_highlight(self, path):
143+
self.texture_specular_highlight = Texture(path)
144+
145+
def set_texture_alpha(self, path):
146+
self.texture_alpha = Texture(path)
147+
148+
def set_texture_bump(self, path):
149+
self.texture_bump = Texture(path)
150+
119151
def unset_texture(self):
120152
self.texture = None
121153

@@ -166,24 +198,57 @@ def parse_Ns(self):
166198

167199
@auto_consume
168200
def parse_d(self):
169-
self.this_material.set_alpha(self.values[1])
201+
"""Transparency"""
202+
self.this_material.set_alpha(float(self.values[1]))
203+
204+
@auto_consume
205+
def parse_Tr(self):
206+
"""Transparency (alternative)"""
207+
self.this_material.set_alpha(1.0 - float(self.values[1]))
170208

171209
@auto_consume
172210
def parse_map_Kd(self):
211+
"""Diffuse map"""
173212
Kd = os.path.join(self.dir, " ".join(self.values[1:]))
174213
self.this_material.set_texture(Kd)
175214

176215
@auto_consume
177-
def parse_Ni(self):
178-
# unimplemented
179-
pass
216+
def parse_map_Ka(self):
217+
"""Ambient map"""
218+
Kd = os.path.join(self.dir, " ".join(self.values[1:]))
219+
self.this_material.set_texture_ambient(Kd)
180220

181221
@auto_consume
182-
def parse_Tr(self):
183-
# unimplemented
184-
pass
222+
def parse_map_Ks(self):
223+
"""Specular color map"""
224+
Kd = os.path.join(self.dir, " ".join(self.values[1:]))
225+
self.this_material.set_texture_specular_color(Kd)
226+
227+
@auto_consume
228+
def parse_map_Ns(self):
229+
"""Specular color map"""
230+
Kd = os.path.join(self.dir, " ".join(self.values[1:]))
231+
self.this_material.set_texture_specular_highlight(Kd)
232+
233+
@auto_consume
234+
def parse_map_d(self):
235+
"""Alpha map"""
236+
Kd = os.path.join(self.dir, " ".join(self.values[1:]))
237+
self.this_material.set_texture_alpha(Kd)
238+
239+
@auto_consume
240+
def parse_map_bump(self):
241+
"""Bump map"""
242+
Kd = os.path.join(self.dir, " ".join(self.values[1:]))
243+
self.this_material.set_texture_bump(Kd)
244+
245+
def parse_bump(self):
246+
self.parse_map_bump()
247+
248+
@auto_consume
249+
def parse_Ni(self):
250+
self.this_material.optical_density = float(self.values[1])
185251

186252
@auto_consume
187253
def parse_illum(self):
188-
# unimplemented
189-
pass
254+
self.this_material.illumination_model = float(self.values[1])

pywavefront/visualization.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@ def draw_material(material, face=GL_FRONT_AND_BACK):
8787
glEnable(GL_CULL_FACE)
8888
glCullFace(GL_BACK)
8989

90-
if material.texture and material.has_uvs:
91-
bind_texture(material.texture)
90+
# Fall back to ambient texture if no diffuse
91+
texture = material.texture or material.texture_ambient
92+
if texture and material.has_uvs:
93+
bind_texture(texture)
9294
else:
9395
glDisable(GL_TEXTURE_2D)
9496

test/simple_parsetest.mtl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Comment
2+
newmtl Material.simple
3+
Ns 1.0
4+
Ka 0.0 0.0 0.0
5+
Kd 0.1 0.1 0.1
6+
Ks 0.2 0.2 0.2
7+
d 1.0
8+
Tr 0.0
9+
illum 2
10+
Ni 0.75
11+
map_Kd kd.png
12+
map_Ka ka.png
13+
map_Ks ks.png
14+
map_Ns ns.png
15+
map_d d.png
16+
map_bump bump.png
17+
bump bump.png
18+
newmtl Material2.simple
19+
Ns 1.0
20+
Ka 0.0 0.0 0.0
21+
Kd 0.64 0.64 0.64
22+
Ks 0.5 0.5 0.5
23+
d 1.0
24+
map_Kd 4x4.png

test/test_material.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@ def setUp(self):
1313
# Append current path to locate files
1414
self.material = pywavefront.material.Material(prepend_dir('material'))
1515
self.material.set_texture(prepend_dir('4x4.png'))
16+
self.material.set_texture_ambient(prepend_dir('4x4.png'))
1617

1718
def testSetTexture(self):
1819
"""Running set_texture should set a texture."""
1920
self.assertEqual(self.material.texture.__class__,
20-
pywavefront.texture.Texture)
21+
pywavefront.texture.Texture)
22+
self.assertEqual(self.material.texture_ambient.__class__,
23+
pywavefront.texture.Texture)
24+
25+
self.assertEqual(self.material.texture.path, prepend_dir('4x4.png'))
26+
self.assertEqual(self.material.texture_ambient.path, prepend_dir('4x4.png'))
2127

2228
def testUnsetTexture(self):
2329
"""Running unset_texture should set texture to None."""
@@ -57,6 +63,9 @@ def testSetEmissive(self):
5763
self.material.set_emissive()
5864
self.assertEqual(self.material.emissive, [0., 0., 0., 0.])
5965

66+
def testTransparency(self):
67+
self.assertEqual(self.material.transparency, 1.0)
68+
6069

6170
class TestInvalidMaterial(unittest.TestCase):
6271

test/test_parser.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import pywavefront.parser
55
from pywavefront.exceptions import PywavefrontException
6-
6+
from pywavefront.material import MaterialParser
77

88
def prepend_dir(file):
99
return os.path.join(os.path.dirname(__file__), file)
@@ -151,15 +151,15 @@ def testObjColors(self):
151151

152152
class TestMtlParser(unittest.TestCase):
153153
def setUp(self):
154-
# Append current path to locate files
155-
meshes = pywavefront.Wavefront(prepend_dir('simple.obj'))
156-
self.material1 = meshes.mesh_list[0].materials[0]
157-
self.material2 = meshes.mesh_list[1].materials[0]
154+
parser = MaterialParser(prepend_dir('simple_parsetest.mtl'))
155+
self.materials = parser.materials
156+
self.material1 = self.materials['Material.simple']
157+
self.material2 = self.materials['Material2.simple']
158158

159159
def testMtlName(self):
160160
"""Parsing an obj file with known material names should set those names."""
161-
self.assertEqual(self.material1.name, 'Material.simple')
162-
self.assertEqual(self.material2.name, 'Material2.simple')
161+
self.assertIn('Material.simple', self.materials.keys())
162+
self.assertIn('Material2.simple', self.materials.keys())
163163

164164
def testMtlShininess(self):
165165
"""Parsing an obj file with known material shininess should set it."""
@@ -180,11 +180,22 @@ def testMtlSpecular(self):
180180
# also tests d
181181
self.assertEqual(self.material1.specular, [0.2, 0.2, 0.2, 1.])
182182

183-
def testMtlTextureName(self):
184-
"""Parsing an obj file with known material texture should set its name."""
185-
# also tests d
186-
self.assertEqual(self.material1.texture.path,
187-
prepend_dir('4x4.png'))
183+
def testMtlTransparency(self):
184+
self.assertEqual(self.material1.transparency, 1.0)
185+
186+
def testMtlIllum(self):
187+
self.assertEqual(self.material1.illumination_model, 2)
188+
189+
def testMtlNi(self):
190+
self.assertEqual(self.material1.optical_density, 0.75)
191+
192+
def testTextures(self):
193+
self.assertEqual(self.material1.texture.path, prepend_dir('kd.png'))
194+
self.assertEqual(self.material1.texture_ambient.path, prepend_dir('ka.png'))
195+
self.assertEqual(self.material1.texture_specular_color.path, prepend_dir('ks.png'))
196+
self.assertEqual(self.material1.texture_specular_highlight.path, prepend_dir('ns.png'))
197+
self.assertEqual(self.material1.texture_alpha.path, prepend_dir('d.png'))
198+
self.assertEqual(self.material1.texture_bump.path, prepend_dir('bump.png'))
188199

189200

190201
class TestParserFailure(unittest.TestCase):

0 commit comments

Comments
 (0)