Merge pull request #135 from torrinworx/main

Merging latest main branch changes to Logic_v2
pull/136/head
Torrin Leonard 2022-08-10 22:22:39 -04:00 zatwierdzone przez GitHub
commit fe41ea36f5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
9 zmienionych plików z 837 dodań i 1073 usunięć

Wyświetl plik

@ -1,15 +1,15 @@
bl_info = {
"name": "Blend_My_NFTs",
"author": "Torrin Leonard, This Cozy Studio Inc",
"version": (4, 4, 0),
"version": (4, 0, 2),
"blender": (3, 2, 0),
"location": "View3D",
"description": "A free and opensource Blender add-on that enables you to create thousands of unique images, animations, and 3D models.",
"description": "An open source, free to use Blender add-on that enables you to create thousands of unique images, animations, and 3D models.",
"category": "Development",
}
BMNFTS_VERSION = "v4.4.0"
LAST_UPDATED = "2:25PM, June 18th, 2022"
BMNFTS_VERSION = "v4.0.2"
LAST_UPDATED = "8:19AM, May 31st, 2022"
# ======== Import handling ======== #
@ -23,9 +23,10 @@ import os
import sys
import json
import importlib
from datetime import datetime, timezone
from dataclasses import dataclass
from typing import Any
# "a little hacky bs" - Matthew TheBrochacho ;)
# "a little hacky bs" - matt159 ;)
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from main import \
@ -34,6 +35,7 @@ from main import \
Exporter, \
get_combinations, \
HeadlessUtil, \
Intermediate, \
loading_animation, \
Logic, \
Material_Generator, \
@ -53,6 +55,7 @@ if "bpy" in locals():
"get_combinations": get_combinations,
"HeadlessUtil": HeadlessUtil,
"loading_animation": loading_animation,
"Intermediate": Intermediate,
"Logic": Logic,
"Material_Generator": Material_Generator,
"Metadata": Metadata,
@ -71,8 +74,101 @@ if "bpy" in locals():
# Used for updating text and buttons in UI panels
combinations: int = 0
recommended_limit: int = 0
dt = datetime.now(timezone.utc).astimezone() # Date Time in UTC local
@dataclass
class BMNFTData:
nftName: str
save_path: str
nftsPerBatch: int
batchToGenerate: int
collectionSize: int
Blend_My_NFTs_Output: str
batch_json_save_path: str
nftBatch_save_path: str
enableImages: bool
imageFileFormat: str
enableAnimations: bool
animationFileFormat: str
enableModelsBlender: bool
modelFileFormat: str
enableCustomFields: bool
cardanoMetaDataBool: bool
solanaMetaDataBool: bool
erc721MetaData: bool
cardano_description: str
solana_description: str
erc721_description: str
enableMaterials: bool
materialsFile: str
enableLogic: bool
enable_Logic_Json: bool
logicFile: str
enableRarity: bool
custom_Fields: dict = None
fail_state: Any = False
failed_batch: Any = None
failed_dna: Any = None
failed_dna_index: Any = None
def __post_init__(self):
self.custom_Fields = {}
def getBMNFTData():
_save_path = bpy.path.abspath(bpy.context.scene.input_tool.save_path)
_Blend_My_NFTs_Output, _batch_json_save_path, _nftBatch_save_path = make_directories(_save_path)
data = BMNFTData (
nftName = bpy.context.scene.input_tool.nftName,
save_path = _save_path,
nftsPerBatch = bpy.context.scene.input_tool.nftsPerBatch,
batchToGenerate = bpy.context.scene.input_tool.batchToGenerate,
collectionSize = bpy.context.scene.input_tool.collectionSize,
enableRarity = bpy.context.scene.input_tool.enableRarity,
Blend_My_NFTs_Output = _Blend_My_NFTs_Output,
batch_json_save_path = _batch_json_save_path,
nftBatch_save_path = _nftBatch_save_path,
enableLogic = bpy.context.scene.input_tool.enableLogic,
enable_Logic_Json = bpy.context.scene.input_tool.enable_Logic_Json,
logicFile = bpy.context.scene.input_tool.logicFile,
enableImages = bpy.context.scene.input_tool.imageBool,
imageFileFormat = bpy.context.scene.input_tool.imageEnum,
enableAnimations = bpy.context.scene.input_tool.animationBool,
animationFileFormat = bpy.context.scene.input_tool.animationEnum,
enableModelsBlender = bpy.context.scene.input_tool.modelBool,
modelFileFormat = bpy.context.scene.input_tool.modelEnum,
enableCustomFields = bpy.context.scene.input_tool.enableCustomFields,
cardanoMetaDataBool = bpy.context.scene.input_tool.cardanoMetaDataBool,
solanaMetaDataBool = bpy.context.scene.input_tool.solanaMetaDataBool,
erc721MetaData = bpy.context.scene.input_tool.erc721MetaData,
cardano_description = bpy.context.scene.input_tool.cardano_description,
solana_description = bpy.context.scene.input_tool.solana_description,
erc721_description = bpy.context.scene.input_tool.erc721_description,
enableMaterials = bpy.context.scene.input_tool.enableMaterials,
materialsFile = bpy.path.abspath(bpy.context.scene.input_tool.materialsFile)
)
return data
@persistent
def Refresh_UI(dummy1, dummy2):
@ -127,6 +223,17 @@ def runAsHeadless():
For use when running from the command line.
"""
# force CUDA device usage with cycles renderer
cprefs = bpy.context.preferences.addons['cycles'].preferences
cprefs.compute_device_type = 'CUDA'
cprefs.get_devices()
print(cprefs.devices.keys())
for key in cprefs.devices.keys():
cprefs.devices[key].use = True
print('Using {} devices for rendering!'.format(cprefs.get_num_gpu_devices()))
def dumpSettings(settings):
output = (
f"nftName={ settings.nftName }\n"
@ -174,23 +281,25 @@ def runAsHeadless():
settings.save_path = pairs[3][1]
settings.enableRarity = pairs[4][1] == 'True'
settings.enableLogic = pairs[5][1] == 'True'
settings.imageBool = pairs[6][1] == 'True'
settings.imageEnum = pairs[7][1]
settings.animationBool = pairs[8][1] == 'True'
settings.animationEnum = pairs[9][1]
settings.modelBool = pairs[10][1] == 'True'
settings.modelEnum = pairs[11][1]
settings.batchToGenerate = int(pairs[12][1])
settings.cardanoMetaDataBool = pairs[13][1] == 'True'
settings.cardano_description = pairs[14][1]
settings.erc721MetaData = pairs[15][1] == 'True'
settings.erc721_description = pairs[16][1]
settings.solanaMetaDataBool = pairs[17][1] == 'True'
settings.solanaDescription = pairs[18][1]
settings.enableCustomFields = pairs[19][1] == 'True'
settings.customfieldsFile = pairs[20][1]
settings.enableMaterials = pairs[21][1] == 'True'
settings.materialsFile = pairs[22][1]
settings.enableLogicJson = pairs[6][1] == 'True'
settings.logicFile = pairs[7][1]
settings.imageBool = pairs[8][1] == 'True'
settings.imageEnum = pairs[9][1]
settings.animationBool = pairs[10][1] == 'True'
settings.animationEnum = pairs[11][1]
settings.modelBool = pairs[12][1] == 'True'
settings.modelEnum = pairs[13][1]
settings.batchToGenerate = int(pairs[14][1])
settings.cardanoMetaDataBool = pairs[15][1] == 'True'
settings.cardano_description = pairs[16][1]
settings.erc721MetaData = pairs[17][1] == 'True'
settings.erc721_description = pairs[18][1]
settings.solanaMetaDataBool = pairs[19][1] == 'True'
settings.solanaDescription = pairs[20][1]
settings.enableCustomFields = pairs[21][1] == 'True'
settings.customfieldsFile = pairs[22][1]
settings.enableMaterials = pairs[23][1] == 'True'
settings.materialsFile = pairs[24][1]
if args.save_path:
settings.save_path = args.save_path
@ -198,80 +307,19 @@ def runAsHeadless():
if args.batch_number:
settings.batchToGenerate = args.batch_number
# dumpSettings(settings)
# don't mind me, just copy-pasting code around...
if args.operation == 'create-dna':
nftName = settings.nftName
collectionSize = settings.collectionSize
nftsPerBatch = settings.nftsPerBatch
save_path = bpy.path.abspath(settings.save_path)
logicFile = bpy.path.abspath(settings.logicFile)
enableRarity = settings.enableRarity
enableLogic = settings.enableLogic
enableMaterials = settings.enableMaterials
materialsFile = settings.materialsFile
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
DNA_Generator.send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, enableMaterials,
materialsFile, Blend_My_NFTs_Output, batch_json_save_path)
elif args.operation == 'generate-nfts':
nftName = settings.nftName
save_path = bpy.path.abspath(settings.save_path)
batchToGenerate = settings.batchToGenerate
collectionSize = settings.collectionSize
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
input = getBMNFTData()
if args.batch_data_path:
batch_json_save_path = args.batch_data_path;
input.batch_json_save_path = args.batch_data_path
enableImages = settings.imageBool
imageFileFormat = settings.imageEnum
if args.operation == 'create-dna':
Intermediate.send_To_Record_JSON(input)
enableAnimations = settings.animationBool
animationFileFormat = settings.animationEnum
elif args.operation == 'generate-nfts':
Intermediate.render_and_save_NFTs(input)
enableModelsBlender = settings.modelBool
modelFileFormat = settings.modelEnum
enableMaterials = settings.enableMaterials
materialsFile = settings.materialsFile
# fail state variables, set to no fail due to resume_failed_batch() Operator in BMNFTS_PT_GenerateNFTs Panel
fail_state = False
failed_batch = None
failed_dna = None
failed_dna_index = None
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,
enableMaterials, materialsFile
)
elif args.operation == 'refactor-batches':
class refactorData:
save_path = bpy.path.abspath(settings.save_path)
custom_Fields_File = bpy.path.abspath(settings.customfieldsFile)
enableCustomFields = settings.enableCustomFields
cardanoMetaDataBool = settings.cardanoMetaDataBool
solanaMetaDataBool = settings.solanaMetaDataBool
erc721MetaData = settings.erc721MetaData
cardano_description = settings.cardano_description
solana_description = settings.solana_description
erc721_description = settings.erc721_description
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
Refactorer.reformatNFTCollection(refactorData)
Refactorer.reformatNFTCollection(input)
# ======== User input Property Group ======== #
@ -378,21 +426,6 @@ class BMNFTS_PGT_Input_Properties(bpy.types.PropertyGroup):
)
# Other Panel:
enableAutoSave: bpy.props.BoolProperty(name="Auto Save Before Generation", description="Automatically saves your Blender file when 'Generate NFTs & Create Metadata' button is clicked")
# Auto Shutodwn:
enableAutoShutdown: bpy.props.BoolProperty(name="Auto Shutdown", description="Automatically shuts down your computer after a Batch is finished Generating")
specify_timeBool: bpy.props.BoolProperty(name="Shutdown in a Given Amount of Time", description="Wait a given amount of time after a Batch is generated before Automatic Shutdown")
hours: bpy.props.IntProperty(default=0, min=0)
minutes: bpy.props.IntProperty(default=0, min=0)
# Send Batch Complete Email:
emailNotificationBool: bpy.props.BoolProperty(name="Email Notifications", description="Receive Email Notifications from Blender once a batch is finished generating")
sender_from: bpy.props.StringProperty(name="From", default="from@example.com")
email_password: bpy.props.StringProperty(name="Password", subtype='PASSWORD')
receiver_to: bpy.props.StringProperty(name="To", default="to@example.com")
# API Panel properties:
apiKey: bpy.props.StringProperty(name="API Key", subtype='PASSWORD')
@ -410,73 +443,15 @@ class createData(bpy.types.Operator):
name="Reverse Order")
def execute(self, context):
nftName = bpy.context.scene.input_tool.nftName
collectionSize = bpy.context.scene.input_tool.collectionSize
nftsPerBatch = bpy.context.scene.input_tool.nftsPerBatch
save_path = bpy.path.abspath(bpy.context.scene.input_tool.save_path)
enableRarity = bpy.context.scene.input_tool.enableRarity
enableLogic = bpy.context.scene.input_tool.enableLogic
enable_Logic_Json = bpy.context.scene.input_tool.enable_Logic_Json
logicFile = bpy.path.abspath(bpy.context.scene.input_tool.logicFile)
enableMaterials = bpy.context.scene.input_tool.enableMaterials
materialsFile = bpy.path.abspath(bpy.context.scene.input_tool.materialsFile)
# Handling Custom Fields UIList input:
if enableLogic:
if enable_Logic_Json and logicFile:
logicFile = json.load(open(logicFile))
input = getBMNFTData()
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
DNA_Generator.send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, enableMaterials,
materialsFile, Blend_My_NFTs_Output, batch_json_save_path)
if enable_Logic_Json and not logicFile:
if input.enableLogic:
if input.enable_Logic_Json and not input.logicFile:
self.report({'ERROR'}, f"No Logic.json file path set. Please set the file path to your Logic.json file.")
if not enable_Logic_Json:
scn = context.scene
if self.reverse_order:
logicFile = {}
num = 1
for i in range(scn.logic_fields_index, -1, -1):
item = scn.logic_fields[i]
Intermediate.send_To_Record_JSON(input)
item_list1 = item.item_list1
rule_type = item.rule_type
item_list2 = item.item_list2
logicFile[f"Rule-{num}"] = {
"Items-1": item_list1.split(','),
"Rule-Type": rule_type,
"Items-2": item_list2.split(',')
}
num += 1
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
DNA_Generator.send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, enableMaterials,
materialsFile, Blend_My_NFTs_Output, batch_json_save_path)
else:
logicFile = {}
num = 1
for item in scn.logic_fields:
item_list1 = item.item_list1
rule_type = item.rule_type
item_list2 = item.item_list2
logicFile[f"Rule-{num}"] = {
"Items-1": item_list1.split(','),
"Rule-Type": rule_type,
"Items-2": item_list2.split(',')
}
num += 1
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
DNA_Generator.send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, enableMaterials,
materialsFile, Blend_My_NFTs_Output, batch_json_save_path)
if not enableLogic:
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
DNA_Generator.send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, enableMaterials,
materialsFile, Blend_My_NFTs_Output, batch_json_save_path)
self.report({'INFO'}, f"NFT Data created!")
return {"FINISHED"}
@ -496,76 +471,10 @@ class exportNFTs(bpy.types.Operator):
def execute(self, context):
enableAutoSave = bpy.context.scene.input_tool.enableAutoSave
if enableAutoSave:
bpy.ops.wm.save_mainfile()
class input:
nftName = bpy.context.scene.input_tool.nftName
save_path = bpy.path.abspath(bpy.context.scene.input_tool.save_path)
batchToGenerate = bpy.context.scene.input_tool.batchToGenerate
collectionSize = bpy.context.scene.input_tool.collectionSize
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
enableImages = bpy.context.scene.input_tool.imageBool
imageFileFormat = bpy.context.scene.input_tool.imageEnum
enableAnimations = bpy.context.scene.input_tool.animationBool
animationFileFormat = bpy.context.scene.input_tool.animationEnum
enableModelsBlender = bpy.context.scene.input_tool.modelBool
modelFileFormat = bpy.context.scene.input_tool.modelEnum
enableCustomFields = bpy.context.scene.input_tool.enableCustomFields
custom_Fields = {}
cardanoMetaDataBool = bpy.context.scene.input_tool.cardanoMetaDataBool
solanaMetaDataBool = bpy.context.scene.input_tool.solanaMetaDataBool
erc721MetaData = bpy.context.scene.input_tool.erc721MetaData
cardano_description = bpy.context.scene.input_tool.cardano_description
solana_description = bpy.context.scene.input_tool.solana_description
erc721_description = bpy.context.scene.input_tool.erc721_description
enableMaterials = bpy.context.scene.input_tool.enableMaterials
materialsFile = bpy.path.abspath(bpy.context.scene.input_tool.materialsFile)
enableAutoShutdown = bpy.context.scene.input_tool.enableAutoShutdown
specify_timeBool = bpy.context.scene.input_tool.specify_timeBool
hours = bpy.context.scene.input_tool.hours
minutes = bpy.context.scene.input_tool.minutes
emailNotificationBool = bpy.context.scene.input_tool.emailNotificationBool
sender_from = bpy.context.scene.input_tool.sender_from
email_password = bpy.context.scene.input_tool.email_password
receiver_to = bpy.context.scene.input_tool.receiver_to
# fail state variables, set to no fail due to resume_failed_batch() Operator in BMNFTS_PT_GenerateNFTs Panel
fail_state = False
failed_batch = None
failed_dna = None
failed_dna_index = None
input = getBMNFTData()
# Handling Custom Fields UIList input:
if input.enableCustomFields:
scn = context.scene
if self.reverse_order:
for i in range(scn.custom_metadata_fields_index, -1, -1):
item = scn.custom_metadata_fields[i]
if item.field_name in list(input.custom_Fields.keys()):
raise ValueError(f"A duplicate of '{item.field_name}' was found. Please ensure all Custom Metadata field Names are unique.")
else:
input.custom_Fields[item.field_name] = item.field_value
else:
for item in scn.custom_metadata_fields:
if item.field_name in list(input.custom_Fields.keys()):
raise ValueError(f"A duplicate of '{item.field_name}' was found. Please ensure all Custom Metadata field Names are unique.")
else:
input.custom_Fields[item.field_name] = item.field_value
Exporter.render_and_save_NFTs(input)
Intermediate.render_and_save_NFTs(input)
self.report({'INFO'}, f"All NFTs generated for batch {input.batchToGenerate}!")
@ -579,42 +488,53 @@ class resume_failed_batch(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
class input:
save_path = bpy.path.abspath(bpy.context.scene.input_tool.save_path)
batchToGenerate = bpy.context.scene.input_tool.batchToGenerate
_save_path = bpy.path.abspath(bpy.context.scene.input_tool.save_path)
_Blend_My_NFTs_Output, _batch_json_save_path, _nftBatch_save_path = make_directories(_save_path)
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
file_name = os.path.join(batch_json_save_path, "Batch{}.json".format(batchToGenerate))
batch = json.load(open(file_name))
_batchToGenerate = bpy.context.scene.input_tool.batchToGenerate
nftName = batch["Generation Save"][-1]["Render_Settings"]["nftName"]
collectionSize = batch["Generation Save"][-1]["Render_Settings"]["collectionSize"]
nftBatch_save_path = batch["Generation Save"][-1]["Render_Settings"]["nftBatch_save_path"]
file_name = os.path.join(_batch_json_save_path, "Batch{}.json".format(_batchToGenerate))
batchData = json.load(open(file_name))
enableImages = batch["Generation Save"][-1]["Render_Settings"]["enableImages"]
imageFileFormat = batch["Generation Save"][-1]["Render_Settings"]["imageFileFormat"]
_fail_state, _failed_batch, _failed_dna, _failed_dna_index = Checks.check_FailedBatches(_batch_json_save_path)
enableAnimations = batch["Generation Save"][-1]["Render_Settings"]["enableAnimations"]
animationFileFormat = batch["Generation Save"][-1]["Render_Settings"]["animationFileFormat"]
input = BMNFTData (
nftName = batchData["Generation Save"][-1]["Render_Settings"]["nftName"],
save_path = _save_path,
collectionSize = batchData["Generation Save"][-1]["Render_Settings"]["collectionSize"],
enableModelsBlender = batch["Generation Save"][-1]["Render_Settings"]["enableModelsBlender"]
modelFileFormat = batch["Generation Save"][-1]["Render_Settings"]["modelFileFormat"]
Blend_My_NFTs_Output = _Blend_My_NFTs_Output,
batch_json_save_path = _batch_json_save_path,
nftBatch_save_path = batchData["Generation Save"][-1]["Render_Settings"]["nftBatch_save_path"],
enableCustomFields = batch["Generation Save"][-1]["Render_Settings"]["enableCustomFields"]
custom_Fields = batch["Generation Save"][-1]["Render_Settings"]["custom_Fields"]
enableImages = batchData["Generation Save"][-1]["Render_Settings"]["enableImages"],
imageFileFormat = batchData["Generation Save"][-1]["Render_Settings"]["imageFileFormat"],
cardanoMetaDataBool = batch["Generation Save"][-1]["Render_Settings"]["cardanoMetaDataBool"]
solanaMetaDataBool = batch["Generation Save"][-1]["Render_Settings"]["solanaMetaDataBool"]
erc721MetaData = batch["Generation Save"][-1]["Render_Settings"]["erc721MetaData"]
enableAnimations = batchData["Generation Save"][-1]["Render_Settings"]["enableAnimations"],
animationFileFormat = batchData["Generation Save"][-1]["Render_Settings"]["animationFileFormat"],
cardano_description = batch["Generation Save"][-1]["Render_Settings"]["cardano_description"]
solana_description = batch["Generation Save"][-1]["Render_Settings"]["solana_description"]
erc721_description = batch["Generation Save"][-1]["Render_Settings"]["erc721_description"]
enableModelsBlender = batchData["Generation Save"][-1]["Render_Settings"]["enableModelsBlender"],
modelFileFormat = batchData["Generation Save"][-1]["Render_Settings"]["modelFileFormat"],
enableMaterials = batch["Generation Save"][-1]["Render_Settings"]["enableMaterials"]
materialsFile = batch["Generation Save"][-1]["Render_Settings"]["materialsFile"]
enableCustomFields = batchData["Generation Save"][-1]["Render_Settings"]["enableCustomFields"],
custom_Fields = batchData["Generation Save"][-1]["Render_Settings"]["custom_Fields"],
fail_state, failed_batch, failed_dna, failed_dna_index = Checks.check_FailedBatches(batch_json_save_path)
cardanoMetaDataBool = batchData["Generation Save"][-1]["Render_Settings"]["cardanoMetaDataBool"],
solanaMetaDataBool = batchData["Generation Save"][-1]["Render_Settings"]["solanaMetaDataBool"],
erc721MetaData = batchData["Generation Save"][-1]["Render_Settings"]["erc721MetaData"],
cardano_description = batchData["Generation Save"][-1]["Render_Settings"]["cardano_description"],
solana_description = batchData["Generation Save"][-1]["Render_Settings"]["solana_description"],
erc721_description = batchData["Generation Save"][-1]["Render_Settings"]["erc721_description"],
enableMaterials = batchData["Generation Save"][-1]["Render_Settings"]["enableMaterials"],
materialsFile = batchData["Generation Save"][-1]["Render_Settings"]["materialsFile"],
fail_state = _fail_state,
failed_batch = _failed_batch,
failed_dna = _failed_dna,
failed_dna_index = _failed_dna_index
)
Exporter.render_and_save_NFTs(input)
@ -635,25 +555,8 @@ class refactor_Batches(bpy.types.Operator):
name="Reverse Order")
def execute(self, context):
class input:
save_path = bpy.path.abspath(bpy.context.scene.input_tool.save_path)
enableCustomFields = bpy.context.scene.input_tool.enableCustomFields
custom_Fields = {}
cardanoMetaDataBool = bpy.context.scene.input_tool.cardanoMetaDataBool
solanaMetaDataBool = bpy.context.scene.input_tool.solanaMetaDataBool
erc721MetaData = bpy.context.scene.input_tool.erc721MetaData
cardano_description = bpy.context.scene.input_tool.cardano_description
solana_description = bpy.context.scene.input_tool.solana_description
erc721_description = bpy.context.scene.input_tool.erc721_description
Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path)
# Passing info to main functions for refactoring:
Refactorer.reformatNFTCollection(input)
Refactorer.reformatNFTCollection(getBMNFTData())
return {"FINISHED"}
def invoke(self, context, event):
@ -695,6 +598,8 @@ class export_settings(bpy.types.Operator):
"\n"
"#Enable Logic\n"
f"enableLogic={ str(settings.enableLogic) }\n"
f"enableLogicJson={ str(settings.enable_Logic_Json) }\n"
f"logicFilePath={ settings.logicFile }\n"
"\n"
"#NFT Media output type(s):\n"
f"imageBool={ str(settings.imageBool) }\n"
@ -956,57 +861,10 @@ class BMNFTS_PT_Other(bpy.types.Panel):
input_tool_scene = scene.input_tool
"""
Other:
A place to store miscellaneous settings, features, and external links that the user may find useful but doesn't
want to get in the way of their work flow.
Export Settings:
This panel gives the user the option to export all settings from the Blend_My_NFTs addon into a config file. Settings
will be read from the config file when running heedlessly.
"""
row = layout.row()
row.prop(input_tool_scene, "enableAutoSave")
# Auto Shutdown:
row = layout.row()
row.prop(input_tool_scene, "enableAutoShutdown")
row.label(text="*Must Run Blender as Admin")
if bpy.context.scene.input_tool.enableAutoShutdown:
row = layout.row()
row.prop(input_tool_scene, "specify_timeBool")
time_row1 = layout.row()
time_row1.label(text=f"Hours")
time_row1.prop(input_tool_scene, "hours", text="")
time_row2 = layout.row()
time_row2.label(text=f"Minutes")
time_row2.prop(input_tool_scene, "minutes", text="")
if not bpy.context.scene.input_tool.specify_timeBool:
time_row1.enabled = False
time_row2.enabled = False
else:
time_row1.enabled = True
time_row2.enabled = True
layout.separator()
row = layout.row()
row.prop(input_tool_scene, "emailNotificationBool")
row.label(text="*Windows 10+ only")
if bpy.context.scene.input_tool.emailNotificationBool:
row = layout.row()
row.prop(input_tool_scene, "sender_from")
row = layout.row()
row.prop(input_tool_scene, "email_password")
layout.separator()
row = layout.row()
row.prop(input_tool_scene, "receiver_to")
layout.separator()
layout.label(text=f"Running Blend_My_NFTs Headless:")
save_path = bpy.path.abspath(bpy.context.scene.input_tool.save_path)

Wyświetl plik

@ -2,8 +2,7 @@
# This file is for storing or updating constant values that may need to be changes depending on system requirements and
# different usecases.
import os
import json
import platform
removeList = [".gitignore", ".DS_Store", "desktop.ini", ".ini"]
@ -35,18 +34,3 @@ class bcolors:
ERROR = '\033[91m' # RED
RESET = '\033[0m' # RESET COLOR
def save_result(result):
"""
Saves json result to json file at the specified path.
"""
file_name = "log.json"
if platform.system() == "Linux" or platform.system() == "Darwin":
path = os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop', file_name)
if platform.system() == "Windows":
path = os.path.join(os.environ["HOMEPATH"], "Desktop", file_name)
data = json.dumps(result, indent=1, ensure_ascii=True)
with open(path, 'w') as outfile:
outfile.write(data + '\n')

Wyświetl plik

@ -82,7 +82,38 @@ def get_hierarchy():
allAttDataList = {}
for i in attributeVariants:
# Check if name follows naming conventions:
if int(i.count("_")) > 2 and int(i.split("_")[1]) > 0:
if i.count("_") > 2:
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"
)
def getName(i):
"""
Returns the name of "i" attribute variant
"""
name = i.split("_")[0]
return name
def getOrder_rarity(i):
"""
Returns the "order" and "rarity" (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)
try:
number = orderRarity[0]
except:
raise Exception(
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
f"There is a naming issue with the following Attribute/Variant: '{i}'\n"
@ -91,10 +122,8 @@ def get_hierarchy():
)
try:
number = i.split("_")[1]
name = i.split("_")[0]
rarity = i.split("_")[2]
except IndexError:
rarity = orderRarity[1]
except:
raise Exception(
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
f"There is a naming issue with the following Attribute/Variant: '{i}'\n"
@ -102,7 +131,8 @@ def get_hierarchy():
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
)
allAttDataList[i] = {"name": name, "number": number, "rarity": rarity}
eachObject = {"name": name, "number": number, "rarity": rarity}
allAttDataList[i] = eachObject
return allAttDataList
variantMetaData = attributeData(attributeVariants)
@ -123,7 +153,6 @@ def get_hierarchy():
return hierarchy
def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enableMaterials, materialsFile):
"""
Returns batchDataDictionary containing the number of NFT combinations, hierarchy, and the DNAList.
@ -134,7 +163,7 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
# DNA random, Rarity and Logic methods:
DataDictionary = {}
def createDNArandom(hierarchy):
def createDNArandom():
"""Creates a single DNA randomly without Rarity or Logic."""
dnaStr = ""
dnaStrList = []
@ -159,28 +188,24 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
return str(dna)
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"""
singleDNA = ""
# Comments for debugging random, rarity, logic, and materials.
if not enableRarity:
singleDNA = createDNArandom(hierarchy)
singleDNA = createDNArandom()
# print("============")
# print(f"Original DNA: {singleDNA}")
if enableRarity:
singleDNA = Rarity.createDNArarity(hierarchy)
# print(f"Rarity DNA: {singleDNA}")
if enableLogic:
singleDNA = Logic.logicafyDNAsingle(hierarchy, singleDNA, logicFile, enableRarity, enableMaterials)
# print(f"Logic DNA: {singleDNA}")
singleDNA = Logic.logicafyDNAsingle(hierarchy, singleDNA, logicFile)
# print(f"Original DNA: {singleDNA}")
# print("============\n")
if enableMaterials:
singleDNA = Material_Generator.apply_materials(hierarchy, singleDNA, materialsFile, enableRarity)
singleDNA = Material_Generator.apply_materials(hierarchy, singleDNA, materialsFile)
# print(f"Materials DNA: {singleDNA}")
# print("============\n")
return singleDNA
@ -223,7 +248,6 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
return DataDictionary
def makeBatches(collectionSize, nftsPerBatch, save_path, batch_json_save_path):
"""
Sorts through all the batches and outputs a given number of batches depending on collectionSize and nftsPerBatch.
@ -279,7 +303,6 @@ def makeBatches(collectionSize, nftsPerBatch, save_path, batch_json_save_path):
with open(os.path.join(batch_json_save_path, f"Batch{i + 1}.json"), "w") as outfile:
outfile.write(batchDictionary)
def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, enableMaterials,
materialsFile, Blend_My_NFTs_Output, batch_json_save_path):
"""
@ -299,14 +322,13 @@ def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, e
)
if not enableRarity and not enableLogic:
print(
f"{bcolors.OK}NFT DNA will be determined randomly, no special properties or parameters are applied.\n{bcolors.RESET}")
print(f"{bcolors.OK}NFT DNA will be determined randomly, no special properties or parameters are applied.\n{bcolors.RESET}")
if enableRarity:
print(f"{bcolors.OK}Rarity is ON. Weights listed in .blend scene will be taken into account.\n{bcolors.RESET}")
if enableLogic:
print(f"{bcolors.OK}Logic is ON. {len(list(logicFile.keys()))} rules detected and applied.\n{bcolors.RESET}")
print(f"{bcolors.OK}Logic is ON. Rules listed in {logicFile} will be taken into account.\n{bcolors.RESET}")
time_start = time.time()
@ -323,8 +345,7 @@ def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, e
Checks.raise_Error_ZeroCombinations()
if enableRarity:
Checks.check_Rarity(DataDictionary["hierarchy"], DataDictionary["DNAList"],
os.path.join(save_path, "Blend_My_NFTs Output/NFT_Data"))
Checks.check_Rarity(DataDictionary["hierarchy"], DataDictionary["DNAList"], os.path.join(save_path, "Blend_My_NFTs Output/NFT_Data"))
except FileNotFoundError:
raise FileNotFoundError(

Wyświetl plik

@ -4,12 +4,9 @@
import bpy
import os
import ssl
import time
import json
import smtplib
import datetime
import platform
from .loading_animation import Loader
from .Constants import bcolors, removeList, remove_file_by_extension
from .Metadata import createCardanoMetadata, createSolanaMetaData, createErc721MetaData
@ -115,20 +112,18 @@ def render_and_save_NFTs(input):
Renders the NFT DNA in a Batch#.json, where # is renderBatch in config.py. Turns off the viewport camera and
the render camera for all items in hierarchy.
"""
print(f"\nFAILED BATCH = {input.failed_batch}\n")
print(f"\nBATCH TO GENERATE = {input.batchToGenerate}\n")
time_start_1 = time.time()
# If failed Batch is detected and user is resuming its generation:
if input.fail_state:
print(f"{bcolors.ERROR}\nResuming Failed Batch {input.failed_batch}\n{bcolors.RESET}")
NFTs_in_Batch, hierarchy, BatchDNAList = getBatchData(input.failed_batch, input.batch_json_save_path)
for a in range(input.failed_dna):
del BatchDNAList[0]
x = input.failed_dna + 1
# If user is generating the normal way:
else:
print(f"\nGenerating Batch {input.batchToGenerate}\n")
NFTs_in_Batch, hierarchy, BatchDNAList = getBatchData(input.batchToGenerate, input.batch_json_save_path)
save_generation_state(input)
x = 1
@ -182,14 +177,12 @@ def render_and_save_NFTs(input):
if hierarchy[attribute][var]['number'] == variant:
variant = var
if material != '0': # If material is not empty
if material != '0':
for variant_m in materialsFile:
if variant == variant_m:
# Getting Materials name from Materials index in the Materials List
materials_list = list(materialsFile[variant_m]["Material List"].keys())
material = materials_list[int(material) - 1] # Subtract 1 because '0' means empty mat
break
for mat in materialsFile[variant_m]["Material List"]:
if mat.split('_')[1] == material:
material = mat
full_dna_dict[variant] = material
@ -420,13 +413,13 @@ def render_and_save_NFTs(input):
if not os.path.exists(solanaMetadataPath):
os.makedirs(solanaMetadataPath)
createSolanaMetaData(name, Order_Num, full_single_dna, dnaDictionary, metadataMaterialDict, input.custom_Fields,
input.enableCustomFields, input.solana_description, solanaMetadataPath)
input.enableCustomFields, input.cardano_description, solanaMetadataPath)
if input.erc721MetaData:
if not os.path.exists(erc721MetadataPath):
os.makedirs(erc721MetadataPath)
createErc721MetaData(name, Order_Num, full_single_dna, dnaDictionary, metadataMaterialDict, input.custom_Fields,
input.enableCustomFields, input.erc721_description, erc721MetadataPath)
input.enableCustomFields, input.cardano_description, erc721MetadataPath)
if not os.path.exists(BMNFT_metaData_Folder):
os.makedirs(BMNFT_metaData_Folder)
@ -464,64 +457,3 @@ def render_and_save_NFTs(input):
batch_infoFolder = os.path.join(input.nftBatch_save_path, "Batch" + str(input.batchToGenerate), "batch_info.json")
save_batch(batch_info, batch_infoFolder)
# Send Email that Batch is complete:
if input.emailNotificationBool:
port = 465 # For SSL
smtp_server = "smtp.gmail.com"
sender_email = input.sender_from # Enter your address
receiver_email = input.receiver_to # Enter receiver address
password = input.email_password
# Get batch info for message:
if input.fail_state:
batch = input.fail_state
batchData = getBatchData(input.failed_batch, input.batch_json_save_path)
else:
batchData = getBatchData(input.batchToGenerate, input.batch_json_save_path)
batch = input.batchToGenerate
generation_time = str(datetime.timedelta(seconds=batch_complete_time))
message = f"""\
Subject: Batch {batch} completed {x - 1} NFTs in {generation_time} (h:m:s)
Generation Time:
{generation_time.split(':')[0]} Hours, {generation_time.split(':')[1]} Minutes, {generation_time.split(':')[2]} Seconds
Batch Data:
{batchData}
This message was sent from an instance of the Blend_My_NFTs Blender add-on.
"""
context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
# Automatic Shutdown:
# If user selects automatic shutdown but did not specify time after Batch completion
def shutdown(time):
plateform = platform.system()
if plateform == "Windows":
os.system(f"shutdown /s /t {time}")
if plateform == "Darwin":
os.system(f"shutdown /s /t {time}")
if input.enableAutoShutdown and not input.specify_timeBool:
shutdown(0)
# If user selects automatic shutdown and specify time after Batch completion
if input.enableAutoShutdown and input.specify_timeBool:
hours = (int(input.hours)/60)/60
minutes = int(input.minutes)/60
total_sleep_time = hours + minutes
# time.sleep(total_sleep_time)
shutdown(total_sleep_time)

Wyświetl plik

@ -57,4 +57,11 @@ def getPythonArgs():
help="Use pre-existing batch data for rendering"
)
parser.add_argument("--logic-file",
dest="logic_file",
metavar='FILE',
required=False,
help="Overwrite the logic file path in the config file"
)
return (parser.parse_args(argv), parser)

Wyświetl plik

@ -0,0 +1,76 @@
import json
import bpy
from main import DNA_Generator, Exporter
def send_To_Record_JSON(input, reverse_order=False):
if input.enableLogic:
if input.enable_Logic_Json and input.logicFile:
input.logicFile = json.load(open(input.logicFile))
if input.enable_Logic_Json and not input.logicFile:
print({'ERROR'}, f"No Logic.json file path set. Please set the file path to your Logic.json file.")
if not input.enable_Logic_Json:
scn = bpy.context.scene
if reverse_order:
input.logicFile = {}
num = 1
for i in range(scn.logic_fields_index, -1, -1):
item = scn.logic_fields[i]
item_list1 = item.item_list1
rule_type = item.rule_type
item_list2 = item.item_list2
input.logicFile[f"Rule-{num}"] = {
"Items-1": item_list1.split(','),
"Rule-Type": rule_type,
"Items-2": item_list2.split(',')
}
num += 1
else:
input.logicFile = {}
num = 1
for item in scn.logic_fields:
item_list1 = item.item_list1
rule_type = item.rule_type
item_list2 = item.item_list2
input.logicFile[f"Rule-{num}"] = {
"Items-1": item_list1.split(','),
"Rule-Type": rule_type,
"Items-2": item_list2.split(',')
}
num += 1
DNA_Generator.send_To_Record_JSON( input.collectionSize,
input.nftsPerBatch,
input.save_path,
input.enableRarity,
input.enableLogic,
input.logicFile,
input.enableMaterials,
input.materialsFile,
input.Blend_My_NFTs_Output,
input.batch_json_save_path
)
def render_and_save_NFTs(input, reverse_order=False):
if input.enableCustomFields:
scn = bpy.context.scene
if reverse_order:
for i in range(scn.custom_metadata_fields_index, -1, -1):
item = scn.custom_metadata_fields[i]
if item.field_name in list(input.custom_Fields.keys()):
raise ValueError(f"A duplicate of '{item.field_name}' was found. Please ensure all Custom Metadata field Names are unique.")
else:
input.custom_Fields[item.field_name] = item.field_value
else:
for item in scn.custom_metadata_fields:
if item.field_name in list(input.custom_Fields.keys()):
raise ValueError(f"A duplicate of '{item.field_name}' was found. Please ensure all Custom Metadata field Names are unique.")
else:
input.custom_Fields[item.field_name] = item.field_value
Exporter.render_and_save_NFTs(input)

Wyświetl plik

@ -2,281 +2,207 @@
# The purpose of this file is to add logic and rules to the DNA that are sent to the NFTRecord.json file in DNA_Generator.py
import bpy
import json
import random
import collections
from .Constants import bcolors, removeList, remove_file_by_extension, save_result
# Helper Functions
def isAttorVar(hierarchy, items_List):
items_returned = collections.defaultdict(list)
for i in items_List:
for j in hierarchy:
if i == j: # If i is an Attribute, add all i Variants to dictionary.
items_returned[i] = list(hierarchy[j].keys())
items_returned[i].append("Empty")
if i in list(hierarchy[j].keys()):
items_returned[j].append(i)
# Check if all variants in an attribute were included, if so, add "Empty" variant.
for i in items_returned:
if list(items_returned[i]) == list(hierarchy[i].keys()):
items_returned[i].append("Empty")
return dict(items_returned)
def getAttIndex(hierarchy, attribute):
attList = list(hierarchy.keys())
index = attList.index(attribute)
return index
def getVarNum(variant):
if variant == "Empty":
num = '0'
else:
num = variant.split("_")[1]
return num
def items_to_num(items_List):
num_List = {}
for i in items_List:
variant_num_list = []
for j in items_List[i]:
variant_num_list.append(getVarNum(j))
num_List[i] = variant_num_list
return num_List
def rar_selectVar(hierarchy, items_List, deconstructed_DNA):
for attribute in items_List:
a_attribute_index = getAttIndex(hierarchy, attribute)
selected_variants = items_List[attribute]
hierarchy_selected_variants = list(hierarchy[attribute])
left_over_variants = [x for x in hierarchy_selected_variants if x not in selected_variants]
if not left_over_variants:
deconstructed_DNA[int(a_attribute_index)] = "0"
else:
number_List_Of_i = []
rarity_List_Of_i = []
ifZeroBool = None
variantNum = None
for a in left_over_variants:
number = a.split("_")[1]
rarity = a.split("_")[2]
number_List_Of_i.append(int(number))
rarity_List_Of_i.append(float(rarity))
for x in rarity_List_Of_i:
if x == 0:
ifZeroBool = True
elif x != 0:
ifZeroBool = False
if ifZeroBool:
variantNum = random.choices(number_List_Of_i, k=1)
if not ifZeroBool:
variantNum = random.choices(number_List_Of_i, weights=rarity_List_Of_i, k=1)
deconstructed_DNA[int(a_attribute_index)] = str(variantNum[0])
return deconstructed_DNA
def reconstructDNA(deconstructedDNA):
reconstructed_DNA = ""
for a in deconstructedDNA:
num = "-" + str(a)
reconstructed_DNA += num
return ''.join(reconstructed_DNA.split('-', 1))
return (''.join(reconstructed_DNA.split('-', 1)))
def get_var_info(variant, hierarchy):
# Get info for variant dict
name = variant.split("_")[0]
order_number = variant.split("_")[1]
rarity_number = variant.split("_")[2]
attribute = ""
def strip_empty_variant(num_list):
"""Strips empty variants if full attribute collection. Used for processing below."""
for i in num_list:
var_list = num_list[i]
if "0" in var_list:
var_list.remove("0")
num_list[i] = var_list
return num_list
for a in hierarchy:
for var in list(hierarchy[a].keys()):
if var == variant:
attribute = a
break
attribute_index = list(hierarchy.keys()).index(attribute)
# 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
return [name, order_number, rarity_number, attribute, attribute_index] # list of Var info sent back
num_List1 = strip_empty_variant(num_List1)
num_List2 = strip_empty_variant(num_List2)
def apply_rules_to_dna(hierarchy, deconstructed_DNA, if_dict, result_dict, result_dict_type, enableRarity):
# Check if Variants in if_dict are in deconstructed_DNA, if so return if_list_selected = True:
if_list_selected = False
for a in deconstructed_DNA:
attribute_index = deconstructed_DNA.index(a)
attribute = list(hierarchy.keys())[attribute_index]
for b in hierarchy[attribute]:
if hierarchy[attribute][b]["number"] == a:
a_dna_var = b
if attribute in if_dict:
if a_dna_var in list(if_dict[attribute].keys()):
if_list_selected = True
# Apply changes in accordance to Variants in 'result_dict' and 'if_list_selected' bool above:
for a in deconstructed_DNA:
attribute_index = deconstructed_DNA.index(a)
attribute = list(hierarchy.keys())[attribute_index]
if attribute in result_dict: # Check if Attribute from DNA is in 'result_dict'
# If 'a' is a full Attribute and Variants in if_dict not selected, set 'a' to empty (0):
if list(result_dict[attribute].keys()) == list(hierarchy[attribute].keys()) and not if_list_selected:
deconstructed_DNA[attribute_index] = "0"
# If 'a' is a full Attribute and result_dict_type = "NOT", set 'a' to empty (0):
if list(result_dict[attribute].keys()) == list(
hierarchy[attribute].keys()) and if_list_selected and result_dict_type == "NOT":
deconstructed_DNA[attribute_index] = "0"
# If Variants in if_dict are selected, set each attribute in 'result_dict' to a random or rarity selected Variant from
# 'result_dict[attribute]' variant_list:
if if_list_selected:
# Invert 'items_returned' if 'NOT' rule is selected:
if result_dict_type == "NOT":
for a in result_dict:
var_selected_list = list(result_dict[a].keys()) # list of variants from 'NOT'
att_selected_list = list(hierarchy[a].keys()) # full list of variants from hierarchy attribute
# If 'a' is not a full Attribute, invert the variants:
if len(var_selected_list) != len(att_selected_list):
var_selected_list = [i for i in att_selected_list if i not in var_selected_list]
var_selected_list_complete = {}
for i in var_selected_list:
var_selected_list_complete[i] = get_var_info(i, hierarchy)
result_dict[a] = var_selected_list_complete
for a in result_dict:
attribute_index = list(hierarchy.keys()).index(a)
attribute = list(hierarchy.keys())[attribute_index]
variant_list = list(result_dict[a].keys())
if attribute in result_dict: # Check if Attribute from DNA is in 'then_dict'
number_List_Of_i = []
rarity_List_Of_i = []
ifZeroBool = None
variantNum = None
for b in variant_list:
number = b.split("_")[1]
rarity = b.split("_")[2]
number_List_Of_i.append(int(number))
rarity_List_Of_i.append(float(rarity))
for b in rarity_List_Of_i:
if b == 0:
ifZeroBool = True
elif b != 0:
ifZeroBool = False
if enableRarity:
try:
if ifZeroBool:
variantNum = random.choices(number_List_Of_i, k=1)
elif not ifZeroBool:
variantNum = 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 '{a}'. 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"
)
for a in num_List1:
for b in num_List2:
if str(deconstructed_DNA[getAttIndex(hierarchy, a)]) in num_List1[a] and \
str(deconstructed_DNA[getAttIndex(hierarchy, b)]) in num_List2[b]:
violates_rule = True
return violates_rule
else:
try:
variantNum = random.choices(number_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 '{a}'. 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"
)
deconstructed_DNA[int(attribute_index)] = str(variantNum[0])
return deconstructed_DNA
def get_rule_break_type(hierarchy, deconstructed_DNA, if_dict, result_dict, result_dict_type):
# Check if Variants in 'if_dict' found in deconstructed_DNA:
if_bool = False # True if Variant in 'deconstructed_DNA' found in 'if_dict'
for a in if_dict: # Attribute in 'if_dict'
for b in if_dict[a]: # Variant in if_dict[Attribute]
var_order_num = str(if_dict[a][b][1]) # Order number of 'b' (Variant)
dna_order_num = str(
deconstructed_DNA[if_dict[a][b][4]]) # Order Number of 'b's attribute in deconstructed_DNA
if var_order_num == dna_order_num: # If DNA selected Variants found inside IF list variants:
if_bool = True
break
else:
continue
break
# Check if Variants in 'result_dict' found in deconstructed_DNA:
full_att_bool = False
result_bool = False # True if Variant in 'deconstructed_DNA' found in 'result_dict'
for a in result_dict: # Attribute in 'result_dict'
for b in result_dict[a]: # Variant in if_dict[Attribute]
var_order_num = str(result_dict[a][b][1]) # Order number of 'b' (Variant)
dna_order_num = str(
deconstructed_DNA[result_dict[a][b][4]]) # Order Number of 'b's attribute in deconstructed_DNA
if var_order_num == dna_order_num: # If DNA selected Variants found inside THEN list variants:
if list(result_dict[a].keys()) == list(hierarchy[a].keys()):
full_att_bool = True
result_bool = True
break
else:
continue
break
# Rule Bool return summary:
violates_rule = False
return violates_rule
# If Variants in 'if_dict' found in deconstructed_DNA and Variants in 'result_dict' not found in deconstructed_DNA:
if if_bool and not result_bool:
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 \
str(deconstructed_DNA[getAttIndex(hierarchy, b)]) not in num_List2[b]:
violates_rule = True
return violates_rule
elif if_bool and result_bool and result_dict_type == "NOT":
else:
violates_rule = False
return violates_rule
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
# If Variants in 'if_dict' not found in deconstructed_DNA, and 'result_dict' variants are found in deconstructed_DNA,
# and they are a part of a full Attribute in 'then_dict'
elif not if_bool and result_bool and full_att_bool:
violates_rule = True
# If Variants in 'if_dict' not found in deconstructed_DNA, but Variants in 'then_dict' are found in deconstructed_DNA,
# and don't make up a full Attribute:
# elif not if_bool and result_bool and not full_att_bool:
# violates_rule = False
return violates_rule, if_bool, result_bool, full_att_bool
return violates_rule
else:
violates_rule = False
return violates_rule
def create_dicts(hierarchy, rule_list_items, result_dict_type):
structure = {
"attribute1": {
"variant1": [
"name",
"order_number",
"rarity_number"
"attribute"
"attribute_index"
],
"variant2": [
"name",
"order_number",
"rarity_number"
"attribute"
"attribute_index"
]
},
"attribute2": {
"variant1": [
"name",
"order_number",
"rarity_number"
"attribute"
"attribute_index"
],
"variant2": [
"name",
"order_number",
"rarity_number"
"attribute"
"attribute_index"
]
}
}
# Main Function
def logicafyDNAsingle(hierarchy, singleDNA, logicFile):
items_returned = collections.defaultdict(dict)
for a in rule_list_items:
for b in hierarchy:
if a == b: # If 'a' is an Attribute, add all 'a' Variants to items_returned dict.
variant_list_of_a = list(hierarchy[a].keys())
variant_dict_of_a = {}
for c in variant_list_of_a:
variant_dict_of_a[c] = get_var_info(c, hierarchy)
items_returned[a] = variant_dict_of_a
if a in list(hierarchy[b].keys()): # If 'a' is a Variant, add all info about that variant to items_returned
items_returned[b][a] = get_var_info(a, hierarchy)
items_returned = dict(items_returned)
return dict(items_returned)
def logicafyDNAsingle(hierarchy, singleDNA, logicFile, enableRarity, enableMaterials):
deconstructed_DNA = singleDNA.split("-")
didReconstruct = True
originalDNA = str(singleDNA)
while didReconstruct:
didReconstruct = False
for rule in logicFile:
# Items from 'IF' key for a given rule
if_dict = create_dicts(hierarchy, logicFile[rule]["IF"], "IF")
items_List1 = isAttorVar(hierarchy, logicFile[rule]["Items-1"])
items_List2 = isAttorVar(hierarchy, logicFile[rule]["Items-2"])
num_List1 = items_to_num(items_List1)
num_List2 = items_to_num(items_List2)
result_dict_type = ""
if "THEN" in logicFile[rule]:
result_dict_type = "THEN"
if logicFile[rule]["Rule-Type"] == "Never With":
if never_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
if "NOT" in logicFile[rule]:
result_dict_type = "NOT"
rand_bool = bool(random.getrandbits(1))
result_dict = create_dicts(hierarchy, logicFile[rule][result_dict_type], result_dict_type)
if rand_bool:
deconstructed_DNA = rar_selectVar(hierarchy, items_List2, deconstructed_DNA)
# Change 'then_bool' to 'result_bool'
violates_rule, if_bool, then_bool, full_att_bool = get_rule_break_type(hierarchy, deconstructed_DNA,
if_dict, result_dict, result_dict_type)
if violates_rule:
# print(f"======={deconstructed_DNA} VIOLATES RULE======")
if not rand_bool:
deconstructed_DNA = rar_selectVar(hierarchy, items_List1, deconstructed_DNA)
deconstructed_DNA = apply_rules_to_dna(
hierarchy, deconstructed_DNA, if_dict, result_dict, result_dict_type, enableRarity
)
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:
if "0" in num_List1[b]: # If complete attribute
deconstructed_DNA[getAttIndex(hierarchy, b)] = "0"
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:

Wyświetl plik

@ -7,58 +7,36 @@ import bpy
import json
import random
from .Constants import bcolors, removeList, remove_file_by_extension, save_result
def select_material(materialList, variant, enableRarity):
def select_material(materialList):
"""Selects a material from a passed material list. """
material_List_Of_i = [] # List of Material names instead of order numbers
number_List_Of_i = []
rarity_List_Of_i = []
ifZeroBool = None
for material in materialList:
# Material Order Number comes from index in the Material List in materials.json for a given Variant.
# material_order_num = list(materialList.keys()).index(material)
material_List_Of_i.append(material)
material_order_num = material.split("_")[1]
number_List_Of_i.append(material_order_num)
material_rarity_percent = materialList[material]
material_rarity_percent = material.split("_")[1]
rarity_List_Of_i.append(float(material_rarity_percent))
print(f"MATERIAL_LIST_OF_I:{material_List_Of_i}")
print(f"RARITY_LIST_OF_I:{rarity_List_Of_i}")
for b in rarity_List_Of_i:
if b == 0:
for x in rarity_List_Of_i:
if x == 0:
ifZeroBool = True
elif b != 0:
break
elif x != 0:
ifZeroBool = False
if enableRarity:
try:
if ifZeroBool:
selected_material = random.choices(material_List_Of_i, k=1)
selected_material = random.choices(number_List_Of_i, k=1)
elif not ifZeroBool:
selected_material = random.choices(material_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 Material List of the Variant collection '{variant}'. 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"
)
else:
try:
selected_material = random.choices(material_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 Material List of the Variant collection '{variant}'. 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"
)
selected_material = random.choices(number_List_Of_i, weights=rarity_List_Of_i, k=1)
return selected_material[0], materialList
return selected_material[0]
def get_variant_att_index(variant, hierarchy):
variant_attribute = None
@ -91,7 +69,7 @@ def match_DNA_to_Variant(hierarchy, singleDNA):
dnaDictionary.update({x: k})
return dnaDictionary
def apply_materials(hierarchy, singleDNA, materialsFile, enableRarity):
def apply_materials(hierarchy, singleDNA, materialsFile):
"""
DNA with applied material example: "1-1:1-1" <Normal DNA>:<Selected Material for each Variant>
@ -107,22 +85,12 @@ def apply_materials(hierarchy, singleDNA, materialsFile, enableRarity):
complete = False
for b in materialsFile:
if singleDNADict[a] == b:
material_name, materialList, = select_material(materialsFile[b]['Material List'], b, enableRarity)
material_order_num = list(materialList.keys()).index(material_name) # Gets the Order Number of the Material
deconstructed_MaterialDNA[a] = str(material_order_num + 1)
mat = select_material(materialsFile[b]['Material List'])
deconstructed_MaterialDNA[a] = mat
complete = True
if not complete:
deconstructed_MaterialDNA[a] = "0"
# Make Attributes have the same materials:
# Order your Attributes alphabetically, then assign each Attribute a number, starting with 0. So Attribute 'A' = 0,
# Attribute 'B' = 1, 'C' = 2, 'D' = 3, etc. For each pair you want to equal another, add its number it to this list:
# synced_material_attributes = [1, 2]
#
# first_mat = deconstructed_MaterialDNA[synced_material_attributes[0]]
# for i in synced_material_attributes:
# deconstructed_MaterialDNA[i] = first_mat
material_DNA = ""
for a in deconstructed_MaterialDNA:
num = "-" + str(deconstructed_MaterialDNA[a])

Wyświetl plik

@ -4,9 +4,6 @@
import bpy
import random
from .Constants 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
@ -17,6 +14,7 @@ def createDNArarity(hierarchy):
for i in hierarchy:
number_List_Of_i = []
rarity_List_Of_i = []
count = 0
ifZeroBool = None
for k in hierarchy[i]:
@ -26,24 +24,18 @@ def createDNArarity(hierarchy):
rarity = hierarchy[i][k]["rarity"]
rarity_List_Of_i.append(float(rarity))
count += 1
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))