wavefront obj export

This commit is contained in:
Laura Klünder 2017-11-14 20:53:04 +01:00
parent afbb9e7427
commit b76f35a4a5
8 changed files with 80 additions and 7 deletions

View file

@ -67,10 +67,19 @@ class Command(BaseCommand):
renderer = MapRenderer(level.pk, minx, miny, maxx, maxy, access_permissions=options['permissions'],
full_levels=options['full_levels'])
stl = renderer.render(get_engine(options['filetype']), center=not options['no_center'])
data = stl.render()
filename = os.path.join(settings.RENDER_ROOT,
'level_%s_%s.%s' % (level.short_label,
renderer.access_cache_key.replace('_', '-'),
options['filetype']))
render = renderer.render(get_engine(options['filetype']), center=not options['no_center'])
data = render.render(filename)
if isinstance(data, tuple):
other_data = data[1:]
data = data[0]
else:
other_data = ()
open(filename, 'wb').write(data)
for filename, data in other_data:
open(filename, 'wb').write(data)

View file

@ -3,6 +3,7 @@ from django.core import checks
from c3nav.mapdata.render.engines.base import register_engine, get_engine, get_engine_filetypes # noqa
from c3nav.mapdata.render.engines.openscad import OpenSCADEngine # noqa
from c3nav.mapdata.render.engines.wavefront import WavefrontEngine # noqa
from c3nav.mapdata.render.engines.stl import STLEngine # noqa
from c3nav.mapdata.render.engines.svg import SVGEngine # noqa

View file

@ -51,7 +51,7 @@ class RenderEngine(ABC):
self.background_rgb = tuple(int(background[i:i + 2], 16)/255 for i in range(1, 6, 2))
@abstractmethod
def render(self) -> bytes:
def render(self, filename=None) -> bytes:
# render the image to png.
pass

View file

@ -196,7 +196,7 @@ class OpenGLEngine(Base3DEngine):
worker = OpenGLWorker()
def render(self) -> bytes:
def render(self, filename=None) -> bytes:
return self.worker.render(self.width, self.height, self.gl_scale, self.gl_offset, self.background_rgb,
np.vstack(self.vertices).astype(np.float32).tobytes() if self.vertices else b'')

View file

@ -28,7 +28,7 @@ class OpenSCADEngine(Base3DEngine):
b' convexity = 10\n' +
b' );')
def render(self) -> bytes:
def render(self, filename=None) -> bytes:
result = (b'c3nav_export();\n\n' +
b'module c3nav_export() {\n' +
b'\n'.join((b' %s();' % group.replace('-', 'minus').encode())

View file

@ -21,7 +21,7 @@ class STLEngine(Base3DEngine):
def _create_facet(self, facet) -> bytes:
return self.facet_template % tuple(facet.flatten())
def render(self) -> bytes:
def render(self, filename=None) -> bytes:
facets = np.vstack(chain(*(chain(*v.values()) for v in self.vertices.values())))
facets = np.hstack((np.cross(facets[:, 1]-facets[:, 0], facets[:, 2]-facets[:, 1]).reshape((-1, 1, 3)),
facets))

View file

@ -83,7 +83,7 @@ class SVGEngine(RenderEngine):
result += '</svg>'
return result
def render(self):
def render(self, filename=None):
# render the image to png. returns bytes if f is None, otherwise it calls f.write()
if self.width == 256 and self.height == 256 and not self.g:

View file

@ -0,0 +1,63 @@
import os
from itertools import chain
import numpy as np
from c3nav.mapdata.render.engines import register_engine
from c3nav.mapdata.render.engines.base3d import Base3DEngine
@register_engine
class WavefrontEngine(Base3DEngine):
filetype = 'obj'
def _normal_normal(self, normal):
return normal / (np.absolute(normal).max())
def render(self, filename=None):
facets = np.vstack(chain(*(chain(*v.values()) for v in self.vertices.values())))
vertices = tuple(set(tuple(vertex) for vertex in facets.reshape((-1, 3))))
vertices_lookup = {vertex: i for i, vertex in enumerate(vertices, start=1)}
normals = np.cross(facets[:, 1] - facets[:, 0], facets[:, 2] - facets[:, 1]).reshape((-1, 3))
normals = normals / np.amax(np.absolute(normals), axis=1).reshape((-1, 1))
normals = tuple(set(tuple(normal) for normal in normals))
normals_lookup = {normal: i for i, normal in enumerate(normals, start=1)}
materials = b''
materials_filename = filename + '.mtl'
for name, color in self.colors.items():
materials += ((b'newmtl %s\n' % name.encode()) +
(b'Ka %.2f %.2f %.2f\n' % color[:3]) +
(b'Kd %.2f %.2f %.2f\n' % color[:3]) +
b'Ks 0.00 0.00 0.00\n' +
(b'd %.2f\n' % color[3]) +
b'illum 2\n')
result = b'mtllib %s\n' % os.path.split(materials_filename)[-1].encode()
result += b'o c3navExport\n'
result += b''.join((b'v %.3f %.3f %.3f\n' % vertex) for vertex in vertices)
result += b''.join((b'vn %.6f %.6f %.6f\n' % normal) for normal in normals)
for group, subgroups in self.groups.items():
result += b'\n# ' + group.encode() + b'\n'
for subgroup in subgroups:
result += b'\n# ' + subgroup.encode() + b'\n'
for i, vertices in enumerate(self.vertices[subgroup].values()):
if not vertices:
continue
for j, facets in enumerate(vertices):
if not facets.size:
continue
normals = np.cross(facets[:, 1] - facets[:, 0], facets[:, 2] - facets[:, 1]).reshape((-1, 3))
normals = normals / np.amax(np.absolute(normals), axis=1).reshape((-1, 1))
normals = tuple(normals_lookup[tuple(normal)] for normal in normals)
result += ((b'g %s_%d_%d\n' % (subgroup.encode(), i, j)) +
(b'usemtl %s\n' % subgroup.encode()) +
b's off\n' +
b''.join((b'f %d//%d %d//%d %d//%d\n' % (vertices_lookup[tuple(a)], normals[k],
vertices_lookup[tuple(b)], normals[k],
vertices_lookup[tuple(c)], normals[k],)
for k, (a, b, c) in enumerate(facets)))
)
return result, (materials_filename, materials)