kopia lustrzana https://github.com/torrinworx/Blend_My_NFTs
243 wiersze
7.9 KiB
Python
243 wiersze
7.9 KiB
Python
|
import bpy
|
||
|
import re
|
||
|
import copy
|
||
|
|
||
|
enableGeneration = False
|
||
|
colorList = []
|
||
|
|
||
|
class bcolors:
|
||
|
'''
|
||
|
The colour of console messages.
|
||
|
'''
|
||
|
OK = '\033[92m' # GREEN
|
||
|
WARNING = '\033[93m' # YELLOW
|
||
|
ERROR = '\033[91m' # RED
|
||
|
RESET = '\033[0m' # RESET COLOR
|
||
|
|
||
|
def stripColorFromName(name):
|
||
|
return "_".join(name.split("_")[:-1])
|
||
|
|
||
|
def get_combinations_from_scene():
|
||
|
'''
|
||
|
Generates important variables, dictionaries, and lists needed to be stored to catalog the NFTs.
|
||
|
:return: listAllCollections, attributeCollections, attributeCollections1, hierarchy, variantMetaData, possibleCombinations
|
||
|
'''
|
||
|
|
||
|
coll = bpy.context.scene.collection
|
||
|
|
||
|
try:
|
||
|
scriptIgnore = bpy.data.collections["Script_Ignore"]
|
||
|
except:
|
||
|
print(f"{bcolors.ERROR} ERROR:\nScript_Ignore collection is not in .blend file scene. Please add the Script_Ignore collection to your "
|
||
|
f".blend file scene. For more information, read the README.md file.\n {bcolors.RESET}")
|
||
|
|
||
|
listAllCollInScene = []
|
||
|
listAllCollections = []
|
||
|
|
||
|
def traverse_tree(t):
|
||
|
yield t
|
||
|
for child in t.children:
|
||
|
yield from traverse_tree(child)
|
||
|
|
||
|
for c in traverse_tree(coll):
|
||
|
listAllCollInScene.append(c)
|
||
|
|
||
|
def listSubIgnoreCollections():
|
||
|
def getParentSubCollections(collection):
|
||
|
yield collection
|
||
|
for child in collection.children:
|
||
|
yield from getParentSubCollections(child)
|
||
|
|
||
|
collList = []
|
||
|
for c in getParentSubCollections(scriptIgnore):
|
||
|
collList.append(c.name)
|
||
|
return collList
|
||
|
|
||
|
ignoreList = listSubIgnoreCollections()
|
||
|
|
||
|
for i in listAllCollInScene:
|
||
|
if enableGeneration:
|
||
|
if i.name in colorList:
|
||
|
for j in range(len(colorList[i.name])):
|
||
|
if i.name[-1].isdigit() and i.name not in ignoreList:
|
||
|
listAllCollections.append(i.name + "_" + str(j + 1))
|
||
|
elif j == 0:
|
||
|
listAllCollections.append(i.name)
|
||
|
elif i.name[-1].isdigit() and i.name not in ignoreList:
|
||
|
listAllCollections.append(i.name + "_0")
|
||
|
else:
|
||
|
listAllCollections.append(i.name)
|
||
|
else:
|
||
|
listAllCollections.append(i.name)
|
||
|
|
||
|
listAllCollections.remove(scriptIgnore.name)
|
||
|
|
||
|
if "Scene Collection" in listAllCollections:
|
||
|
listAllCollections.remove("Scene Collection")
|
||
|
|
||
|
if "Master Collection" in listAllCollections:
|
||
|
listAllCollections.remove("Master Collection")
|
||
|
|
||
|
def allScriptIgnore(collection):
|
||
|
'''
|
||
|
Removes all collections, sub collections in Script_Ignore collection from listAllCollections.
|
||
|
'''
|
||
|
for coll in list(collection.children):
|
||
|
listAllCollections.remove(coll.name)
|
||
|
listColl = list(coll.children)
|
||
|
if len(listColl) > 0:
|
||
|
allScriptIgnore(coll)
|
||
|
|
||
|
allScriptIgnore(scriptIgnore)
|
||
|
listAllCollections.sort()
|
||
|
|
||
|
exclude = ["_", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
|
||
|
attributeCollections = copy.deepcopy(listAllCollections)
|
||
|
|
||
|
def filter_num():
|
||
|
"""
|
||
|
This function removes items from 'attributeCollections' if they include values from the 'exclude' variable.
|
||
|
It removes child collections from the parent collections in from the "listAllCollections" list.
|
||
|
"""
|
||
|
for x in attributeCollections:
|
||
|
if any(a in x for a in exclude):
|
||
|
attributeCollections.remove(x)
|
||
|
|
||
|
for i in range(len(listAllCollections)):
|
||
|
filter_num()
|
||
|
|
||
|
attributeVariants = [x for x in listAllCollections if x not in attributeCollections]
|
||
|
attributeCollections1 = copy.deepcopy(attributeCollections)
|
||
|
|
||
|
def attributeData(attributeVariants):
|
||
|
"""
|
||
|
Creates a dictionary of each attribute
|
||
|
"""
|
||
|
allAttDataList = {}
|
||
|
count = 0
|
||
|
previousAttribute = ""
|
||
|
for i in attributeVariants:
|
||
|
|
||
|
def getName(i):
|
||
|
"""
|
||
|
Returns the name of "i" attribute variant
|
||
|
"""
|
||
|
name = i.split("_")[0]
|
||
|
return name
|
||
|
|
||
|
def getOrder_rarity(i):
|
||
|
"""
|
||
|
Returns the "order", "rarity" and "color" (if enabled) of i attribute variant in a list
|
||
|
"""
|
||
|
x = re.sub(r'[a-zA-Z]', "", i)
|
||
|
a = x.split("_")
|
||
|
del a[0]
|
||
|
return list(a)
|
||
|
|
||
|
name = getName(i)
|
||
|
orderRarity = getOrder_rarity(i)
|
||
|
|
||
|
if len(orderRarity) == 0:
|
||
|
print(f"{bcolors.ERROR} \nERROR: {bcolors.RESET}")
|
||
|
print(f"The collection {i} doesn't follow the naming conventions of attributes. Please move this \n"
|
||
|
"colleciton to Script_Ignore or review proper collection format in README.md")
|
||
|
return
|
||
|
|
||
|
elif len(orderRarity) > 0:
|
||
|
number = orderRarity[0]
|
||
|
if enableGeneration:
|
||
|
if count == 1 or count == 0:
|
||
|
previousAttribute = i.partition("_")[0]
|
||
|
count +=1
|
||
|
elif i.partition("_")[0] == previousAttribute:
|
||
|
count +=1
|
||
|
else:
|
||
|
count = 1
|
||
|
number = str(count)
|
||
|
rarity = orderRarity[1]
|
||
|
if enableGeneration and stripColorFromName(i) in colorList:
|
||
|
color = orderRarity[2]
|
||
|
else:
|
||
|
color = "0"
|
||
|
eachObject = {"name": name, "number": number, "rarity": rarity, "color": color}
|
||
|
allAttDataList[i] = eachObject
|
||
|
return allAttDataList
|
||
|
|
||
|
variantMetaData = attributeData(attributeVariants)
|
||
|
|
||
|
def getHierarchy():
|
||
|
"""
|
||
|
Constructs the hierarchy dictionary from attributeCollections1 and variantMetaData.
|
||
|
"""
|
||
|
hierarchy = {}
|
||
|
for i in attributeCollections1:
|
||
|
colParLong = list(bpy.data.collections[str(i)].children)
|
||
|
colParShort = {}
|
||
|
for x in colParLong:
|
||
|
if enableGeneration:
|
||
|
"""
|
||
|
Append colors to blender name for PNG generator and NFTRecord.json to create the correct list
|
||
|
"""
|
||
|
if x.name in colorList:
|
||
|
for j in range(len(colorList[x.name])):
|
||
|
colParShort[x.name + "_" + str(j+1)] = None
|
||
|
else:
|
||
|
colParShort[x.name + "_0"] = None
|
||
|
else:
|
||
|
colParShort[x.name] = None
|
||
|
hierarchy[i] = colParShort
|
||
|
|
||
|
for a in hierarchy:
|
||
|
for b in hierarchy[a]:
|
||
|
for x in variantMetaData:
|
||
|
if str(x) == str(b):
|
||
|
(hierarchy[a])[b] = variantMetaData[x]
|
||
|
|
||
|
return hierarchy
|
||
|
|
||
|
hierarchy = getHierarchy()
|
||
|
|
||
|
def numOfCombinations(hierarchy):
|
||
|
"""
|
||
|
Returns "combinations" the number of all possible NFT combinations.
|
||
|
"""
|
||
|
|
||
|
hierarchyByNum = []
|
||
|
|
||
|
for i in hierarchy:
|
||
|
# Ignore Collections with nothing in them
|
||
|
if len(hierarchy[i]) != 0:
|
||
|
hierarchyByNum.append(len(hierarchy[i]))
|
||
|
else:
|
||
|
print(f"The following collection has been identified as empty: {i}")
|
||
|
|
||
|
combinations = 1
|
||
|
for i in hierarchyByNum:
|
||
|
combinations = combinations*i
|
||
|
|
||
|
if combinations == 0:
|
||
|
print(bcolors.ERROR + "\nERROR:" + bcolors.RESET)
|
||
|
print("The number of all possible combinations is equal to 0. Please review your collection hierarchy"
|
||
|
"and ensure it is formatted correctly. Please review README.md for more information. \nHere is the "
|
||
|
"hierarchy of all collections the DNA_Generator gathered from your .blend file, excluding those in "
|
||
|
f"Script_Ignore: {hierarchy}")
|
||
|
|
||
|
return combinations
|
||
|
|
||
|
possibleCombinations = numOfCombinations(hierarchy)
|
||
|
|
||
|
for i in variantMetaData:
|
||
|
def cameraToggle(i, toggle=True):
|
||
|
if enableGeneration:
|
||
|
"""
|
||
|
Remove Color code so blender recognises the collection
|
||
|
"""
|
||
|
i = stripColorFromName(i)
|
||
|
bpy.data.collections[i].hide_render = toggle
|
||
|
bpy.data.collections[i].hide_viewport = toggle
|
||
|
cameraToggle(i)
|
||
|
|
||
|
return possibleCombinations
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
get_combinations_from_scene()
|