Files
IfcOpenShell/src/ifcblenderexport/gbxml.py
T

173 lines
7.9 KiB
Python

import bpy
import uuid
import math
import sys
#sys.path.append('C:\Program Files\Python37\Lib\site-packages')
import lxml
import bspy
from bspy import Gbxml
class GbxmlExporter():
def __init__(self):
self.gbxml = Gbxml()
self.campus = None
def export(self):
print('# Start export')
self.campus = self.gbxml.add_element(self.gbxml.root(), 'Campus')
self.campus.set('id', 'campus-1')
name = self.gbxml.add_element(self.campus, 'Name', 'My project')
location = self.gbxml.add_element(self.campus, 'Location')
self.gbxml.add_element(location, 'ZipcodeOrPostalCode', 'G20 0SP')
self.gbxml.add_element(location, 'Name', 'London/Heathrow')
self.gbxml.add_element(location, 'Latitude', '51.480000')
self.gbxml.add_element(location, 'Longitude', '-0.450000')
self.gbxml.add_element(location, 'Elevation', '24.000000')
building = self.gbxml.add_element(self.campus, 'Building')
building.set('id', str(uuid.uuid4()))
building.set('buildingType', 'Office')
for object in bpy.context.selected_objects:
self.create_space(object, building)
# hardcoded test
construction = self.gbxml.add_element(self.gbxml.root(), 'Construction')
construction.set('id', 'defaultconstruction')
self.gbxml.add_element(construction, 'Name', 'test construction name')
u_value = self.gbxml.add_element(construction, 'U-value', '0.42')
u_value.set('unit', 'WPerSquareMeterK')
layer = self.gbxml.add_element(construction, 'LayerId')
layer.set('layerIdRef', 'defaultlayer')
layer = self.gbxml.add_element(self.gbxml.root(), 'Layer')
layer.set('id', 'defaultlayer')
material = self.gbxml.add_element(layer, 'MaterialId')
material.set('materialIdRef', 'defaultmaterial')
material = self.gbxml.add_element(self.gbxml.root(), 'Material')
material.set('id', 'defaultmaterial')
thickness = self.gbxml.add_element(material, 'Thickness', '0.2')
thickness.set('unit', 'Meters')
self.gbxml.add_element(material, 'Name', 'test material name')
r_value = self.gbxml.add_element(material, 'R-value', '0.13')
r_value.set('unit', 'SquareMeterKPerW')
self.append_template('C:/cygwin64/home/moud308/Projects/New Folder/presets/light-schedule.xml')
self.append_template('C:/cygwin64/home/moud308/Projects/New Folder/presets/window-types.xml')
with open('C:/cygwin64/home/moud308/Projects/New Folder/out.xml', 'w') as out:
out.write(self.gbxml.xmlstring())
print('# Validation results: {}'.format(self.gbxml.validate()))
print('# Finish export')
def append_template(self, file):
parser = lxml.etree.XMLParser(remove_blank_text=True)
template = lxml.etree.parse(file, parser).findall('.')[0]
for child in template.getchildren():
self.gbxml.root().append(child)
def create_space(self, object, building):
space = self.gbxml.add_element(building, 'Space')
space.set('id', object.name)
space.set('lightScheduleIdRef', 'aim0130') # hardcoded
light_power_per_area = self.gbxml.add_element(space, 'LightPowerPerArea', '5') # hardcoded test
light_power_per_area.set('unit', 'WattPerSquareMeter')
calculated_area = 0
shell_geometry = self.gbxml.add_element(space, 'ShellGeometry')
shell_geometry.set('id', 'shellid')
closed_shell = self.gbxml.add_element(shell_geometry, 'ClosedShell')
vertices_in_vg = self.get_vertices_in_vg(object, 0)
for polygon in object.data.polygons:
# First vg is reserved for surfaces
if not self.is_polygon_in_vg(polygon, vertices_in_vg):
continue
calculated_area += polygon.area
self.create_poly_loop(object, polygon, closed_shell)
self.create_space_boundary(object, polygon, space)
self.create_surface(object, polygon)
self.gbxml.add_element(space, 'Area', str(calculated_area))
self.gbxml.add_element(space, 'Volume', str(self.get_volume(object)))
def get_vertices_in_vg(self, object, vg_index):
return [ v.index for v in object.data.vertices if vg_index in [ g.group for g in v.groups ] ]
# Can move into a common Blender helper class?
def is_polygon_in_vg(self, polygon, vertices_in_vg):
for v in polygon.vertices:
if v not in vertices_in_vg:
return False
return True
def create_space_boundary(self, object, polygon, parent):
space_boundary = self.gbxml.add_element(parent, 'SpaceBoundary')
space_boundary.set('isSecondLevelBoundary', 'true')
space_boundary.set('surfaceIdRef', 'surface-{}-{}'.format(object.name, polygon.index))
planar_geometry = self.gbxml.add_element(space_boundary, 'PlanarGeometry')
self.create_poly_loop(object, polygon, planar_geometry)
def create_surface(self, object, polygon):
surface = self.gbxml.add_element(self.campus, 'Surface')
surface.set('id', 'surface-{}-{}'.format(object.name, polygon.index))
surface.set('surfaceType', 'ExteriorWall')
surface.set('constructionIdRef', 'defaultconstruction')
adjacent_space_id = self.gbxml.add_element(surface, 'AdjacentSpaceId')
adjacent_space_id.set('spaceIdRef', object.name)
rectangular_geometry = self.gbxml.add_element(surface, 'RectangularGeometry')
self.gbxml.add_element(
rectangular_geometry, 'Azimuth',
str(math.degrees(math.atan2(polygon.normal[0], polygon.normal[1]))))
self.gbxml.add_element(
rectangular_geometry, 'Tilt',
str(math.degrees(math.atan2(polygon.normal[2], polygon.normal[1])) - 90))
planar_geometry = self.gbxml.add_element(surface, 'PlanarGeometry')
self.create_poly_loop(object, polygon, planar_geometry)
for vg in object.vertex_groups:
if '/'.join(vg.name.split('/')[0:2]) == 'openings/{}'.format(polygon.index):
vertices_in_vg = self.get_vertices_in_vg(object, vg.index)
for p in object.data.polygons:
if self.is_polygon_in_vg(p, vertices_in_vg):
self.create_opening(object, p, surface)
def create_opening(self, object, polygon, parent):
opening = self.gbxml.add_element(parent, 'Opening')
opening.set('id', 'opening-{}-{}'.format(object.name, polygon.index))
opening.set('windowTypeIdRef', 'STD_EX11') # harcoded
opening.set('openingType', 'FixedWindow') # hardcoded
planar_geometry = self.gbxml.add_element(opening, 'PlanarGeometry')
self.create_poly_loop(object, polygon, planar_geometry)
def create_poly_loop(self, object, polygon, parent):
poly_loop = self.gbxml.add_element(parent, 'PolyLoop')
for vertice in polygon.vertices:
cartesian_point = self.gbxml.add_element(poly_loop, 'CartesianPoint')
for coord in [0, 1, 2]:
coordinate = self.gbxml.add_element(cartesian_point, 'Coordinate')
coordinate.text = str(object.data.vertices[vertice].co[coord])
def get_volume(self, o):
volume = 0
ob_mat = o.matrix_world
me = o.data
me.calc_loop_triangles()
for tf in me.loop_triangles:
tfv = tf.vertices
if len(tf.vertices) == 3:
tf_tris = (me.vertices[tfv[0]], me.vertices[tfv[1]], me.vertices[tfv[2]]),
else:
tf_tris = (me.vertices[tfv[0]], me.vertices[tfv[1]], me.vertices[tfv[2]]),\
(me.vertices[tfv[2]], me.vertices[tfv[3]], me.vertices[tfv[0]])
for tf_iter in tf_tris:
v1 = ob_mat @ tf_iter[0].co
v2 = ob_mat @ tf_iter[1].co
v3 = ob_mat @ tf_iter[2].co
volume += v1.dot(v2.cross(v3)) / 6.0
return volume
gbxml_exporter = GbxmlExporter()
gbxml_exporter.export()