obj export improvements

This commit is contained in:
Guille Gutierrez 2023-09-28 15:20:28 +02:00
parent f495c1d27a
commit 207058a16f
2 changed files with 53 additions and 23 deletions

View File

@ -4,9 +4,11 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
import math
from pathlib import Path from pathlib import Path
import numpy as np
class Obj: class Obj:
""" """
@ -17,41 +19,80 @@ class Obj:
self._path = path self._path = path
self._export() self._export()
def _to_vertex(self, coordinate): def _ground(self, coordinate):
x = coordinate[0] - self._city.lower_corner[0] x = coordinate[0] - self._city.lower_corner[0]
y = coordinate[1] - self._city.lower_corner[1] y = coordinate[1] - self._city.lower_corner[1]
z = coordinate[2] - self._city.lower_corner[2] z = coordinate[2] - self._city.lower_corner[2]
return f'v {x} {y} {z}\n' return x, y, z
def _to_vertex(self, coordinate):
x, y, z = self._ground(coordinate)
return f'v {x} {z} {y}\n'
def _to_texture_vertex(self, coordinate):
u, v, _ = self._ground(coordinate)
return f'vt {u} {v}\n'
def _to_normal_vertex(self, coordinates):
ground_vertex = []
for coordinate in coordinates:
x, y, z = self._ground(coordinate)
ground_vertex.append(np.array([x, y, z]))
# recalculate the normal to get grounded values
edge_1 = ground_vertex[1] - ground_vertex[0]
edge_2 = ground_vertex[2] - ground_vertex[0]
normal = np.cross(edge_1, edge_2)
normal = normal / np.linalg.norm(normal)
return f'vn {normal[0]} {normal[1]} {normal[2]}\n'
def _export(self): def _export(self):
if self._city.name is None: if self._city.name is None:
self._city.name = 'unknown_city' self._city.name = 'unknown_city'
file_name = self._city.name + '.obj' obj_name = f'{self._city.name}.obj'
file_path = (Path(self._path).resolve() / file_name).resolve() mtl_name = f'{self._city.name}.mtl'
obj_file_path = (Path(self._path).resolve() / obj_name).resolve()
mtl_file_path = (Path(self._path).resolve() / mtl_name).resolve()
with open(mtl_file_path, 'w', encoding='utf-8') as mtl:
mtl.write("newmtl cerc_base_material\n")
mtl.write("Ka 1.0 1.0 1.0 # Ambient color (white)\n")
mtl.write("Kd 0.3 0.8 0.3 # Diffuse color (greenish)\n")
mtl.write("Ks 1.0 1.0 1.0 # Specular color (white)\n")
mtl.write("Ns 400.0 # Specular exponent (defines shininess)\n")
vertices = {} vertices = {}
with open(file_path, 'w', encoding='utf-8') as obj: normals_index = {}
faces = []
vertex_index = 0
normal_index = 0
with open(obj_file_path, 'w', encoding='utf-8') as obj:
obj.write("# cerc-hub export\n") obj.write("# cerc-hub export\n")
vertex_index = 0 obj.write(f'mtllib {mtl_name}')
faces = []
for building in self._city.buildings: for building in self._city.buildings:
obj.write(f'# building {building.name}\n') obj.write(f'# building {building.name}\n')
obj.write(f'g {building.name}\n') obj.write(f'g {building.name}\n')
obj.write('s off\n') obj.write('s off\n')
for surface in building.surfaces: for surface in building.surfaces:
obj.write(f'# surface {surface.name}\n') obj.write(f'# surface {surface.name}\n')
face = 'f ' face = []
normal = self._to_normal_vertex(surface.perimeter_polygon.coordinates)
normal_index += 1
textures = []
for coordinate in surface.perimeter_polygon.coordinates: for coordinate in surface.perimeter_polygon.coordinates:
vertex = self._to_vertex(coordinate) vertex = self._to_vertex(coordinate)
if vertex not in vertices: if vertex not in vertices:
vertex_index += 1 vertex_index += 1
vertices[vertex] = vertex_index vertices[vertex] = vertex_index
current = vertex_index current = vertex_index
obj.write(vertex) obj.write(vertex)
textures.append(self._to_texture_vertex(coordinate)) # only append if non-existing
else: else:
current = vertices[vertex] current = vertices[vertex]
face = f'{face} {current}' face.insert(0, f'{current}/{current}/{normal_index}') # insert counterclockwise
obj.writelines(normal) # add the normal
obj.writelines(textures) # add the texture vertex
faces.append(f'{face} {face.split(" ")[1]}\n') faces.append(f"f {' '.join(face)}\n")
obj.writelines(faces) obj.writelines(faces)
faces = [] faces = []

View File

@ -66,12 +66,7 @@ class TestExports(TestCase):
def _export(self, export_type, from_pickle=False): def _export(self, export_type, from_pickle=False):
self._complete_city = self._get_complete_city(from_pickle) self._complete_city = self._get_complete_city(from_pickle)
try: ExportsFactory(export_type, self._complete_city, self._output_path).export()
ExportsFactory(export_type, self._complete_city, self._output_path).export()
except ValueError as err:
if export_type != 'stl':
logging.warning('No backend export for STL test, skipped')
raise err
def _export_building_energy(self, export_type, from_pickle=False): def _export_building_energy(self, export_type, from_pickle=False):
self._complete_city = self._get_complete_city(from_pickle) self._complete_city = self._get_complete_city(from_pickle)
@ -83,12 +78,6 @@ class TestExports(TestCase):
""" """
self._export('obj', False) self._export('obj', False)
def test_stl_export(self):
"""
export to stl
"""
self._export('stl', False)
def test_energy_ade_export(self): def test_energy_ade_export(self):
""" """
export to energy ADE export to energy ADE