Add more general shape description.

This commit is contained in:
jmsgrogan 2017-10-24 20:18:12 +01:00
parent cfb558e540
commit 422f458469
27 changed files with 234 additions and 106 deletions

View file

@ -1,33 +1,24 @@
import os import os
import bpy import bpy
import product_gen.generate_lamp_base import product_gen.generate_lamp_base
import product_gen.generate_shades import product_gen.generate_shades as gs
def generate_models(shape, height, radius): def generate_models(shape_parameters):
# Shade # Shade
radius2 = 0.3 if shape_parameters["shape"] == "mesh":
if shape == "cone": shade = gs.generate_mesh_shade(shape_parameters)
shade = product_gen.generate_shades.generate_pendant_shade(radius, elif shape_parameters["shape"] == "led":
radius2, shade = gs.generate_led_shade(shape_parameters)
height) else: # default pendant
elif shape == "mesh": shade = gs.generate_pendant_shade(shape_parameters)
shade = product_gen.generate_shades.generate_mesh_shade(radius,
radius2, height)
elif shape == "bio":
shade = product_gen.generate_shades.generate_bio_shade(radius,
radius2, height)
elif shape == "pendant":
shade = product_gen.generate_shades.generate_pendant_shade(radius,
radius2,
height)
# Base # Base
if "bio" not in shape: if "bio" not in shape_parameters["shape"]:
radius1 = 0.3 radius1 = 0.3
radius2 = 0.07 radius2 = 0.07
depth = 0.5 depth = 0.5
location = (0.0, 0.0, height/2.0+depth/2.0) location = (0.0, 0.0, depth/2.0)
base = product_gen.generate_lamp_base.generate_cone_base(radius1, base = product_gen.generate_lamp_base.generate_cone_base(radius1,
radius2, radius2,
depth, depth,

View file

@ -5,25 +5,21 @@ import product_gen.generate_lamp
import rendering.setup_scene import rendering.setup_scene
import rendering.setup_renderer import rendering.setup_renderer
def generate(shape, def generate(shape_parameters, output_prefix, is_final=False):
height,
radius,
color,
output_prefix,
is_final=False):
rendering.setup_renderer.setup_renderer() rendering.setup_renderer.setup_renderer(shape_parameters)
rendering.setup_scene.initialize_scene() rendering.setup_scene.initialize_scene(shape_parameters)
# Generate models # Generate models
models = product_gen.generate_lamp.generate_models(shape, height, radius) models = product_gen.generate_lamp.generate_models(shape_parameters)
# Optimize geometries for render # Optimize geometries for render
product_gen.generate_lamp.optimize_for_render(models) product_gen.generate_lamp.optimize_for_render(models)
# Set up materials and textures # Set up materials and textures
if "bio" not in shape: color = (0.1, 0.1, 0.1)
if "bio" not in shape_parameters["shape"]:
colormap = {"shade": color, colormap = {"shade": color,
"base": (132.0/255.0, 64.0/255.0, 11.0/255.0), "base": (132.0/255.0, 64.0/255.0, 11.0/255.0),
"chord": (5.0/255.0, 5.0/255.0, 5.0/255.0)} "chord": (5.0/255.0, 5.0/255.0, 5.0/255.0)}
@ -32,7 +28,7 @@ def generate(shape,
product_gen.generate_lamp.apply_textures(colormap, models) product_gen.generate_lamp.apply_textures(colormap, models)
rendering.setup_scene.setup_scene() rendering.setup_scene.setup_scene(shape_parameters)
# Do rendering # Do rendering
prefix = os.getcwd() + "/" + output_prefix + "/" + output_prefix prefix = os.getcwd() + "/" + output_prefix + "/" + output_prefix
@ -52,4 +48,4 @@ def generate(shape,
bpy.ops.wm.save_as_mainfile(filepath=prefix + ".blend") bpy.ops.wm.save_as_mainfile(filepath=prefix + ".blend")
return height, radius, color return shape_parameters

View file

@ -14,27 +14,103 @@ def UpOrDown(normal):
return True return True
return False return False
def generate_pendant_shade(radius1, radius2, depth): def generate_pendant_shade(shape_parameters):
bpy.ops.mesh.primitive_cylinder_add(radius=radius1, radius = shape_parameters["radius"]
depth = shape_parameters["height"]
print(depth)
bpy.ops.mesh.primitive_cylinder_add(radius=radius,
depth=depth) depth=depth)
cone = bpy.data.objects["Cylinder"] cone = bpy.data.objects["Cylinder"]
cone.name = "shade" cone.name = "shade"
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
for idx in range(3): num_subdivisions = 3
if len(shape_parameters["division_offsets"])>3:
num_subdivisions = 4
for idx in range(num_subdivisions):
bpy.ops.mesh.subdivide() bpy.ops.mesh.subdivide()
bm = bmesh.from_edit_mesh(cone.data) bm = bmesh.from_edit_mesh(cone.data)
for i in range( len( bm.verts ) ): num_verts = len(bm.verts)
bm.verts.ensure_lookup_table()
vert = bm.verts[i]
theta = math.atan2(vert.co.x, vert.co.y)
delta = abs(vert.co.z-depth/2.0)
mapped_rad = radius2 + delta**2 summed_offset = 0.0
for jdx in range(len(shape_parameters["division_offsets"])):
summed_offset += shape_parameters["division_offsets"][jdx]
print(summed_offset)
for idx in range(num_verts):
bm.verts.ensure_lookup_table()
vert = bm.verts[idx]
theta = math.atan2(vert.co.x, vert.co.y)
vert.co.z = vert.co.z -depth/2.0
delta = abs(vert.co.z)
frac = delta/depth
summed_offset = 0.0
prev_offset = 0.0
division_index = len(shape_parameters["division_offsets"])-1
for jdx in range(len(shape_parameters["division_offsets"])):
prev_offset = summed_offset
summed_offset += shape_parameters["division_offsets"][jdx]
if frac >=prev_offset and frac <= summed_offset:
division_index = jdx
if division_index==0:
print(vert.co.z, frac)
current_offset = shape_parameters["division_offsets"][division_index]
division_type = shape_parameters["division_patterns"][division_index]
if division_index==0:
print(vert.co.z, frac, division_index)
division_radius = shape_parameters["fixture_radius"]
previous_offset = 0.0
previous_radius = division_radius
else:
division_radius = shape_parameters["radius"]*shape_parameters["division_radii"][division_index]
previous_offset = shape_parameters["division_offsets"][division_index-1]
previous_radius = shape_parameters["radius"]*shape_parameters["division_radii"][division_index-1]
if previous_radius< shape_parameters["fixture_radius"]:
previous_radius = shape_parameters["fixture_radius"]
if division_radius<previous_radius:
division_radius = previous_radius
#print(division_radius, vert.co.z)
if division_type == "straight":
vert.co.x = division_radius*math.sin(theta)
vert.co.y = division_radius*math.cos(theta)
# elif division_type == "square":
# mapped_rad = division_radius + (delta/3.0)**2
# vert.co.x = mapped_rad*math.sin(theta)
# vert.co.y = mapped_rad*math.cos(theta)
# elif division_type == "inv_square":
# mapped_rad = division_radius + 1.0/(0.01+delta**2)
# vert.co.x = mapped_rad*math.sin(theta)
# vert.co.y = mapped_rad*math.cos(theta)
else:
# elif division_type == "sine":
mapped_rad = division_radius + math.sin(math.pi/2.0*frac)
vert.co.x = mapped_rad*math.sin(theta) vert.co.x = mapped_rad*math.sin(theta)
vert.co.y = mapped_rad*math.cos(theta) vert.co.y = mapped_rad*math.cos(theta)
# elif division_type == "inv_sine":
# mapped_rad = division_radius + 1.0/math.sin(delta)
# vert.co.x = mapped_rad*math.sin(theta)
# vert.co.y = mapped_rad*math.cos(theta)
# else:
# #elif division_type == "ramp":
# mapped_rad = division_radius + delta
# vert.co.x = mapped_rad*math.sin(theta)
# vert.co.y = mapped_rad*math.cos(theta)
# elif division_type == "inv_ramp":
# mapped_rad = division_radius + delta
# vert.co.x = mapped_rad*math.sin(theta)
# vert.co.y = mapped_rad*math.cos(theta)
for face in bm.faces: for face in bm.faces:
if UpOrDown(face.normal): if UpOrDown(face.normal):
@ -160,7 +236,7 @@ def make_square_ring(radius, depth, thickness):
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
return bpy.data.objects["square_ring"] return bpy.data.objects["square_ring"]
def generate_bio_shade(radius1, radius2, depth): def generate_led_shade(radius1, radius2, depth):
ring1 = make_square_ring(3.0*radius1, depth/10.0, 0.2) ring1 = make_square_ring(3.0*radius1, depth/10.0, 0.2)
bpy.ops.transform.rotate(value=-math.pi/12.0, axis=(1.0,0.0,0.0)) bpy.ops.transform.rotate(value=-math.pi/12.0, axis=(1.0,0.0,0.0))

View file

Binary file not shown.

View file

@ -1,22 +1,23 @@
import bpy import bpy
def setup_renderer(engine="CYCLES"): def setup_renderer(shape_parameters, engine="CYCLES"):
# Set up and do the render # Set up and do the render
this_scene = bpy.context.scene
if engine=="CYCLES": if engine=="CYCLES":
bpy.context.scene.render.engine = 'CYCLES' this_scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.device = 'GPU' this_scene.cycles.device = 'GPU'
bpy.context.scene.cycles.samples = 12.0 this_scene.cycles.samples = 12.0
bpy.context.scene.cycles.caustics_reflective = False this_scene.cycles.caustics_reflective = False
bpy.context.scene.cycles.caustics_refractive = False this_scene.cycles.caustics_refractive = False
bpy.context.scene.cycles.max_bounces = 0.0 this_scene.cycles.max_bounces = 0.0
bpy.data.scenes["Scene"].render.use_border = False # Tell Blender to use border render this_scene.render.use_border = False
bpy.data.scenes["Scene"].render.border_max_y = 0.75 # Set the border top at 3/4 of the image's height this_scene.render.border_max_y = 0.75
bpy.data.scenes["Scene"].render.border_min_y = 0.33 # Set the border bottom at 1/3 of the image's height this_scene.render.border_min_y = 0.33
bpy.data.scenes["Scene"].render.border_min_x = 0.25 # Set the border left at 1/4 of the image's width this_scene.render.border_min_x = 0.25
bpy.data.scenes["Scene"].render.border_max_x = 0.65 # Set the border right at the image's right border this_scene.render.border_max_x = 0.65
bpy.context.scene.render.resolution_x = 600.0 this_scene.render.resolution_x = 600.0
bpy.context.scene.render.resolution_y = 600.0 this_scene.render.resolution_y = 600.0
bpy.context.scene.render.tile_x = 32 this_scene.render.tile_x = 32
bpy.context.scene.render.tile_y = 32 this_scene.render.tile_y = 32
bpy.context.scene.render.resolution_percentage = 50.0 this_scene.render.resolution_percentage = 50.0

View file

@ -1,38 +1,38 @@
import math import math
import bpy import bpy
def initialize_scene(): def initialize_scene(shape_parameters):
# Set up scene # Set up scene
objs = bpy.data.objects objs = bpy.data.objects
bpy.data.scenes['Scene'].objects.unlink(objs["Cube"]) bpy.data.scenes['Scene'].objects.unlink(objs["Cube"])
objs.remove(objs["Cube"]) objs.remove(objs["Cube"])
def setup_scene(): def setup_scene(shape_parameters):
# Set up world # Set up world
bpy.context.scene.world.use_sky_paper = True bpy.context.scene.world.use_sky_paper = True
bpy.context.scene.world.horizon_color = (0.58, 0.58, 0.58) bpy.context.scene.world.horizon_color = (0.99, 0.8, 0.8)
#bpy.context.scene.world.light_settings.use_environment_light = True #bpy.context.scene.world.light_settings.use_environment_light = True
# Add back wall # Add back wall
# bpy.ops.mesh.primitive_plane_add(radius=20, location=(-10.0, 0.0, 0.0)) bpy.ops.mesh.primitive_plane_add(radius=20, location=(-10.0, 0.0, 0.0))
# bpy.ops.transform.rotate(value=math.pi/2.0, axis=(0.0,1.0,0.0)) bpy.ops.transform.rotate(value=math.pi/2.0, axis=(0.0,1.0,0.0))
# bpy.data.objects["Plane"].name = "back_wall" bpy.data.objects["Plane"].name = "back_wall"
# mat = bpy.data.materials.get("backwall-material") mat = bpy.data.materials.get("backwall-material")
# if mat is None: if mat is None:
# mat = bpy.data.materials.new("backwall-material") mat = bpy.data.materials.new("backwall-material")
# mat.diffuse_color = (0.25, 0.25, 0.25) mat.diffuse_color = (0.25, 0.25, 0.25)
# mat.specular_color = (0.25, 0.25, 0.25) mat.specular_color = (0.25, 0.25, 0.25)
# mat.diffuse_intensity = 1.0 mat.diffuse_intensity = 1.0
# mat.specular_intensity = 1.0 mat.specular_intensity = 1.0
# bpy.context.scene.objects.active = bpy.data.objects["back_wall"] bpy.context.scene.objects.active = bpy.data.objects["back_wall"]
# if bpy.context.active_object.data.materials: if bpy.context.active_object.data.materials:
# # assign to 1st material slot # assign to 1st material slot
# bpy.context.active_object.data.materials[0] = mat bpy.context.active_object.data.materials[0] = mat
# else: else:
# # no slots # no slots
# bpy.context.active_object.data.materials.append(mat) bpy.context.active_object.data.materials.append(mat)
# Set up cameras # Set up cameras
bpy.data.objects["Camera"].location = (10.0, 0.0, -1) bpy.data.objects["Camera"].location = (10.0, 0.0, -1)
@ -41,6 +41,7 @@ def setup_scene():
bpy.data.objects["Camera"].rotation_mode = 'XYZ' bpy.data.objects["Camera"].rotation_mode = 'XYZ'
bpy.data.objects["Camera"].rotation_euler[0] = math.pi/2.0 bpy.data.objects["Camera"].rotation_euler[0] = math.pi/2.0
bpy.data.objects["Camera"].rotation_euler[1] = 0.0 bpy.data.objects["Camera"].rotation_euler[1] = 0.0
# todo tilt down further for light render
bpy.data.objects["Camera"].rotation_euler[2] = math.pi/2.0 bpy.data.objects["Camera"].rotation_euler[2] = math.pi/2.0
# Lamps # Lamps

0
src/shapes/__init__.py Normal file
View file

Binary file not shown.

View file

@ -0,0 +1,70 @@
import random
import copy
_shape_description = {"shape": "pendant",
"division_patterns": [],
"division_offsets": [],
"division_radii": [],
"radius": 1.0,
"height": 1.0,
"fixture_radius": 0.3,
"style": "dark"}
_styles = {"dark" : {"shade": (0.1, 0.1, 0.1),
"chord": (0.05, 0.05, 0.05),
"base": (0.05, 0.05, 0.05),
"use_wall": True,
"wall": (0.9, 0.9, 0.9)},
"billard": {"shade": (0.1, 0.1, 0.1),
"chord": (0.05, 0.05, 0.05),
"base": (0.05, 0.05, 0.05),
"use_wall": True,
"wall": (0.9, 0.9, 0.9)},
"light": {"shade": (0.1, 0.1, 0.1),
"chord": (0.05, 0.05, 0.05),
"base": (0.05, 0.05, 0.05),
"use_wall": True,
"wall": (0.9, 0.9, 0.9)},
}
_division_types = ["straight",
"square",
"inv_square",
"sine",
"inv_sine",
"ramp",
"inv_ramp"]
def get_random_shape_description(shape, bbox, feature_min):
"""
Generate a random shape description and style
"""
shape_description = copy.deepcopy(_shape_description)
shape_description["shape"] = shape
max_divisions = 3
num_divisions = int(1 + random.random()*(max_divisions-1))
num_divisions = 4
remaining_offset = 1.0
feature_height = feature_min[1] + random.random()*(bbox[1]-feature_min[1])
feature_radius = feature_min[0] + random.random()*(bbox[0]-feature_min[0])
shape_description["height"] = feature_height
shape_description["radius"] = feature_radius
min_radius_fraction = feature_min[0]/feature_radius
for idx in range(num_divisions):
shape_description["division_patterns"].append(random.choice(_division_types))
offset = 1.0/num_divisions
#offset = random.random()*remaining_offset
remaining_offset -= offset
shape_description["division_offsets"].append(offset)
radius_fraction = min_radius_fraction+random.random()*(1.0-min_radius_fraction)
shape_description["division_radii"].append(radius_fraction)
shape_description["style"] = random.choice(list(_styles.keys()))
shape_description["division_radii"].sort()
return shape_description

View file

@ -13,7 +13,6 @@ if __name__ == "__main__":
shape_params = json.loads(argv[0]) shape_params = json.loads(argv[0])
shape = shape_params['shape'] shape = shape_params['shape']
output = shape_params['output'] output = shape_params['output']
print(output)
if not os.path.exists(os.getcwd() + "/" + output): if not os.path.exists(os.getcwd() + "/" + output):
os.makedirs(os.getcwd() + "/" + output) os.makedirs(os.getcwd() + "/" + output)

View file

@ -7,6 +7,7 @@ import sys
import random import random
import json import json
import product_gen.generate_product import product_gen.generate_product
import product_gen.shapes.shape_description as sd
from argparse import ArgumentParser from argparse import ArgumentParser
if __name__ == "__main__": if __name__ == "__main__":
@ -23,25 +24,15 @@ if __name__ == "__main__":
# Global bounding box # Global bounding box
bbox_xmax = 2.2 bbox_xmax = 2.2
bbox_ymax = 2.5 bbox_ymax = 20.0
# Minimum feature sizes # Minimum feature sizes
feature_xmin = 0.4 feature_xmin = 0.4
feature_ymin = 1.0 feature_ymin = 1.0
shape_parameters = sd.get_random_shape_description(shape,
[bbox_xmax, bbox_ymax],
[feature_xmin, feature_ymin])
height = 1.0 + random.random()*1.5 shape_parameters = product_gen.generate_product.generate(shape_parameters, output)
radius = 0.2 + random.random()*1.0
color = [0.1, 0.1, 0.1]
height, radius, color = product_gen.generate_product.generate(shape,
height,
radius,
color,
output)
shape_parameters = {"shape": shape,
"height": height,
"radius": radius,
"color": color}
with open(os.getcwd() + "/" + output + "/"+ output + '.json', 'w') as outfile: with open(os.getcwd() + "/" + output + "/"+ output + '.json', 'w') as outfile:
json.dump(shape_parameters, outfile) json.dump(shape_parameters, outfile)

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
{"color": [0.1, 0.1, 0.1], "shape": "cone", "radius": 1.0698675312816335, "height": 2.122232726076932} {"radius": 0.2561832220044716, "height": 2.088070401902109, "shape": "cone", "color": [0.1, 0.1, 0.1]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
{"height": 1.5, "color": [0.1, 0.1, 0.1], "shape": "bio", "radius": 1.5} {"shape": "pendant", "style": "dark", "division_patterns": ["ramp", "ramp", "inv_ramp", "inv_sine"], "fixture_radius": 0.3, "height": 4.305539689400392, "radius": 0.8584699492695225, "division_offsets": [0.25, 0.25, 0.25, 0.25], "division_radii": [0.5441324228652892, 0.6530641783849034, 0.7409502304795115, 0.751148488921151]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

After

Width:  |  Height:  |  Size: 119 KiB

View file

@ -3,6 +3,7 @@ import sys
import random import random
import json import json
import product_gen.generate_product import product_gen.generate_product
import shapes.shape_description as sd
from argparse import ArgumentParser from argparse import ArgumentParser
if __name__ == "__main__": if __name__ == "__main__":
@ -13,15 +14,17 @@ if __name__ == "__main__":
if not os.path.exists(os.getcwd() + "/" + output): if not os.path.exists(os.getcwd() + "/" + output):
os.makedirs(os.getcwd() + "/" + output) os.makedirs(os.getcwd() + "/" + output)
height = 1.5 # Global bounding box
radius = 1.5 bbox_xmax = 4.0
color = [0.1, 0.1, 0.1] bbox_ymax = 5.0
product_gen.generate_product.generate(shape, height, radius, color, output) # Minimum feature sizes
feature_xmin = 0.4
feature_ymin = 1.0
shape_parameters = sd.get_random_shape_description(shape,
[bbox_xmax, bbox_ymax],
[feature_xmin, feature_ymin])
shape_parameters = product_gen.generate_product.generate(shape_parameters, output)
shape_parameters = {"shape": shape,
"height": height,
"radius": radius,
"color": color}
with open(os.getcwd() + "/" + output + "/"+ output + '.json', 'w') as outfile: with open(os.getcwd() + "/" + output + "/"+ output + '.json', 'w') as outfile:
json.dump(shape_parameters, outfile) json.dump(shape_parameters, outfile)