Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions pywavefront/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
# See: http://paulbourke.net/dataformats/mtl/

import pywavefront
from itertools import chain
from pathlib import Path, PureWindowsPath
import re

Expand Down Expand Up @@ -145,26 +146,33 @@ def parse_o(self):
"""The -o option offsets the position of the texture map on the surface by
shifting the position of the map origin.
"""
u = float(next(self._gen))
v = float(next(self._gen))
w = float(next(self._gen))
self._options.o = u, v, w
self._options.o = self._parse_vector(0.0)

def parse_s(self):
"""The -s option scales the size of the texture pattern on the textured
surface by expanding or shrinking the pattern
"""
u = float(next(self._gen))
v = float(next(self._gen))
w = float(next(self._gen))
self._options.s = u, v, w
self._options.s = self._parse_vector(1.0)

def parse_t(self):
"""The -t option turns on turbulence for textures."""
u = float(next(self._gen))
v = float(next(self._gen))
w = float(next(self._gen))
self._options.t = u, v, w
self._options.t = self._parse_vector(0.0)

def _parse_vector(self, default):
"""Read required u and optional v/w, preserving the following token."""
values = [float(next(self._gen))]
for _ in range(2):
try:
item = next(self._gen)
except StopIteration:
break
try:
values.append(float(item))
except ValueError:
self._gen = chain([item], self._gen)
break

return tuple(values) + (default,) * (3 - len(values))

def parse_texres(self):
"""The -texres option specifies the resolution of texture created when an
Expand Down
58 changes: 58 additions & 0 deletions tests/test_texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import os
from pathlib import Path

import pytest

import pywavefront
import pywavefront.texture
import utils

Expand Down Expand Up @@ -74,3 +77,58 @@ def test_options(self):
self.assertEqual(opts.s, (1.0, 1.0, 1.0))
self.assertEqual(opts.t, (0.0, 0.0, 0.0))
self.assertEqual(opts.texres, '1024')


@pytest.mark.parametrize('option,default', [('o', 0.0), ('s', 1.0), ('t', 0.0)])
@pytest.mark.parametrize('values', ['-2.5', '-2.5 3e-2', '-2.5 3e-2 4'])
@pytest.mark.parametrize('following', ['', ' -clamp on'])
def test_optional_vector_components(option, default, values, following):
name = 'textures/2 diffuse.png'
texture = pywavefront.texture.Texture(
'-{} {}{} {}'.format(option, values, following, name), '')
provided = tuple(float(value) for value in values.split())
expected = provided + (default,) * (3 - len(provided))
assert getattr(texture.options, option) == expected
assert texture.name == name
assert texture.options.clamp == ('on' if following else 'off')


@pytest.mark.parametrize('option', ['o', 's', 't'])
def test_vector_requires_numeric_first_component(option):
with pytest.raises(ValueError):
pywavefront.texture.Texture('-{} invalid texture.png'.format(option), '')


def test_repeated_vector_restores_defaults():
texture = pywavefront.texture.Texture(
'-s 2 3 4 -o 1 2 3 -s 5 -o 6 -t 7 8 texture.png', '')
assert texture.options.s == (5.0, 1.0, 1.0)
assert texture.options.o == (6.0, 0.0, 0.0)
assert texture.options.t == (7.0, 8.0, 0.0)
assert texture.name == 'texture.png'


def test_load_material_with_optional_vector_components(tmp_path):
(tmp_path / 'model.mtl').write_text(
'newmtl surface\n'
'map_Kd -s 2 -clamp on diffuse.png\n'
'map_Ka -o -0.5 0.25 ambient.png\n'
'bump -t 0.1 -bm 0.5 bump.png\n')
model = tmp_path / 'model.obj'
model.write_text(
'mtllib model.mtl\n'
'v 0 0 0\nv 1 0 0\nv 0 1 0\n'
'usemtl surface\nf 1 2 3\n')
scene = pywavefront.Wavefront(model, collect_faces=True)
material = scene.materials['surface']

assert scene.mesh_list[0].faces == [[0, 1, 2]]
assert material.vertices == [0, 0, 0, 1, 0, 0, 0, 1, 0]
assert material.texture.name == 'diffuse.png'
assert material.texture.options.s == (2.0, 1.0, 1.0)
assert material.texture.options.clamp == 'on'
assert material.texture_ambient.name == 'ambient.png'
assert material.texture_ambient.options.o == (-0.5, 0.25, 0.0)
assert material.texture_bump.name == 'bump.png'
assert material.texture_bump.options.t == (0.1, 0.0, 0.0)
assert material.texture_bump.options.bm == 0.5