Reformatting dependencies

- Deprecated Rarity.py, moved to Helpers to avoid circular import issues
- Moved get_hierarchy() function from DNA_Generator.py to Helpers to avoid circular import issues
- Reformatted some comments and example code
pull/142/head
Torrin Leonard 2022-08-24 09:57:59 -04:00
rodzic 5e353e42b6
commit 4a939bb9e6
6 zmienionych plików z 182 dodań i 188 usunięć

Wyświetl plik

@ -44,7 +44,6 @@ from main import \
Logic, \ Logic, \
Material_Generator, \ Material_Generator, \
Metadata, \ Metadata, \
Rarity, \
Refactorer Refactorer
from UILists import \ from UILists import \
@ -61,7 +60,6 @@ if "bpy" in locals():
"Logic": Logic, "Logic": Logic,
"Material_Generator": Material_Generator, "Material_Generator": Material_Generator,
"Metadata": Metadata, "Metadata": Metadata,
"Rarity": Rarity,
"Refactorer": Refactorer, "Refactorer": Refactorer,
"Custom_Metadata_UIList": Custom_Metadata_UIList, "Custom_Metadata_UIList": Custom_Metadata_UIList,
"Logic_UIList": Logic_UIList, "Logic_UIList": Logic_UIList,
@ -547,7 +545,7 @@ class resume_failed_batch(bpy.types.Operator):
file_name = os.path.join(_batch_json_save_path, "Batch{}.json".format(_batchToGenerate)) file_name = os.path.join(_batch_json_save_path, "Batch{}.json".format(_batchToGenerate))
batchData = json.load(open(file_name)) batchData = json.load(open(file_name))
_fail_state, _failed_batch, _failed_dna, _failed_dna_index = Checks.check_FailedBatches(_batch_json_save_path) _fail_state, _failed_batch, _failed_dna, _failed_dna_index = Helpers.check_FailedBatches(_batch_json_save_path)
render_settings = batchData["Generation Save"][-1]["Render_Settings"] render_settings = batchData["Generation Save"][-1]["Render_Settings"]
@ -887,7 +885,7 @@ class BMNFTS_PT_GenerateNFTs(bpy.types.Panel):
batch_json_save_path = os.path.join(Blend_My_NFTs_Output, "Batch_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") 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) fail_state, failed_batch, failed_dna, failed_dna_index = Helpers.check_FailedBatches(batch_json_save_path)
if fail_state: if fail_state:
row = layout.row() row = layout.row()

Wyświetl plik

@ -3,123 +3,11 @@
import bpy import bpy
import os import os
import copy
import time import time
import json import json
import random import random
from functools import partial from functools import partial
from . import Rarity, Logic, Material_Generator, Helpers from . import Logic, Material_Generator, Helpers
from .Helpers import bcolors, Loader
def get_hierarchy():
"""
Returns the hierarchy of a given Blender scene.
"""
coll = bpy.context.scene.collection
scriptIgnoreCollection = bpy.data.collections["Script_Ignore"]
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)
for i in listAllCollInScene:
listAllCollections.append(i.name)
listAllCollections.remove(scriptIgnoreCollection.name)
if "Scene Collection" in listAllCollections:
listAllCollections.remove("Scene Collection")
if "Master Collection" in listAllCollections:
listAllCollections.remove("Master Collection")
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(scriptIgnoreCollection)
listAllCollections.sort()
exclude = ["_"] # Excluding characters that identify a Variant
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 = {}
for i in attributeVariants:
# Check if name follows naming conventions:
if int(i.count("_")) > 2 and int(i.split("_")[1]) > 0:
raise Exception(
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
f"There is a naming issue with the following Attribute/Variant: '{i}'\n"
f"Review the naming convention of Attribute and Variant collections here:\n{bcolors.RESET}"
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
)
try:
number = i.split("_")[1]
name = i.split("_")[0]
rarity = i.split("_")[2]
except IndexError:
raise Exception(
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
f"There is a naming issue with the following Attribute/Variant: '{i}'\n"
f"Review the naming convention of Attribute and Variant collections here:\n{bcolors.RESET}"
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
)
allAttDataList[i] = {"name": name, "number": number, "rarity": rarity}
return allAttDataList
variantMetaData = attributeData(attributeVariants)
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
def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enableMaterials, materialsFile, enable_debug): def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enableMaterials, materialsFile, enable_debug):
@ -127,7 +15,7 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
Returns batchDataDictionary containing the number of NFT combinations, hierarchy, and the DNAList. Returns batchDataDictionary containing the number of NFT combinations, hierarchy, and the DNAList.
""" """
hierarchy = get_hierarchy() hierarchy = Helpers.get_hierarchy()
# DNA random, Rarity and Logic methods: # DNA random, Rarity and Logic methods:
DataDictionary = {} DataDictionary = {}
@ -156,6 +44,48 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
return str(dna) return str(dna)
def createDNArarity(hierarchy):
"""
Sorts through DataDictionary and appropriately weights each variant based on their rarity percentage set in Blender
("rarity" in DNA_Generator). Then
"""
singleDNA = ""
for i in hierarchy:
number_List_Of_i = []
rarity_List_Of_i = []
ifZeroBool = None
for k in hierarchy[i]:
number = hierarchy[i][k]["number"]
number_List_Of_i.append(number)
rarity = hierarchy[i][k]["rarity"]
rarity_List_Of_i.append(float(rarity))
for x in rarity_List_Of_i:
if x == 0:
ifZeroBool = True
elif x != 0:
ifZeroBool = False
try:
if ifZeroBool:
variantByNum = random.choices(number_List_Of_i, k=1)
elif not ifZeroBool:
variantByNum = random.choices(number_List_Of_i, weights=rarity_List_Of_i, k=1)
except IndexError:
raise IndexError(
f"\n{Helpers.bcolors.ERROR}Blend_My_NFTs Error:\n"
f"An issue was found within the Attribute collection '{i}'. For more information on Blend_My_NFTs compatible scenes, "
f"see:\n{Helpers.bcolors.RESET}"
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
)
singleDNA += "-" + str(variantByNum[0])
singleDNA = ''.join(singleDNA.split('-', 1))
return singleDNA
def singleCompleteDNA(): def singleCompleteDNA():
""" """
This function applies Rarity and Logic to a single DNA created by createDNASingle() if Rarity or Logic specified This function applies Rarity and Logic to a single DNA created by createDNASingle() if Rarity or Logic specified
@ -167,7 +97,7 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
# print("============") # print("============")
# print(f"Original DNA: {singleDNA}") # print(f"Original DNA: {singleDNA}")
if enableRarity: if enableRarity:
singleDNA = Rarity.createDNArarity(hierarchy) singleDNA = createDNArarity(hierarchy)
# print(f"Rarity DNA: {singleDNA}") # print(f"Rarity DNA: {singleDNA}")
if enableLogic: if enableLogic:
@ -297,13 +227,13 @@ def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, e
if not enableRarity and not enableLogic: if not enableRarity and not enableLogic:
print( print(
f"{bcolors.OK}NFT DNA will be determined randomly, no special properties or parameters are applied.\n{bcolors.RESET}") f"{Helpers.bcolors.OK}NFT DNA will be determined randomly, no special properties or parameters are applied.\n{Helpers.bcolors.RESET}")
if enableRarity: if enableRarity:
print(f"{bcolors.OK}Rarity is ON. Weights listed in .blend scene will be taken into account.\n{bcolors.RESET}") print(f"{Helpers.bcolors.OK}Rarity is ON. Weights listed in .blend scene will be taken into account.\n{Helpers.bcolors.RESET}")
if enableLogic: if enableLogic:
print(f"{bcolors.OK}Logic is ON. {len(list(logicFile.keys()))} rules detected and applied.\n{bcolors.RESET}") print(f"{Helpers.bcolors.OK}Logic is ON. {len(list(logicFile.keys()))} rules detected and applied.\n{Helpers.bcolors.RESET}")
time_start = time.time() time_start = time.time()
@ -325,10 +255,10 @@ def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, e
except FileNotFoundError: except FileNotFoundError:
raise FileNotFoundError( raise FileNotFoundError(
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n" f"\n{Helpers.bcolors.ERROR}Blend_My_NFTs Error:\n"
f"Data not saved to NFTRecord.json. Please review your Blender scene and ensure it follows " f"Data not saved to NFTRecord.json. Please review your Blender scene and ensure it follows "
f"the naming conventions and scene structure. For more information, " f"the naming conventions and scene structure. For more information, "
f"see:\n{bcolors.RESET}" f"see:\n{Helpers.bcolors.RESET}"
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n" f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
) )
finally: finally:
@ -340,20 +270,20 @@ def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, e
outfile.write(ledger + '\n') outfile.write(ledger + '\n')
print( print(
f"\n{bcolors.OK}Blend_My_NFTs Success:\n" f"\n{Helpers.bcolors.OK}Blend_My_NFTs Success:\n"
f"{len(DataDictionary['DNAList'])} NFT DNA saved to {NFTRecord_save_path}. NFT DNA Successfully created.\n{bcolors.RESET}") f"{len(DataDictionary['DNAList'])} NFT DNA saved to {NFTRecord_save_path}. NFT DNA Successfully created.\n{Helpers.bcolors.RESET}")
except: except:
raise ( raise (
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n" f"\n{Helpers.bcolors.ERROR}Blend_My_NFTs Error:\n"
f"Data not saved to NFTRecord.json. Please review your Blender scene and ensure it follows " f"Data not saved to NFTRecord.json. Please review your Blender scene and ensure it follows "
f"the naming conventions and scene structure. For more information, " f"the naming conventions and scene structure. For more information, "
f"see:\n{bcolors.RESET}" f"see:\n{Helpers.bcolors.RESET}"
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n" f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
) )
# Loading Animation: # Loading Animation:
loading = Loader(f'Creating NFT DNA...', '').start() loading = Helpers.Loader(f'Creating NFT DNA...', '').start()
create_nft_data() create_nft_data()
makeBatches(collectionSize, nftsPerBatch, save_path, batch_json_save_path) makeBatches(collectionSize, nftsPerBatch, save_path, batch_json_save_path)
loading.stop() loading.stop()
@ -361,5 +291,5 @@ def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, e
time_end = time.time() time_end = time.time()
print( print(
f"{bcolors.OK}Created and saved NFT DNA in {time_end - time_start}s.\n{bcolors.RESET}" f"{Helpers.bcolors.OK}Created and saved NFT DNA in {time_end - time_start}s.\n{Helpers.bcolors.RESET}"
) )

Wyświetl plik

@ -1,6 +1,6 @@
# Purpose: # Purpose:
# This file takes a given Batch created by DNA_Generator.py and tells blender to render the image or export a 3D model to # This file takes a given Batch created by DNA_Generator.py and tells blender to render the image or export a 3D model
# the NFT_Output folder. # to the NFT_Output folder.
import bpy import bpy
import os import os

Wyświetl plik

@ -1,6 +1,7 @@
import bpy import bpy
import os import os
import json import json
import copy
import platform import platform
from time import sleep from time import sleep
from itertools import cycle from itertools import cycle
@ -8,9 +9,6 @@ from threading import Thread
from shutil import get_terminal_size from shutil import get_terminal_size
from collections import Counter, defaultdict from collections import Counter, defaultdict
from . import DNA_Generator
# ======== ENABLE DEBUG ======== # # ======== ENABLE DEBUG ======== #
# This section is used for debugging, coding, or general testing purposes. # This section is used for debugging, coding, or general testing purposes.
@ -81,6 +79,121 @@ def save_result(result):
outfile.write(data + '\n') outfile.write(data + '\n')
# ======== GET COMBINATIONS ======== #
# This section retrieves the Scene hierarchy from the current Blender file.
def get_hierarchy():
"""
Returns the hierarchy of a given Blender scene.
"""
coll = bpy.context.scene.collection
scriptIgnoreCollection = bpy.data.collections["Script_Ignore"]
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)
for i in listAllCollInScene:
listAllCollections.append(i.name)
listAllCollections.remove(scriptIgnoreCollection.name)
if "Scene Collection" in listAllCollections:
listAllCollections.remove("Scene Collection")
if "Master Collection" in listAllCollections:
listAllCollections.remove("Master Collection")
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(scriptIgnoreCollection)
listAllCollections.sort()
exclude = ["_"] # Excluding characters that identify a Variant
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 = {}
for i in attributeVariants:
# Check if name follows naming conventions:
if int(i.count("_")) > 2 and int(i.split("_")[1]) > 0:
raise Exception(
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
f"There is a naming issue with the following Attribute/Variant: '{i}'\n"
f"Review the naming convention of Attribute and Variant collections here:\n{bcolors.RESET}"
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
)
try:
number = i.split("_")[1]
name = i.split("_")[0]
rarity = i.split("_")[2]
except IndexError:
raise Exception(
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
f"There is a naming issue with the following Attribute/Variant: '{i}'\n"
f"Review the naming convention of Attribute and Variant collections here:\n{bcolors.RESET}"
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
)
allAttDataList[i] = {"name": name, "number": number, "rarity": rarity}
return allAttDataList
variantMetaData = attributeData(attributeVariants)
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
# ======== GET COMBINATIONS ======== # # ======== GET COMBINATIONS ======== #
# This section is used to get the number of combinations for checks and the UI display # This section is used to get the number of combinations for checks and the UI display
@ -91,7 +204,7 @@ def get_combinations():
combinations. combinations.
""" """
hierarchy = DNA_Generator.get_hierarchy() hierarchy = get_hierarchy()
hierarchyByNum = [] hierarchyByNum = []
for i in hierarchy: for i in hierarchy:
@ -140,7 +253,7 @@ def check_Scene(): # Not complete
f"\nhttps://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}" f"\nhttps://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
) )
hierarchy = DNA_Generator.get_hierarchy() hierarchy = get_hierarchy()
collections = bpy.context.scene.collection collections = bpy.context.scene.collection
# attribute_naming_conventions # attribute_naming_conventions

Wyświetl plik

@ -194,7 +194,9 @@ def get_rule_break_type(hierarchy, deconstructed_DNA, if_dict, result_dict, resu
def create_dicts(hierarchy, rule_list_items, result_dict_type): def create_dicts(hierarchy, rule_list_items, result_dict_type):
# Example of output structure: """
Example of output structure:
structure = { structure = {
"attribute1": { "attribute1": {
"variant1": [ "variant1": [
@ -229,6 +231,7 @@ def create_dicts(hierarchy, rule_list_items, result_dict_type):
] ]
} }
} }
"""
items_returned = collections.defaultdict(dict) items_returned = collections.defaultdict(dict)
for a in rule_list_items: for a in rule_list_items:

Wyświetl plik

@ -1,50 +0,0 @@
# Purpose:
# This file sorts the Variants in DNA slots based on the rarity value set in the name.
import bpy
import random
from .Helpers import bcolors, removeList, remove_file_by_extension
def createDNArarity(hierarchy):
"""
Sorts through DataDictionary and appropriately weights each variant based on their rarity percentage set in Blender
("rarity" in DNA_Generator). Then
"""
singleDNA = ""
for i in hierarchy:
number_List_Of_i = []
rarity_List_Of_i = []
ifZeroBool = None
for k in hierarchy[i]:
number = hierarchy[i][k]["number"]
number_List_Of_i.append(number)
rarity = hierarchy[i][k]["rarity"]
rarity_List_Of_i.append(float(rarity))
for x in rarity_List_Of_i:
if x == 0:
ifZeroBool = True
elif x != 0:
ifZeroBool = False
try:
if ifZeroBool:
variantByNum = random.choices(number_List_Of_i, k=1)
elif not ifZeroBool:
variantByNum = random.choices(number_List_Of_i, weights=rarity_List_Of_i, k=1)
except IndexError:
raise IndexError(
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
f"An issue was found within the Attribute collection '{i}'. For more information on Blend_My_NFTs compatible scenes, "
f"see:\n{bcolors.RESET}"
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
)
singleDNA += "-" + str(variantByNum[0])
singleDNA = ''.join(singleDNA.split('-', 1))
return singleDNA