mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-10 01:33:28 +00:00
187 lines
6.1 KiB
Python
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'}
|