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 bpy
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
radius2 = 0.3
if shape == "cone":
shade = product_gen.generate_shades.generate_pendant_shade(radius,
radius2,
height)
elif shape == "mesh":
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)
if shape_parameters["shape"] == "mesh":
shade = gs.generate_mesh_shade(shape_parameters)
elif shape_parameters["shape"] == "led":
shade = gs.generate_led_shade(shape_parameters)
else: # default pendant
shade = gs.generate_pendant_shade(shape_parameters)
# Base
if "bio" not in shape:
if "bio" not in shape_parameters["shape"]:
radius1 = 0.3
radius2 = 0.07
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,
radius2,
depth,

View file

@ -5,25 +5,21 @@ import product_gen.generate_lamp
import rendering.setup_scene
import rendering.setup_renderer
def generate(shape,
height,
radius,
color,
output_prefix,
is_final=False):
def generate(shape_parameters, 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
models = product_gen.generate_lamp.generate_models(shape, height, radius)
models = product_gen.generate_lamp.generate_models(shape_parameters)
# Optimize geometries for render
product_gen.generate_lamp.optimize_for_render(models)
# 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,
"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)}
@ -32,7 +28,7 @@ def generate(shape,
product_gen.generate_lamp.apply_textures(colormap, models)
rendering.setup_scene.setup_scene()
rendering.setup_scene.setup_scene(shape_parameters)
# Do rendering
prefix = os.getcwd() + "/" + output_prefix + "/" + output_prefix
@ -52,4 +48,4 @@ def generate(shape,
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 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)
cone = bpy.data.objects["Cylinder"]
cone.name = "shade"
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()
bm = bmesh.from_edit_mesh(cone.data)
for i in range( 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)
num_verts = len(bm.verts)
mapped_rad = radius2 + delta**2
vert.co.x = mapped_rad*math.sin(theta)
vert.co.y = mapped_rad*math.cos(theta)
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.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:
if UpOrDown(face.normal):
@ -160,7 +236,7 @@ def make_square_ring(radius, depth, thickness):
bpy.ops.object.mode_set(mode='OBJECT')
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)
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
def setup_renderer(engine="CYCLES"):
def setup_renderer(shape_parameters, engine="CYCLES"):
# Set up and do the render
this_scene = bpy.context.scene
if engine=="CYCLES":
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.device = 'GPU'
bpy.context.scene.cycles.samples = 12.0
bpy.context.scene.cycles.caustics_reflective = False
bpy.context.scene.cycles.caustics_refractive = False
bpy.context.scene.cycles.max_bounces = 0.0
bpy.data.scenes["Scene"].render.use_border = False # Tell Blender to use border render
bpy.data.scenes["Scene"].render.border_max_y = 0.75 # Set the border top at 3/4 of the image's height
bpy.data.scenes["Scene"].render.border_min_y = 0.33 # Set the border bottom at 1/3 of the image's height
bpy.data.scenes["Scene"].render.border_min_x = 0.25 # Set the border left at 1/4 of the image's width
bpy.data.scenes["Scene"].render.border_max_x = 0.65 # Set the border right at the image's right border
bpy.context.scene.render.resolution_x = 600.0
bpy.context.scene.render.resolution_y = 600.0
bpy.context.scene.render.tile_x = 32
bpy.context.scene.render.tile_y = 32
bpy.context.scene.render.resolution_percentage = 50.0
this_scene.render.engine = 'CYCLES'
this_scene.cycles.device = 'GPU'
this_scene.cycles.samples = 12.0
this_scene.cycles.caustics_reflective = False
this_scene.cycles.caustics_refractive = False
this_scene.cycles.max_bounces = 0.0
this_scene.render.use_border = False
this_scene.render.border_max_y = 0.75
this_scene.render.border_min_y = 0.33
this_scene.render.border_min_x = 0.25
this_scene.render.border_max_x = 0.65
this_scene.render.resolution_x = 600.0
this_scene.render.resolution_y = 600.0
this_scene.render.tile_x = 32
this_scene.render.tile_y = 32
this_scene.render.resolution_percentage = 50.0

View file

@ -1,38 +1,38 @@
import math
import bpy
def initialize_scene():
def initialize_scene(shape_parameters):
# Set up scene
objs = bpy.data.objects
bpy.data.scenes['Scene'].objects.unlink(objs["Cube"])
objs.remove(objs["Cube"])
def setup_scene():
def setup_scene(shape_parameters):
# Set up world
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
# Add back wall
# 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.data.objects["Plane"].name = "back_wall"
# mat = bpy.data.materials.get("backwall-material")
# if mat is None:
# mat = bpy.data.materials.new("backwall-material")
# mat.diffuse_color = (0.25, 0.25, 0.25)
# mat.specular_color = (0.25, 0.25, 0.25)
# mat.diffuse_intensity = 1.0
# mat.specular_intensity = 1.0
# bpy.context.scene.objects.active = bpy.data.objects["back_wall"]
# if bpy.context.active_object.data.materials:
# # assign to 1st material slot
# bpy.context.active_object.data.materials[0] = mat
# else:
# # no slots
# bpy.context.active_object.data.materials.append(mat)
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.data.objects["Plane"].name = "back_wall"
mat = bpy.data.materials.get("backwall-material")
if mat is None:
mat = bpy.data.materials.new("backwall-material")
mat.diffuse_color = (0.25, 0.25, 0.25)
mat.specular_color = (0.25, 0.25, 0.25)
mat.diffuse_intensity = 1.0
mat.specular_intensity = 1.0
bpy.context.scene.objects.active = bpy.data.objects["back_wall"]
if bpy.context.active_object.data.materials:
# assign to 1st material slot
bpy.context.active_object.data.materials[0] = mat
else:
# no slots
bpy.context.active_object.data.materials.append(mat)
# Set up cameras
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_euler[0] = math.pi/2.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
# 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 = shape_params['shape']
output = shape_params['output']
print(output)
if not os.path.exists(os.getcwd() + "/" + output):
os.makedirs(os.getcwd() + "/" + output)

View file

@ -7,6 +7,7 @@ import sys
import random
import json
import product_gen.generate_product
import product_gen.shapes.shape_description as sd
from argparse import ArgumentParser
if __name__ == "__main__":
@ -23,25 +24,15 @@ if __name__ == "__main__":
# Global bounding box
bbox_xmax = 2.2
bbox_ymax = 2.5
bbox_ymax = 20.0
# 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])
height = 1.0 + random.random()*1.5
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}
shape_parameters = product_gen.generate_product.generate(shape_parameters, output)
with open(os.getcwd() + "/" + output + "/"+ output + '.json', 'w') as 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 json
import product_gen.generate_product
import shapes.shape_description as sd
from argparse import ArgumentParser
if __name__ == "__main__":
@ -13,15 +14,17 @@ if __name__ == "__main__":
if not os.path.exists(os.getcwd() + "/" + output):
os.makedirs(os.getcwd() + "/" + output)
height = 1.5
radius = 1.5
color = [0.1, 0.1, 0.1]
# Global bounding box
bbox_xmax = 4.0
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:
json.dump(shape_parameters, outfile)