kopia lustrzana https://github.com/torrinworx/Blend_My_NFTs
Committing Logic patch and Checks
rodzic
bc5568b564
commit
c8e8d62837
23
__init__.py
23
__init__.py
|
@ -157,9 +157,8 @@ def update_combinations(dummy1, dummy2):
|
|||
global recommended_limit
|
||||
global offset
|
||||
|
||||
combinations = (get_combinations.get_combinations_from_scene())
|
||||
combinations = (get_combinations.get_combinations())
|
||||
recommended_limit = int(round(combinations/2))
|
||||
|
||||
redraw_panel()
|
||||
|
||||
|
||||
|
@ -176,7 +175,7 @@ class createData(bpy.types.Operator):
|
|||
def execute(self, context):
|
||||
|
||||
nftName = bpy.context.scene.my_tool.nftName
|
||||
maxNFTs = bpy.context.scene.my_tool.collectionSize
|
||||
collectionSize = bpy.context.scene.my_tool.collectionSize
|
||||
nftsPerBatch = bpy.context.scene.my_tool.nftsPerBatch
|
||||
save_path = bpy.path.abspath(bpy.context.scene.my_tool.save_path)
|
||||
logicFile = bpy.path.abspath(bpy.context.scene.my_tool.logicFile)
|
||||
|
@ -186,8 +185,8 @@ class createData(bpy.types.Operator):
|
|||
|
||||
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
|
||||
|
||||
DNA_Generator.send_To_Record_JSON(maxNFTs, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, Blend_My_NFTs_Output)
|
||||
Batch_Sorter.makeBatches(nftName, maxNFTs, nftsPerBatch, save_path, batch_json_save_path)
|
||||
DNA_Generator.send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, Blend_My_NFTs_Output)
|
||||
Batch_Sorter.makeBatches(nftName, collectionSize, nftsPerBatch, save_path, batch_json_save_path)
|
||||
|
||||
self.report({'INFO'}, f"NFT Data created!")
|
||||
|
||||
|
@ -203,7 +202,7 @@ class exportNFTs(bpy.types.Operator):
|
|||
nftName = bpy.context.scene.my_tool.nftName
|
||||
save_path = bpy.path.abspath(bpy.context.scene.my_tool.save_path)
|
||||
batchToGenerate = bpy.context.scene.my_tool.batchToGenerate
|
||||
maxNFTs = bpy.context.scene.my_tool.collectionSize
|
||||
collectionSize = bpy.context.scene.my_tool.collectionSize
|
||||
|
||||
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
|
||||
|
||||
|
@ -222,7 +221,7 @@ class exportNFTs(bpy.types.Operator):
|
|||
failed_dna = None
|
||||
failed_dna_index = None
|
||||
|
||||
Exporter.render_and_save_NFTs(nftName, maxNFTs, batchToGenerate, batch_json_save_path, nftBatch_save_path, enableImages,
|
||||
Exporter.render_and_save_NFTs(nftName, collectionSize, batchToGenerate, batch_json_save_path, nftBatch_save_path, enableImages,
|
||||
imageFileFormat, enableAnimations, animationFileFormat, enableModelsBlender,
|
||||
modelFileFormat, fail_state, failed_batch, failed_dna, failed_dna_index
|
||||
)
|
||||
|
@ -241,7 +240,7 @@ class resume_failed_batch(bpy.types.Operator):
|
|||
nftName = bpy.context.scene.my_tool.nftName
|
||||
save_path = bpy.path.abspath(bpy.context.scene.my_tool.save_path)
|
||||
batchToGenerate = bpy.context.scene.my_tool.batchToGenerate
|
||||
maxNFTs = bpy.context.scene.my_tool.collectionSize
|
||||
collectionSize = bpy.context.scene.my_tool.collectionSize
|
||||
|
||||
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
|
||||
fail_state, failed_batch, failed_dna, failed_dna_index = Checks.check_FailedBatches(batch_json_save_path)
|
||||
|
@ -257,7 +256,7 @@ class resume_failed_batch(bpy.types.Operator):
|
|||
enableModelsBlender = batch["Generation Save"][-1]["Render_Settings"]["enableModelsBlender"]
|
||||
modelFileFormat = batch["Generation Save"][-1]["Render_Settings"]["modelFileFormat"]
|
||||
|
||||
Exporter.render_and_save_NFTs(nftName, maxNFTs, failed_batch, batch_json_save_path, nftBatch_save_path, enableImages,
|
||||
Exporter.render_and_save_NFTs(nftName, collectionSize, failed_batch, batch_json_save_path, nftBatch_save_path, enableImages,
|
||||
imageFileFormat, enableAnimations, animationFileFormat, enableModelsBlender,
|
||||
modelFileFormat, fail_state, failed_batch, failed_dna, failed_dna_index
|
||||
)
|
||||
|
@ -380,8 +379,12 @@ class BMNFTS_PT_GenerateNFTs(bpy.types.Panel):
|
|||
row.prop(mytool, "batchToGenerate")
|
||||
|
||||
save_path = bpy.path.abspath(bpy.context.scene.my_tool.save_path)
|
||||
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
|
||||
Blend_My_NFTs_Output = os.path.join(save_path, "Blend_My_NFTs Output", "NFT_Data")
|
||||
batch_json_save_path = os.path.join(Blend_My_NFTs_Output, "Batch_Data")
|
||||
nftBatch_save_path = os.path.join(save_path, "Blend_My_NFTs Output", "Generated NFT Batches")
|
||||
|
||||
fail_state, failed_batch, failed_dna, failed_dna_index = Checks.check_FailedBatches(batch_json_save_path)
|
||||
|
||||
if fail_state:
|
||||
row = layout.row()
|
||||
self.layout.operator("exporter.nfts", icon='RENDER_RESULT', text="Generate NFTs")
|
||||
|
|
111
main/Checks.py
111
main/Checks.py
|
@ -11,6 +11,7 @@ import os
|
|||
import json
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
from . import DNA_Generator, get_combinations
|
||||
|
||||
class bcolors:
|
||||
"""
|
||||
|
@ -22,6 +23,7 @@ class bcolors:
|
|||
ERROR = '\033[91m' # RED
|
||||
RESET = '\033[0m' # RESET COLOR
|
||||
|
||||
|
||||
# Checks:
|
||||
def check_Scene():
|
||||
"""
|
||||
|
@ -29,11 +31,36 @@ def check_Scene():
|
|||
violations.
|
||||
"""
|
||||
|
||||
script_ignore_exists = None # True if Script_Ignore collection exists in Blender scene
|
||||
attribute_naming_conventions = None # True if all attributes in Blender scene follow BMNFTs naming conventions
|
||||
variant_naming_conventions = None # True if all variants in Blender scene follow BMNFTs naming conventions
|
||||
object_placing_conventions = None # True if all objects are within either Script_Ignore or a variant collection
|
||||
|
||||
hierarchy = DNA_Generator.get_hierarchy()
|
||||
|
||||
# script_ignore_exists:
|
||||
try:
|
||||
scriptIgnoreCollection = bpy.data.collections["Script_Ignore"]
|
||||
except KeyError:
|
||||
raise TypeError(
|
||||
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
|
||||
f"collection to your Blender scene and ensure the name is exactly 'Script_Ignore'. For more information, "
|
||||
f"see:"
|
||||
f"\nhttps://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
||||
)
|
||||
else:
|
||||
script_ignore_exists = True
|
||||
|
||||
collections = bpy.context.scene.collection
|
||||
print(collections)
|
||||
|
||||
# attribute_naming_conventions
|
||||
|
||||
|
||||
def check_Rarity(hierarchy, DNAList, save_path):
|
||||
"""Checks rarity percentage of each Variant, then sends it to RarityData.json in NFT_Data folder."""
|
||||
numNFTsGenerated = len(DNAList)
|
||||
|
||||
attributeNames = []
|
||||
numDict = defaultdict(list)
|
||||
|
||||
hierarchy.keys()
|
||||
|
@ -74,9 +101,11 @@ def check_Rarity(hierarchy, DNAList, save_path):
|
|||
|
||||
completeData[i] = x
|
||||
|
||||
print(completeData)
|
||||
|
||||
print(bcolors.OK + "Rarity Checker is active. These are the percentages for each variant per attribute you set in your .blend file:" + bcolors.RESET)
|
||||
print(
|
||||
f"\n{bcolors.OK}\n"
|
||||
f"Rarity Checker is active. These are the percentages for each variant per attribute you set in your .blend file:"
|
||||
f"\n{bcolors.RESET}"
|
||||
)
|
||||
|
||||
for i in completeData:
|
||||
print(i + ":")
|
||||
|
@ -105,42 +134,27 @@ def check_Duplicates(DNAList):
|
|||
print(f"NFTRecord.json contains {duplicates} duplicate NFT DNA.")
|
||||
|
||||
def check_FailedBatches(batch_json_save_path):
|
||||
batch_folders = os.listdir(batch_json_save_path)
|
||||
fail_state = False
|
||||
failed_batch = None
|
||||
failed_dna = None
|
||||
failed_dna_index = None
|
||||
|
||||
for i in batch_folders:
|
||||
batch = json.load(open(os.path.join(batch_json_save_path, i)))
|
||||
NFTs_in_Batch = batch["NFTs_in_Batch"]
|
||||
if "Generation Save" in batch:
|
||||
dna_generated = batch["Generation Save"][-1]["DNA Generated"]
|
||||
if dna_generated < NFTs_in_Batch:
|
||||
fail_state = True
|
||||
failed_batch = int(i.removeprefix("Batch").removesuffix(".json"))
|
||||
failed_dna = dna_generated
|
||||
if os.path.isdir(batch_json_save_path):
|
||||
batch_folders = os.listdir(batch_json_save_path)
|
||||
|
||||
for i in batch_folders:
|
||||
batch = json.load(open(os.path.join(batch_json_save_path, i)))
|
||||
NFTs_in_Batch = batch["NFTs_in_Batch"]
|
||||
if "Generation Save" in batch:
|
||||
dna_generated = batch["Generation Save"][-1]["DNA Generated"]
|
||||
if dna_generated < NFTs_in_Batch:
|
||||
fail_state = True
|
||||
failed_batch = int(i.removeprefix("Batch").removesuffix(".json"))
|
||||
failed_dna = dna_generated
|
||||
|
||||
return fail_state, failed_batch, failed_dna, failed_dna_index
|
||||
|
||||
|
||||
# Raise Errors:
|
||||
def raise_Error_ScriptIgnore():
|
||||
"""Checks if Script_Ignore collection exists, if not raises error."""
|
||||
|
||||
try:
|
||||
scriptIgnore = bpy.data.collections["Script_Ignore"]
|
||||
return scriptIgnore
|
||||
except KeyError:
|
||||
raise KeyError(
|
||||
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
|
||||
f"Script_Ignore collection not found in Blender scene. Please add the Script_Ignore "
|
||||
f"collection to Blender scene or ensure the spelling is exactly 'Script_Ignore'. For more information, "
|
||||
f"see:\n{bcolors.RESET}"
|
||||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
||||
)
|
||||
|
||||
def raise_Error_numBatches(maxNFTs, nftsPerBatch):
|
||||
"""Checks if number of Batches is less than maxNFTs, if not raises error."""
|
||||
|
||||
|
@ -148,15 +162,18 @@ def raise_Error_numBatches(maxNFTs, nftsPerBatch):
|
|||
numBatches = maxNFTs / nftsPerBatch
|
||||
return numBatches
|
||||
except ZeroDivisionError:
|
||||
print(f"{bcolors.ERROR} ERROR:\nnftsPerBatch in config.py needs to be a positive integer. {bcolors.RESET}")
|
||||
raise ZeroDivisionError(
|
||||
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
|
||||
f"The number of combinations is less than the number of \n{bcolors.RESET}"
|
||||
f"The number of NFTs per Batch must be greater than ZERO."
|
||||
f"Please review your Blender scene and ensure it follows "
|
||||
f"the naming conventions and scene structure. For more information, "
|
||||
f"see:\n{bcolors.RESET}"
|
||||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
||||
)
|
||||
|
||||
def raise_Error_ZeroCombinations(combinations):
|
||||
def raise_Error_ZeroCombinations():
|
||||
"""Checks if combinations is greater than 0, if so, raises error."""
|
||||
if combinations == 0:
|
||||
if get_combinations.get_combinations() == 0:
|
||||
raise ValueError(
|
||||
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
|
||||
f"The number of all possible combinations is ZERO. Please review your Blender scene and ensure it follows "
|
||||
|
@ -177,13 +194,29 @@ def raise_Error_numBatchesGreaterThan(numBatches):
|
|||
|
||||
# Raise Warnings:
|
||||
|
||||
def raise_Warning_maxNFTs(nftsPerBatch, maxNFTs):
|
||||
def raise_Warning_maxNFTs(nftsPerBatch, collectionSize):
|
||||
"""
|
||||
Prints warning if nftsPerBatch is greater than collectionSize.
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
if nftsPerBatch > maxNFTs:
|
||||
raise (
|
||||
if nftsPerBatch > collectionSize:
|
||||
raise ValueError(
|
||||
f"\n{bcolors.WARNING}Blend_My_NFTs Warning:\n"
|
||||
f"The number of NFTs Per Batch you set is smaller than the NFT Collection Size you set.\n{bcolors.RESET}"
|
||||
)
|
||||
|
||||
def raise_Warning_collectionSize(DNAList, collectionSize):
|
||||
"""
|
||||
Prints warning if BMNFTs cannot generate requested number of NFTs from a given collectionSize.
|
||||
"""
|
||||
|
||||
if len(DNAList) < collectionSize:
|
||||
print(f"\n{bcolors.WARNING} \nWARNING: \n"
|
||||
f"Blend_My_NFTs cannot generate {collectionSize} NFTs."
|
||||
f" Only {len(DNAList)} NFT DNA were generated."
|
||||
|
||||
f"\nThis might be for a number of reasons:"
|
||||
f"\n a) Rarity is preventing combinations from being generated (See https://github.com/torrinworx/Blend_My_NFTs#notes-on-rarity-and-weighted-variants).\n"
|
||||
f"\n b) Logic is preventing combinations from being generated (See https://github.com/torrinworx/Blend_My_NFTs#logic).\n"
|
||||
f"\n c) The number of possible combinations of your NFT collection is too low. Add more Variants or Attributes to increase the recommended collection size.\n"
|
||||
f"\n{bcolors.RESET}")
|
||||
|
|
|
@ -23,15 +23,14 @@ class bcolors:
|
|||
RESET = '\033[0m' # RESET COLOR
|
||||
|
||||
|
||||
def returnData(maxNFTs, nftsPerBatch):
|
||||
def get_hierarchy():
|
||||
"""
|
||||
Generates important variables, dictionaries, and lists needed to be stored to catalog the NFTs.
|
||||
:return: listAllCollections, attributeCollections, attributeCollections1, hierarchy, variantMetaData, possibleCombinations
|
||||
Returns the hierarchy of a given Blender scene.
|
||||
"""
|
||||
|
||||
coll = bpy.context.scene.collection
|
||||
|
||||
scriptIgnore = Checks.raise_Error_ScriptIgnore()
|
||||
scriptIgnoreCollection = bpy.data.collections["Script_Ignore"]
|
||||
|
||||
listAllCollInScene = []
|
||||
listAllCollections = []
|
||||
|
@ -47,7 +46,7 @@ def returnData(maxNFTs, nftsPerBatch):
|
|||
for i in listAllCollInScene:
|
||||
listAllCollections.append(i.name)
|
||||
|
||||
listAllCollections.remove(scriptIgnore.name)
|
||||
listAllCollections.remove(scriptIgnoreCollection.name)
|
||||
|
||||
if "Scene Collection" in listAllCollections:
|
||||
listAllCollections.remove("Scene Collection")
|
||||
|
@ -55,20 +54,19 @@ def returnData(maxNFTs, nftsPerBatch):
|
|||
if "Master Collection" in listAllCollections:
|
||||
listAllCollections.remove("Master Collection")
|
||||
|
||||
def allScriptIgnore(scriptIgnore):
|
||||
"""
|
||||
Removes all collections, sub collections in Script_Ignore collection from listAllCollections.
|
||||
"""
|
||||
for coll in list(scriptIgnore.children):
|
||||
def allScriptIgnore(scriptIgnoreCollection):
|
||||
# Removes all collections, sub collections in Script_Ignore collection from listAllCollections.
|
||||
|
||||
for coll in list(scriptIgnoreCollection.children):
|
||||
listAllCollections.remove(coll.name)
|
||||
listColl = list(coll.children)
|
||||
if len(listColl) > 0:
|
||||
allScriptIgnore(coll)
|
||||
|
||||
allScriptIgnore(scriptIgnore)
|
||||
allScriptIgnore(scriptIgnoreCollection)
|
||||
listAllCollections.sort()
|
||||
|
||||
exclude = ["_", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
|
||||
exclude = ["_"] # Excluding characters that identify a Variant
|
||||
attributeCollections = copy.deepcopy(listAllCollections)
|
||||
|
||||
def filter_num():
|
||||
|
@ -91,8 +89,6 @@ def returnData(maxNFTs, nftsPerBatch):
|
|||
Creates a dictionary of each attribute
|
||||
"""
|
||||
allAttDataList = {}
|
||||
count = 0
|
||||
previousAttribute = ""
|
||||
for i in attributeVariants:
|
||||
|
||||
def getName(i):
|
||||
|
@ -123,67 +119,28 @@ def returnData(maxNFTs, nftsPerBatch):
|
|||
|
||||
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:
|
||||
colParShort[x.name] = None
|
||||
hierarchy[i] = colParShort
|
||||
hierarchy = {}
|
||||
for i in attributeCollections1:
|
||||
colParLong = list(bpy.data.collections[str(i)].children)
|
||||
colParShort = {}
|
||||
for x in colParLong:
|
||||
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]
|
||||
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
|
||||
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
|
||||
|
||||
# Checks:
|
||||
numBatches = Checks.raise_Error_numBatches(maxNFTs, nftsPerBatch)
|
||||
|
||||
Checks.raise_Error_ZeroCombinations(combinations)
|
||||
|
||||
Checks.raise_Error_numBatchesGreaterThan(numBatches)
|
||||
|
||||
Checks.raise_Error_numBatchesGreaterThan(numBatches)
|
||||
|
||||
return combinations
|
||||
|
||||
possibleCombinations = numOfCombinations(hierarchy)
|
||||
|
||||
return listAllCollections, attributeCollections, attributeCollections1, hierarchy, possibleCombinations
|
||||
|
||||
def generateNFT_DNA(maxNFTs, nftsPerBatch, logicFile, enableRarity, enableLogic):
|
||||
def generateNFT_DNA(collectionSize, logicFile, enableRarity, enableLogic):
|
||||
"""
|
||||
Returns batchDataDictionary containing the number of NFT combinations, hierarchy, and the DNAList.
|
||||
"""
|
||||
|
||||
listAllCollections, attributeCollections, attributeCollections1, hierarchy, possibleCombinations = returnData(maxNFTs, nftsPerBatch)
|
||||
hierarchy = get_hierarchy()
|
||||
|
||||
# DNA random, Rarity and Logic methods:
|
||||
DataDictionary = {}
|
||||
|
@ -232,10 +189,10 @@ def generateNFT_DNA(maxNFTs, nftsPerBatch, logicFile, enableRarity, enableLogic)
|
|||
"""Creates DNAList. Loops through createDNARandom() and applies Rarity, and Logic while checking if all DNA are unique"""
|
||||
DNASetReturn = set()
|
||||
|
||||
for i in range(maxNFTs):
|
||||
for i in range(collectionSize):
|
||||
dnaPushToList = partial(singleCompleteDNA)
|
||||
|
||||
DNASetReturn |= {''.join([dnaPushToList()]) for _ in range(maxNFTs - len(DNASetReturn))}
|
||||
DNASetReturn |= {''.join([dnaPushToList()]) for _ in range(collectionSize - len(DNASetReturn))}
|
||||
|
||||
DNAListReturn = list(DNASetReturn)
|
||||
|
||||
|
@ -244,21 +201,17 @@ def generateNFT_DNA(maxNFTs, nftsPerBatch, logicFile, enableRarity, enableLogic)
|
|||
DNAList = create_DNAList()
|
||||
|
||||
# Messages:
|
||||
if len(DNAList) < maxNFTs:
|
||||
print(f"{bcolors.ERROR} \nWARNING: \n"
|
||||
f"You are seeing this warning because the program cannot generate {maxNFTs} NFTs with rarity enabled. "
|
||||
f"Only {len(DNAList)} NFT DNA were generated."
|
||||
f"Either A) Lower the number of NFTs you wish to create, or B) Increase the maximum number of possible NFTs by"
|
||||
f" creating more variants and attributes in your .blend file.{bcolors.RESET}")
|
||||
|
||||
Checks.raise_Warning_collectionSize(DNAList, collectionSize)
|
||||
|
||||
# Data stored in batchDataDictionary:
|
||||
DataDictionary["numNFTsGenerated"] = len(DNAList)
|
||||
DataDictionary["hierarchy"] = hierarchy
|
||||
DataDictionary["DNAList"] = DNAList
|
||||
|
||||
return DataDictionary, possibleCombinations
|
||||
return DataDictionary
|
||||
|
||||
def send_To_Record_JSON(maxNFTs, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, Blend_My_NFTs_Output):
|
||||
def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, Blend_My_NFTs_Output):
|
||||
"""
|
||||
Creates NFTRecord.json file and sends "batchDataDictionary" to it. NFTRecord.json is a permanent record of all DNA
|
||||
you've generated with all attribute variants. If you add new variants or attributes to your .blend file, other scripts
|
||||
|
@ -266,10 +219,13 @@ def send_To_Record_JSON(maxNFTs, nftsPerBatch, save_path, enableRarity, enableLo
|
|||
repeate DNA.
|
||||
"""
|
||||
|
||||
# Checking Scene is compatible with BMNFTs:
|
||||
Checks.check_Scene()
|
||||
|
||||
# Messages:
|
||||
print(
|
||||
f"\n========================================\n"
|
||||
f"Creating NFT Data. Generating {maxNFTs} NFT DNA.\n"
|
||||
f"Creating NFT Data. Generating {collectionSize} NFT DNA.\n"
|
||||
)
|
||||
|
||||
if not enableRarity and not enableLogic:
|
||||
|
@ -282,17 +238,21 @@ def send_To_Record_JSON(maxNFTs, nftsPerBatch, save_path, enableRarity, enableLo
|
|||
print(f"{bcolors.OK}Logic is ON. Rules listed in {logicFile} will be taken into account.\n{bcolors.RESET}")
|
||||
|
||||
time_start = time.time()
|
||||
|
||||
def create_nft_data():
|
||||
try:
|
||||
DataDictionary, possibleCombinations = generateNFT_DNA(maxNFTs, nftsPerBatch, logicFile, enableRarity, enableLogic)
|
||||
DataDictionary = generateNFT_DNA(collectionSize, logicFile, enableRarity, enableLogic)
|
||||
NFTRecord_save_path = os.path.join(Blend_My_NFTs_Output, "NFTRecord.json")
|
||||
|
||||
# Checks:
|
||||
Checks.raise_Warning_maxNFTs(nftsPerBatch, maxNFTs)
|
||||
|
||||
Checks.check_Rarity(DataDictionary["hierarchy"], DataDictionary["DNAList"], os.path.join(save_path, "Blend_My_NFTs Output/NFT_Data"))
|
||||
|
||||
Checks.raise_Warning_maxNFTs(nftsPerBatch, collectionSize)
|
||||
Checks.check_Duplicates(DataDictionary["DNAList"])
|
||||
Checks.raise_Error_ZeroCombinations()
|
||||
|
||||
if enableRarity:
|
||||
Checks.check_Rarity(DataDictionary["hierarchy"], DataDictionary["DNAList"], os.path.join(save_path, "Blend_My_NFTs Output/NFT_Data"))
|
||||
|
||||
except FileNotFoundError:
|
||||
raise FileNotFoundError(
|
||||
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
|
||||
|
@ -301,8 +261,6 @@ def send_To_Record_JSON(maxNFTs, nftsPerBatch, save_path, enableRarity, enableLo
|
|||
f"see:\n{bcolors.RESET}"
|
||||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
|
||||
)
|
||||
else:
|
||||
print("Something unexpected happened. Please check Blender System Console Traceback error for more information.")
|
||||
finally:
|
||||
loading.stop()
|
||||
|
||||
|
@ -324,8 +282,6 @@ def send_To_Record_JSON(maxNFTs, nftsPerBatch, save_path, enableRarity, enableLo
|
|||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
|
||||
)
|
||||
|
||||
|
||||
|
||||
# Loading Animation:
|
||||
loading = Loader(f'Creating NFT DNA...', '').start()
|
||||
create_nft_data()
|
||||
|
|
|
@ -99,10 +99,21 @@ def reconstructDNA(deconstructedDNA):
|
|||
reconstructed_DNA += num
|
||||
return (''.join(reconstructed_DNA.split('-', 1)))
|
||||
|
||||
def strip_empty_variant(num_list):
|
||||
for i in num_list:
|
||||
var_list = num_list[i]
|
||||
var_list.remove("0")
|
||||
num_list[i] = var_list
|
||||
return num_list
|
||||
|
||||
# Rule Checks:
|
||||
def never_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
|
||||
"""Returns True if singleDNA violates Never with Rule stated in Logic.json."""
|
||||
violates_rule = None
|
||||
|
||||
num_List1 = strip_empty_variant(num_List1)
|
||||
num_List2 = strip_empty_variant(num_List2)
|
||||
|
||||
for a in num_List1:
|
||||
for b in num_List2:
|
||||
if str(deconstructed_DNA[getAttIndex(hierarchy, a)]) in num_List1[a] and \
|
||||
|
@ -116,6 +127,7 @@ def never_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
|
|||
def only_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
|
||||
"""Returns True if singleDNA violates Only with Rule stated in Logic.json."""
|
||||
violates_rule = None
|
||||
|
||||
for a in num_List1:
|
||||
for b in num_List2:
|
||||
if str(deconstructed_DNA[getAttIndex(hierarchy, a)]) in num_List1[a] and \
|
||||
|
@ -130,6 +142,7 @@ def only_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
|
|||
def always_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
|
||||
"""Returns True if singleDNA violates Always with Rule stated in Logic.json."""
|
||||
violates_rule = None
|
||||
|
||||
for a in num_List2:
|
||||
if str(deconstructed_DNA[getAttIndex(hierarchy, a)]) not in num_List2[a]:
|
||||
violates_rule = True
|
||||
|
@ -147,7 +160,6 @@ def logicafyDNAsingle(hierarchy, singleDNA, logicFile):
|
|||
|
||||
didReconstruct = True
|
||||
originalDNA = str(singleDNA)
|
||||
resultDNA = str(singleDNA)
|
||||
|
||||
while didReconstruct:
|
||||
didReconstruct = False
|
||||
|
@ -168,6 +180,12 @@ def logicafyDNAsingle(hierarchy, singleDNA, logicFile):
|
|||
if not rand_bool:
|
||||
deconstructed_DNA = rar_selectVar(hierarchy, items_List1, deconstructed_DNA)
|
||||
|
||||
newDNA = reconstructDNA(deconstructed_DNA)
|
||||
if newDNA != originalDNA:
|
||||
originalDNA = str(newDNA)
|
||||
didReconstruct = True
|
||||
break
|
||||
|
||||
if logicFile[rule]["Rule-Type"] == "Only with":
|
||||
if only_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
|
||||
for b in num_List1:
|
||||
|
@ -177,15 +195,20 @@ def logicafyDNAsingle(hierarchy, singleDNA, logicFile):
|
|||
if "0" not in num_List1[b]: # Not complete attribute, select from other variants with rarity:
|
||||
deconstructed_DNA = rar_selectVar(hierarchy, items_List1, deconstructed_DNA)
|
||||
|
||||
newDNA = reconstructDNA(deconstructed_DNA)
|
||||
if newDNA != originalDNA:
|
||||
originalDNA = str(newDNA)
|
||||
didReconstruct = True
|
||||
break
|
||||
|
||||
if logicFile[rule]["Rule-Type"] == "Always with":
|
||||
if always_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
|
||||
deconstructed_DNA = rar_selectVar(hierarchy, items_List1, deconstructed_DNA)
|
||||
|
||||
newDNA = reconstructDNA(deconstructed_DNA)
|
||||
if newDNA != originalDNA:
|
||||
originalDNA = str(newDNA)
|
||||
didReconstruct = True
|
||||
resultDNA = str(newDNA)
|
||||
break
|
||||
newDNA = reconstructDNA(deconstructed_DNA)
|
||||
if newDNA != originalDNA:
|
||||
originalDNA = str(newDNA)
|
||||
didReconstruct = True
|
||||
break
|
||||
|
||||
return resultDNA
|
||||
return str(reconstructDNA(deconstructed_DNA))
|
|
@ -1,6 +1,6 @@
|
|||
import bpy
|
||||
import re
|
||||
import copy
|
||||
|
||||
from . import DNA_Generator
|
||||
|
||||
class bcolors:
|
||||
"""
|
||||
|
@ -11,169 +11,25 @@ class bcolors:
|
|||
ERROR = '\033[91m' # RED
|
||||
RESET = '\033[0m' # RESET COLOR
|
||||
|
||||
def stripColorFromName(name):
|
||||
return "_".join(name.split("_")[:-1])
|
||||
|
||||
def get_combinations_from_scene():
|
||||
def get_combinations():
|
||||
"""
|
||||
Generates important variables, dictionaries, and lists needed to be stored to catalog the NFTs.
|
||||
:return: listAllCollections, attributeCollections, attributeCollections1, hierarchy, variantMetaData, possibleCombinations
|
||||
Returns "combinations", the number of all possible NFT DNA for a given Blender scene formatted to BMNFTs conventions
|
||||
combinations.
|
||||
"""
|
||||
|
||||
coll = bpy.context.scene.collection
|
||||
hierarchy = DNA_Generator.get_hierarchy()
|
||||
hierarchyByNum = []
|
||||
|
||||
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}")
|
||||
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}")
|
||||
|
||||
listAllCollInScene = []
|
||||
listAllCollections = []
|
||||
combinations = 1
|
||||
for i in hierarchyByNum:
|
||||
combinations = combinations*i
|
||||
|
||||
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)
|
||||
|
||||
for i in listAllCollInScene:
|
||||
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]
|
||||
rarity = orderRarity[1]
|
||||
|
||||
eachObject = {"name": name, "number": number, "rarity": rarity}
|
||||
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:
|
||||
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)
|
||||
|
||||
return possibleCombinations
|
||||
return combinations
|
||||
|
|
Ładowanie…
Reference in New Issue