Skip to content

Commit 8c06d2a

Browse files
committed
Add renderer, material module, dataset generation, configuration file
1 parent b7207c8 commit 8c06d2a

File tree

5 files changed

+441
-0
lines changed

5 files changed

+441
-0
lines changed

dataset/dataset.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import bpy, bmesh
2+
from datetime import datetime
3+
from math import radians
4+
import numpy as np
5+
import os
6+
import random
7+
import sys
8+
9+
file_dir = os.path.dirname(__file__)
10+
sys.path.append(file_dir)
11+
12+
from annotation import Annotation
13+
from blender_utils import extrude, gancio, get_min_max
14+
from dataset_config import *
15+
from generator import BuildingFactory
16+
from material import MaterialFactory
17+
from module import *
18+
from shp2obj import Collection, deselect_all
19+
20+
21+
class Dataset:
22+
def __init__(self):
23+
self.name = 'Building_dataset_{}_{}_{}'.format(datetime.now().year,
24+
datetime.now().month,
25+
datetime.now().day)
26+
self.size = SIZE
27+
self.json = Annotation()
28+
self.factory = BuildingFactory()
29+
self.material_factory = MaterialFactory()
30+
31+
def populate(self):
32+
for i in range(self.size):
33+
building = self.factory.produce()
34+
building.make()
35+
if use_materials:
36+
_monomaterial = np.random.random() < MATERIAL_PROB
37+
mat = self.material_factory.produce()
38+
print(mat.name)
39+
for v in building.volumes:
40+
if not _monomaterial:
41+
mat = self.material_factory.produce()
42+
v.apply(mat)
43+
self.json.add(building, '{}.png'.format(i), '{}.obj'.format(i))
44+
building.save(filename=str(i))
45+
building.demolish()
46+
47+
48+
def write(self):
49+
self.json.write(self.name + '.json')
50+
51+
52+
if __name__ == '__main__':
53+
d = Dataset()
54+
d.populate()
55+
d.write()
56+
57+

dataset/dataset_config.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
MIN_HEIGHT = 3.0
2+
MIN_WIDTH = 6.0
3+
MIN_LENGTH = 6.0
4+
5+
MAX_HEIGHT = 30.0
6+
MAX_WIDTH = 30.0
7+
MAX_LENGTH = 30.0
8+
9+
MAX_VOLUMES = 4
10+
11+
# BUILDINGS = ['Patio', 'L', 'C', 'Single', 'Skyscraper', 'Closedpatio', 'Equalpatio']
12+
# Choose building typologies to be produced
13+
BUILDINGS = ['Patio', 'L', 'C', 'Single', 'Skyscraper', 'Closedpatio', 'Equalpatio']
14+
15+
SIZE = 2 # dataset size
16+
17+
use_materials = True # apply materials to the facades of the buildings, bool
18+
19+
MATERIAL_PROB = 0.7 # Probability of all the volumes of one building to be of the same material
20+
21+
use_modules = True
22+
MODULES = ['window']
23+
24+
MODEL_SAVE = 'Models'
25+
IMG_SAVE = 'Images'
26+
MASK_SAVE = 'Masks'
27+
28+
ENGINE = 'CYCLES'

dataset/material.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import bpy
2+
import numpy as np
3+
import os
4+
import sys
5+
6+
file_dir = os.path.dirname(__file__)
7+
sys.path.append(file_dir)
8+
file_dir = file_dir.replace('\\', '/').replace('\r', '/r').replace('\n', '/n').\
9+
replace('\t', '/t')
10+
11+
12+
class Material:
13+
"""
14+
Class that represents a material object in Blender.
15+
"""
16+
def __init__(self, name, filename='material'):
17+
self.name = name.lower().capitalize() # name of the material and its texture folder
18+
self.filename = filename
19+
self._path = file_dir + '/Textures/{}.blend'.format(self.filename)
20+
self._add = '\\Material\\'
21+
self.value = self._load() # loads material into the scene
22+
self._update_nodes() # loads the textures to the material
23+
24+
def _load(self):
25+
try:
26+
return bpy.data.materials[self.name].copy()
27+
except KeyError:
28+
try:
29+
# print(file_dir)
30+
bpy.ops.wm.append(
31+
filepath=self._path + self._add + self.filename,
32+
filename='{}'.format(self.filename),
33+
directory=self._path + self._add)
34+
bpy.data.materials[-1].name = self.name
35+
return bpy.data.materials[self.name]
36+
except Exception as e:
37+
print(repr(e))
38+
print('Could not import {} from {}'.format(self.name, self._path))
39+
raise KeyboardInterrupt()
40+
41+
def _load_maps(self, map_type):
42+
"""
43+
Function that uploads texture map into the Material tree nodes. Textures
44+
are taken from folder Texture/Material where Material corresponds to the
45+
name of the material.
46+
:param map_type: type of the map to upload, str, one of 'Diffuse', 'Normal',
47+
'Roughness', 'Displacement'
48+
:return: texture map, bpy image object
49+
"""
50+
assert map_type in ['Diffuse', 'Normal', 'Roughness', 'Displacement'], \
51+
"Unknown map type, expected one of: 'Diffuse', 'Normal'," \
52+
"'Roughness', 'Displacement'"
53+
try:
54+
bpy.ops.image.open(filepath=file_dir + '/Textures/{}/{}.png'.
55+
format(self.name, map_type.capitalize()))
56+
return bpy.data.images[-2]
57+
except Exception as e:
58+
print('Failed to load {} texture of {}'.format(map_type, self.name))
59+
print(repr(e))
60+
61+
def _update_nodes(self):
62+
"""
63+
Function that updates the nodes of the material tree with the material
64+
textures.
65+
:return:
66+
"""
67+
self.value.node_tree.nodes['Diffuse_texture'].image = self._load_maps('Diffuse')
68+
self.value.node_tree.nodes['Normal_texture'].image = self._load_maps('Normal')
69+
self.value.node_tree.nodes['Displacement_texture'].image = self._load_maps('Displacement')
70+
71+
72+
class MaskMaterial(Material):
73+
def __init__(self, name='mask', filename='mask', color=(1.0, 1.0, 1.0)):
74+
self.filename = filename
75+
self.color = color
76+
Material.__init__(self, name, filename=self.filename)
77+
78+
def _update_nodes(self):
79+
self.value.node_tree.nodes['RGB'].outputs[0].default_value[0] = self.color[0]
80+
self.value.node_tree.nodes['RGB'].outputs[0].default_value[1] = self.color[1]
81+
self.value.node_tree.nodes['RGB'].outputs[0].default_value[2] = self.color[2]
82+
83+
84+
class MaterialFactory:
85+
"""
86+
Class that produces materials based on the given name.
87+
"""
88+
def __init__(self):
89+
self.materials = os.listdir('{}/Textures'.format(file_dir))
90+
self.materials = [x for x in self.materials if os.path.isdir('{}/Textures/{}'.format(file_dir, x))]
91+
92+
def produce(self, name=None, color=None):
93+
if name:
94+
if name == 'mask':
95+
if not color:
96+
color = [1.0, 0.0, 0.0]
97+
return MaskMaterial(name, color)
98+
else:
99+
name = name.lower().capitalize()
100+
assert name in self.materials, "Unknown material {}, not in Textures folder".format(name)
101+
return Material(name)
102+
else:
103+
return Material(np.random.choice(self.materials))
104+
105+
106+
if __name__ == '__main__':
107+
108+
from generator import *
109+
110+
material = MaterialFactory().produce('CONCrete')
111+
f = CollectionFactory()
112+
collection = f.produce(number=1)
113+
building = ComposedBuilding(collection.collection)
114+
building.make()
115+
building.volumes[0].apply(material)

dataset/renderer.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import bpy, bmesh
2+
from math import radians
3+
import numpy as np
4+
import os
5+
import random
6+
import sys
7+
8+
file_dir = os.path.dirname(__file__)
9+
sys.path.append(file_dir)
10+
11+
from dataset_config import ENGINE
12+
13+
14+
class Renderer:
15+
"""
16+
Class that manages the scene rendering. Incomplete.
17+
"""
18+
def __init__(self):
19+
self.engine = ENGINE
20+
self._scene_name = bpy.data.scenes[-1].name
21+
22+
def render(self):
23+
self._render()
24+
25+
def _render(self, filename='test'):
26+
"""
27+
Function that renders the scene.
28+
:return:
29+
"""
30+
bpy.data.scenes[-1].render.engine = self.engine
31+
bpy.ops.render.render()
32+
if not IMG_SAVE in os.listdir(file_dir):
33+
os.mkdir(file_dir + '/' + IMG_SAVE)
34+
bpy.data.images["Render Result"].save_render(
35+
'{}/{}.png'.format(IMG_SAVE, filename))
36+
37+
def _render_mask(self):
38+
"""
39+
Function that renders the scene as a one-channel mask.
40+
:return:
41+
"""
42+
# update materials
43+
bpy.data.scenes["Scene"].render.engine = 'BLENDER_RENDER'
44+
bpy.ops.render.render()
45+
if not MASK_SAVE in os.listdir(file_dir):
46+
os.mkdir(file_dir + '/' + MASK_SAVE)
47+
bpy.data.images["Render Result"].save_render(
48+
'{}/{}.png'.format(MASK_SAVE, filename))
49+
raise NotImplementedError
50+
51+
def _render_keypoints(self):
52+
"""
53+
Function that renders the scene as a one-channel mask of predefined
54+
keypoints.
55+
:return:
56+
"""
57+
raise NotImplementedError
58+
59+
60+
if __name__ == '__main__':
61+
62+
from generator import *
63+
from material import MaterialFactory
64+
from volume import CollectionFactory
65+
66+
f = CollectionFactory()
67+
collection = f.produce(number=1)
68+
building = ComposedBuilding(collection.collection)
69+
building.make()
70+
for v in building.volumes:
71+
v.apply(MaterialFactory().produce())
72+
r = Renderer()
73+
r.render()
74+
75+

0 commit comments

Comments
 (0)