wavefront obj export
This commit is contained in:
parent
afbb9e7427
commit
b76f35a4a5
8 changed files with 80 additions and 7 deletions
|
@ -67,10 +67,19 @@ class Command(BaseCommand):
|
||||||
renderer = MapRenderer(level.pk, minx, miny, maxx, maxy, access_permissions=options['permissions'],
|
renderer = MapRenderer(level.pk, minx, miny, maxx, maxy, access_permissions=options['permissions'],
|
||||||
full_levels=options['full_levels'])
|
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,
|
filename = os.path.join(settings.RENDER_ROOT,
|
||||||
'level_%s_%s.%s' % (level.short_label,
|
'level_%s_%s.%s' % (level.short_label,
|
||||||
renderer.access_cache_key.replace('_', '-'),
|
renderer.access_cache_key.replace('_', '-'),
|
||||||
options['filetype']))
|
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)
|
open(filename, 'wb').write(data)
|
||||||
|
for filename, data in other_data:
|
||||||
|
open(filename, 'wb').write(data)
|
||||||
|
|
|
@ -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.base import register_engine, get_engine, get_engine_filetypes # noqa
|
||||||
from c3nav.mapdata.render.engines.openscad import OpenSCADEngine # 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.stl import STLEngine # noqa
|
||||||
from c3nav.mapdata.render.engines.svg import SVGEngine # noqa
|
from c3nav.mapdata.render.engines.svg import SVGEngine # noqa
|
||||||
|
|
||||||
|
|
|
@ -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))
|
self.background_rgb = tuple(int(background[i:i + 2], 16)/255 for i in range(1, 6, 2))
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def render(self) -> bytes:
|
def render(self, filename=None) -> bytes:
|
||||||
# render the image to png.
|
# render the image to png.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,7 @@ class OpenGLEngine(Base3DEngine):
|
||||||
|
|
||||||
worker = OpenGLWorker()
|
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,
|
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'')
|
np.vstack(self.vertices).astype(np.float32).tobytes() if self.vertices else b'')
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class OpenSCADEngine(Base3DEngine):
|
||||||
b' convexity = 10\n' +
|
b' convexity = 10\n' +
|
||||||
b' );')
|
b' );')
|
||||||
|
|
||||||
def render(self) -> bytes:
|
def render(self, filename=None) -> bytes:
|
||||||
result = (b'c3nav_export();\n\n' +
|
result = (b'c3nav_export();\n\n' +
|
||||||
b'module c3nav_export() {\n' +
|
b'module c3nav_export() {\n' +
|
||||||
b'\n'.join((b' %s();' % group.replace('-', 'minus').encode())
|
b'\n'.join((b' %s();' % group.replace('-', 'minus').encode())
|
||||||
|
|
|
@ -21,7 +21,7 @@ class STLEngine(Base3DEngine):
|
||||||
def _create_facet(self, facet) -> bytes:
|
def _create_facet(self, facet) -> bytes:
|
||||||
return self.facet_template % tuple(facet.flatten())
|
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.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 = np.hstack((np.cross(facets[:, 1]-facets[:, 0], facets[:, 2]-facets[:, 1]).reshape((-1, 1, 3)),
|
||||||
facets))
|
facets))
|
||||||
|
|
|
@ -83,7 +83,7 @@ class SVGEngine(RenderEngine):
|
||||||
result += '</svg>'
|
result += '</svg>'
|
||||||
return result
|
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()
|
# 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:
|
if self.width == 256 and self.height == 256 and not self.g:
|
||||||
|
|
63
src/c3nav/mapdata/render/engines/wavefront.py
Normal file
63
src/c3nav/mapdata/render/engines/wavefront.py
Normal 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)
|
Loading…
Add table
Add a link
Reference in a new issue