Files
blender-addons/add_mesh_extra_objects/add_mesh_menger_sponge.py
2019-05-15 18:05:28 +02:00

187 lines
6.1 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (c) 2015 sugiany
# This file is distributed under the MIT License. See the LICENSE.md for more details.
import bpy
from bpy_extras.object_utils import (
AddObjectHelper,
object_data_add,
)
from bpy.props import (
IntProperty,
BoolProperty,
BoolVectorProperty,
FloatVectorProperty,
FloatProperty,
)
import mathutils
import copy
class MengerSponge(object):
FACE_INDICES = [
[3, 7, 4, 0],
[5, 6, 2, 1],
[1, 2, 3, 0],
[7, 6, 5, 4],
[4, 5, 1, 0],
[2, 6, 7, 3],
]
def __init__(self, level):
self.__level = level
self.__max_point_number = 3 ** level
self.__vertices_map = {}
self.__indices = []
self.__face_visibility = {}
self.__faces = []
for x in range(3):
for y in range(3):
for z in range(3):
self.__face_visibility[(x, y, z)] = [
x == 0 or x == 2 and (y == 1 or z == 1),
x == 2 or x == 0 and (y == 1 or z == 1),
y == 0 or y == 2 and (x == 1 or z == 1),
y == 2 or y == 0 and (x == 1 or z == 1),
z == 0 or z == 2 and (y == 1 or x == 1),
z == 2 or z == 0 and (y == 1 or x == 1),
]
def create(self, width, height):
m = self.__max_point_number
points = [
(0, 0, 0),
(m, 0, 0),
(m, 0, m),
(0, 0, m),
(0, m, 0),
(m, m, 0),
(m, m, m),
(0, m, m),
]
self.__make_sub_sponge(points, None, self.__level)
vertices = self.__make_vertices(width, height)
return vertices, self.__faces
def __get_vindex(self, p):
if p in self.__vertices_map:
return self.__vertices_map[p]
index = len(self.__vertices_map)
self.__vertices_map[p] = index
return index
def __make_vertices(self, width, height):
vertices = [None] * len(self.__vertices_map)
w2 = width / 2
h2 = height / 2
w_step = width / self.__max_point_number
h_step = height / self.__max_point_number
for p, i in sorted(self.__vertices_map.items(), key=lambda x: x[1]):
vertices[i] = mathutils.Vector([
p[0] * w_step - w2,
p[1] * w_step - w2,
p[2] * h_step - h2,
])
return vertices
def __make_sub_sponge(self, cur_points, face_vis, depth):
if depth <= 0:
if not face_vis:
face_vis = [True] * 6
cur_point_indices = []
for p in cur_points:
cur_point_indices.append(self.__get_vindex(p))
for i, vis in enumerate(face_vis):
if vis:
f = []
for vi in self.FACE_INDICES[i]:
f.append(cur_point_indices[vi])
self.__faces.append(f)
return
base = cur_points[0]
width = (cur_points[1][0] - base[0]) / 3
local_vert_map = {}
for z in range(4):
for y in range(4):
for x in range(4):
local_vert_map[(x, y, z)] = (
width * x + base[0],
width * y + base[1],
width * z + base[2],
)
for x in range(3):
for y in range(3):
for z in range(3):
if [x, y, z].count(1) > 1:
continue
next_points = [
local_vert_map[(x, y, z)],
local_vert_map[(x + 1, y, z)],
local_vert_map[(x + 1, y, z + 1)],
local_vert_map[(x, y, z + 1)],
local_vert_map[(x, y + 1, z)],
local_vert_map[(x + 1, y + 1, z)],
local_vert_map[(x + 1, y + 1, z + 1)],
local_vert_map[(x, y + 1, z + 1)],
]
visibility = copy.copy(self.__face_visibility[(x, y, z)])
if face_vis:
visibility[0] = visibility[0] and (face_vis[0] or x != 0)
visibility[1] = visibility[1] and (face_vis[1] or x != 2)
visibility[2] = visibility[2] and (face_vis[2] or y != 0)
visibility[3] = visibility[3] and (face_vis[3] or y != 2)
visibility[4] = visibility[4] and (face_vis[4] or z != 0)
visibility[5] = visibility[5] and (face_vis[5] or z != 2)
self.__make_sub_sponge(
next_points,
visibility,
depth - 1)
class AddMengerSponge(bpy.types.Operator, AddObjectHelper):
bl_idname = "mesh.menger_sponge_add"
bl_label = "Menger Sponge"
bl_description = "Construct a menger sponge mesh"
bl_options = {'REGISTER', 'UNDO'}
level: IntProperty(
name="Level",
description="Sponge Level",
min=0, max=4,
default=1,
)
radius: FloatProperty(
name="Width",
description="Sponge Radius",
min=0.01, max=100.0,
default=1.0,
)
layers: BoolVectorProperty(
name="Layers",
size=20,
subtype='LAYER',
options={'HIDDEN', 'SKIP_SAVE'},
)
def execute(self, context):
sponger = MengerSponge(self.level)
vertices, faces = sponger.create(self.radius * 2, self.radius * 2)
del sponger
mesh = bpy.data.meshes.new(name='Sponge')
mesh.from_pydata(vertices, [], faces)
uvs = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)]
mesh.uv_layers.new()
for i, uvloop in enumerate(mesh.uv_layers.active.data):
uvloop.uv = uvs[i % 4]
object_data_add(context, mesh, operator=self)
return {'FINISHED'}