kopia lustrzana https://github.com/torrinworx/Blend_My_NFTs
Fixing merge issues
Minor bug fixes to exporter and cleaning up code.pull/136/head
rodzic
752fa4b6e9
commit
4f42106fa3
|
@ -117,10 +117,8 @@ class CUSTOM_logic_objectCollection(PropertyGroup):
|
|||
name="Rule Type",
|
||||
description="Select the Rule Type",
|
||||
items=[
|
||||
('Never With', "Never With", ""),
|
||||
('Only With', "Only With", ""),
|
||||
('Always With', "Always With", ""),
|
||||
|
||||
('THEN', "Then", ""),
|
||||
('NOT', "Not", ""),
|
||||
]
|
||||
)
|
||||
item_list2: StringProperty(default="Item List 2")
|
||||
|
|
398
__init__.py
398
__init__.py
|
@ -1,15 +1,18 @@
|
|||
bl_info = {
|
||||
"name": "Blend_My_NFTs",
|
||||
"author": "Torrin Leonard, This Cozy Studio Inc",
|
||||
"version": (4, 0, 2),
|
||||
"blender": (3, 2, 0),
|
||||
"version": (4, 5, 0),
|
||||
"blender": (3, 2, 2),
|
||||
"location": "View3D",
|
||||
"description": "An open source, free to use Blender add-on that enables you to create thousands of unique images, animations, and 3D models.",
|
||||
"description": "A free and opensource Blender add-on that enables you to create thousands of unique images, animations, and 3D models.",
|
||||
"support": "COMMUNITY",
|
||||
"doc_url": "https://github.com/torrinworx/Blend_My_NFTs",
|
||||
"tracker_url": "https://github.com/torrinworx/Blend_My_NFTs/issues/new",
|
||||
"category": "Development",
|
||||
}
|
||||
|
||||
BMNFTS_VERSION = "v4.0.2"
|
||||
LAST_UPDATED = "8:19AM, May 31st, 2022"
|
||||
BMNFTS_VERSION = "v4.5.0"
|
||||
LAST_UPDATED = "8:19AM, Aug 11th, 2022"
|
||||
|
||||
# ======== Import handling ======== #
|
||||
|
||||
|
@ -18,17 +21,20 @@ from bpy.app.handlers import persistent
|
|||
from bpy.props import (IntProperty,
|
||||
BoolProperty,
|
||||
CollectionProperty)
|
||||
|
||||
# Python modules:
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import importlib
|
||||
from dataclasses import dataclass
|
||||
import traceback
|
||||
from typing import Any
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# "a little hacky bs" - matt159 ;)
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
# Local file imports:
|
||||
from main import \
|
||||
Checks, \
|
||||
DNA_Generator, \
|
||||
|
@ -74,7 +80,41 @@ 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
|
||||
|
||||
|
||||
@persistent
|
||||
def Refresh_UI(dummy1, dummy2):
|
||||
"""
|
||||
Refreshes the UI upon user interacting with Blender (using depsgraph_update_post handler). Might be a better handler
|
||||
to use.
|
||||
"""
|
||||
global combinations
|
||||
global recommended_limit
|
||||
|
||||
combinations = (get_combinations.get_combinations())
|
||||
recommended_limit = int(round(combinations / 2))
|
||||
|
||||
# Add panel classes that require refresh to this refresh_panels tuple:
|
||||
refresh_panel_classes = (
|
||||
BMNFTS_PT_CreateData,
|
||||
)
|
||||
|
||||
def redraw_panel(panels):
|
||||
for i in panels:
|
||||
try:
|
||||
bpy.utils.unregister_class(i)
|
||||
except Exception:
|
||||
print(traceback.format_exc())
|
||||
bpy.utils.register_class(i)
|
||||
|
||||
redraw_panel(refresh_panel_classes)
|
||||
|
||||
|
||||
bpy.app.handlers.depsgraph_update_post.append(Refresh_UI)
|
||||
|
||||
|
||||
# ======== Defining BMNFTs Data ======== #
|
||||
@dataclass
|
||||
class BMNFTData:
|
||||
nftName: str
|
||||
|
@ -115,6 +155,17 @@ class BMNFTData:
|
|||
|
||||
enableRarity: bool
|
||||
|
||||
enableAutoShutdown: bool
|
||||
|
||||
specify_timeBool: bool
|
||||
hours: int
|
||||
minutes: int
|
||||
|
||||
emailNotificationBool: bool
|
||||
sender_from: str
|
||||
email_password: str
|
||||
receiver_to: str
|
||||
|
||||
custom_Fields: dict = None
|
||||
fail_state: Any = False
|
||||
failed_batch: Any = None
|
||||
|
@ -124,82 +175,64 @@ class BMNFTData:
|
|||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
enableModelsBlender=bpy.context.scene.input_tool.modelBool,
|
||||
modelFileFormat=bpy.context.scene.input_tool.modelEnum,
|
||||
|
||||
enableCustomFields = bpy.context.scene.input_tool.enableCustomFields,
|
||||
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,
|
||||
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,
|
||||
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)
|
||||
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,
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
@persistent
|
||||
def Refresh_UI(dummy1, dummy2):
|
||||
"""
|
||||
Refreshes the UI upon user interacting with Blender (using depsgraph_update_post handler). Might be a better handler
|
||||
to use.
|
||||
"""
|
||||
global combinations
|
||||
global recommended_limit
|
||||
|
||||
combinations = (get_combinations.get_combinations())
|
||||
recommended_limit = int(round(combinations / 2))
|
||||
|
||||
# Add panel classes that require refresh to this refresh_panels tuple:
|
||||
refresh_panel_classes = (
|
||||
BMNFTS_PT_CreateData,
|
||||
)
|
||||
|
||||
def redraw_panel(refresh_panel_classes):
|
||||
for i in refresh_panel_classes:
|
||||
try:
|
||||
bpy.utils.unregister_class(i)
|
||||
except:
|
||||
pass
|
||||
bpy.utils.register_class(i)
|
||||
|
||||
redraw_panel(refresh_panel_classes)
|
||||
|
||||
|
||||
bpy.app.handlers.depsgraph_update_post.append(Refresh_UI)
|
||||
|
||||
|
||||
# ======== Helper functions ======== #
|
||||
def make_directories(save_path):
|
||||
|
@ -236,29 +269,29 @@ def runAsHeadless():
|
|||
|
||||
def dumpSettings(settings):
|
||||
output = (
|
||||
f"nftName={ settings.nftName }\n"
|
||||
f"collectionSize={ str(settings.collectionSize) }\n"
|
||||
f"nftsPerBatch={ str(settings.nftsPerBatch) }\n"
|
||||
f"save_path={ settings.save_path }\n"
|
||||
f"enableRarity={ (settings.enableRarity) }\n"
|
||||
f"enableLogic={ str(settings.enableLogic) }\n"
|
||||
f"imageBool={ str(settings.imageBool) }\n"
|
||||
f"imageEnum={ settings.imageEnum }\n"
|
||||
f"animationBool={ str(settings.animationBool) }\n"
|
||||
f"animationEnum={ settings.animationEnum }\n"
|
||||
f"modelBool={ str(settings.modelBool) }\n"
|
||||
f"modelEnum={ settings.modelEnum }\n"
|
||||
f"batchToGenerate={ str(settings.batchToGenerate) }\n"
|
||||
f"cardanoMetaDataBool={ str(settings.cardanoMetaDataBool) }\n"
|
||||
f"cardano_description={ settings.cardano_description }\n"
|
||||
f"erc721MetaData={ str(settings.erc721MetaData) }\n"
|
||||
f"erc721_description={ settings.erc721_description }\n"
|
||||
f"solanaMetaDataBool={ str(settings.solanaMetaDataBool) }\n"
|
||||
f"solana_description={ settings.solana_description }\n"
|
||||
f"enableCustomFields={ str(settings.enableCustomFields) }\n"
|
||||
f"customfieldsFile={ settings.customfieldsFile }\n"
|
||||
f"enableMaterials={ str(settings.customfieldsFile) }\n"
|
||||
f"materialsFile={ settings.materialsFile }\n"
|
||||
f"nftName={settings.nftName}\n"
|
||||
f"collectionSize={str(settings.collectionSize)}\n"
|
||||
f"nftsPerBatch={str(settings.nftsPerBatch)}\n"
|
||||
f"save_path={settings.save_path}\n"
|
||||
f"enableRarity={(settings.enableRarity)}\n"
|
||||
f"enableLogic={str(settings.enableLogic)}\n"
|
||||
f"imageBool={str(settings.imageBool)}\n"
|
||||
f"imageEnum={settings.imageEnum}\n"
|
||||
f"animationBool={str(settings.animationBool)}\n"
|
||||
f"animationEnum={settings.animationEnum}\n"
|
||||
f"modelBool={str(settings.modelBool)}\n"
|
||||
f"modelEnum={settings.modelEnum}\n"
|
||||
f"batchToGenerate={str(settings.batchToGenerate)}\n"
|
||||
f"cardanoMetaDataBool={str(settings.cardanoMetaDataBool)}\n"
|
||||
f"cardano_description={settings.cardano_description}\n"
|
||||
f"erc721MetaData={str(settings.erc721MetaData)}\n"
|
||||
f"erc721_description={settings.erc721_description}\n"
|
||||
f"solanaMetaDataBool={str(settings.solanaMetaDataBool)}\n"
|
||||
f"solana_description={settings.solana_description}\n"
|
||||
f"enableCustomFields={str(settings.enableCustomFields)}\n"
|
||||
f"customfieldsFile={settings.customfieldsFile}\n"
|
||||
f"enableMaterials={str(settings.customfieldsFile)}\n"
|
||||
f"materialsFile={settings.materialsFile}\n"
|
||||
)
|
||||
print(output)
|
||||
|
||||
|
@ -425,10 +458,30 @@ class BMNFTS_PGT_Input_Properties(bpy.types.PropertyGroup):
|
|||
subtype="FILE_PATH"
|
||||
)
|
||||
|
||||
# TODO: Add 'Other' panel inputs to Headless functionality.
|
||||
|
||||
# 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 Shutdown:
|
||||
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')
|
||||
apiKey: bpy.props.StringProperty(name="API Key", subtype='PASSWORD') # Test code for future faetures
|
||||
|
||||
|
||||
# ======== Main Operators ======== #
|
||||
|
@ -448,7 +501,8 @@ class createData(bpy.types.Operator):
|
|||
|
||||
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.")
|
||||
self.report({'ERROR'},
|
||||
f"No Logic.json file path set. Please set the file path to your Logic.json file.")
|
||||
|
||||
Intermediate.send_To_Record_JSON(input)
|
||||
|
||||
|
@ -470,7 +524,6 @@ class exportNFTs(bpy.types.Operator):
|
|||
name="Reverse Order")
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
input = getBMNFTData()
|
||||
# Handling Custom Fields UIList input:
|
||||
|
||||
|
@ -498,42 +551,42 @@ class resume_failed_batch(bpy.types.Operator):
|
|||
|
||||
_fail_state, _failed_batch, _failed_dna, _failed_dna_index = Checks.check_FailedBatches(_batch_json_save_path)
|
||||
|
||||
input = BMNFTData (
|
||||
nftName = batchData["Generation Save"][-1]["Render_Settings"]["nftName"],
|
||||
save_path = _save_path,
|
||||
collectionSize = batchData["Generation Save"][-1]["Render_Settings"]["collectionSize"],
|
||||
input = BMNFTData(
|
||||
nftName=batchData["Generation Save"][-1]["Render_Settings"]["nftName"],
|
||||
save_path=_save_path,
|
||||
collectionSize=batchData["Generation Save"][-1]["Render_Settings"]["collectionSize"],
|
||||
|
||||
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"],
|
||||
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"],
|
||||
|
||||
enableImages = batchData["Generation Save"][-1]["Render_Settings"]["enableImages"],
|
||||
imageFileFormat = batchData["Generation Save"][-1]["Render_Settings"]["imageFileFormat"],
|
||||
enableImages=batchData["Generation Save"][-1]["Render_Settings"]["enableImages"],
|
||||
imageFileFormat=batchData["Generation Save"][-1]["Render_Settings"]["imageFileFormat"],
|
||||
|
||||
enableAnimations = batchData["Generation Save"][-1]["Render_Settings"]["enableAnimations"],
|
||||
animationFileFormat = batchData["Generation Save"][-1]["Render_Settings"]["animationFileFormat"],
|
||||
enableAnimations=batchData["Generation Save"][-1]["Render_Settings"]["enableAnimations"],
|
||||
animationFileFormat=batchData["Generation Save"][-1]["Render_Settings"]["animationFileFormat"],
|
||||
|
||||
enableModelsBlender = batchData["Generation Save"][-1]["Render_Settings"]["enableModelsBlender"],
|
||||
modelFileFormat = batchData["Generation Save"][-1]["Render_Settings"]["modelFileFormat"],
|
||||
enableModelsBlender=batchData["Generation Save"][-1]["Render_Settings"]["enableModelsBlender"],
|
||||
modelFileFormat=batchData["Generation Save"][-1]["Render_Settings"]["modelFileFormat"],
|
||||
|
||||
enableCustomFields = batchData["Generation Save"][-1]["Render_Settings"]["enableCustomFields"],
|
||||
custom_Fields = batchData["Generation Save"][-1]["Render_Settings"]["custom_Fields"],
|
||||
enableCustomFields=batchData["Generation Save"][-1]["Render_Settings"]["enableCustomFields"],
|
||||
custom_Fields=batchData["Generation Save"][-1]["Render_Settings"]["custom_Fields"],
|
||||
|
||||
cardanoMetaDataBool = batchData["Generation Save"][-1]["Render_Settings"]["cardanoMetaDataBool"],
|
||||
solanaMetaDataBool = batchData["Generation Save"][-1]["Render_Settings"]["solanaMetaDataBool"],
|
||||
erc721MetaData = batchData["Generation Save"][-1]["Render_Settings"]["erc721MetaData"],
|
||||
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"],
|
||||
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"],
|
||||
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
|
||||
fail_state=_fail_state,
|
||||
failed_batch=_failed_batch,
|
||||
failed_dna=_failed_dna,
|
||||
failed_dna_index=_failed_dna_index
|
||||
)
|
||||
|
||||
Exporter.render_and_save_NFTs(input)
|
||||
|
@ -582,51 +635,51 @@ class export_settings(bpy.types.Operator):
|
|||
"#when running Blend_My_NFTs in a headless environment.\n"
|
||||
"\n"
|
||||
"#The name of your nft project\n"
|
||||
f"nftName={ settings.nftName }\n"
|
||||
f"nftName={settings.nftName}\n"
|
||||
"\n"
|
||||
"#NFT Collection Size\n"
|
||||
f"collectionSize={ settings.collectionSize }\n"
|
||||
f"collectionSize={settings.collectionSize}\n"
|
||||
"\n"
|
||||
"#The number of NFTs to generate per batch\n"
|
||||
f"nftsPerBatch={ str(settings.nftsPerBatch) }\n"
|
||||
f"nftsPerBatch={str(settings.nftsPerBatch)}\n"
|
||||
"\n"
|
||||
"#Save path for your NFT files\n"
|
||||
f"save_path={ settings.save_path }\n"
|
||||
f"save_path={settings.save_path}\n"
|
||||
"\n"
|
||||
"#Enable Rarity\n"
|
||||
f"enableRarity={ (settings.enableRarity) }\n"
|
||||
f"enableRarity={(settings.enableRarity)}\n"
|
||||
"\n"
|
||||
"#Enable Logic\n"
|
||||
f"enableLogic={ str(settings.enableLogic) }\n"
|
||||
f"enableLogicJson={ str(settings.enable_Logic_Json) }\n"
|
||||
f"logicFilePath={ settings.logicFile }\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"
|
||||
f"imageEnum={ settings.imageEnum }\n"
|
||||
f"animationBool={ str(settings.animationBool) }\n"
|
||||
f"animationEnum={ settings.animationEnum }\n"
|
||||
f"modelBool={ str(settings.modelBool) }\n"
|
||||
f"modelEnum={ settings.modelEnum }\n"
|
||||
f"imageBool={str(settings.imageBool)}\n"
|
||||
f"imageEnum={settings.imageEnum}\n"
|
||||
f"animationBool={str(settings.animationBool)}\n"
|
||||
f"animationEnum={settings.animationEnum}\n"
|
||||
f"modelBool={str(settings.modelBool)}\n"
|
||||
f"modelEnum={settings.modelEnum}\n"
|
||||
"\n"
|
||||
"#Batch to generate\n"
|
||||
f"batchToGenerate={ str(settings.batchToGenerate) }\n"
|
||||
f"batchToGenerate={str(settings.batchToGenerate)}\n"
|
||||
"\n"
|
||||
"#Metadata Format\n"
|
||||
f"cardanoMetaDataBool={ str(settings.cardanoMetaDataBool) }\n"
|
||||
f"cardano_description={ settings.cardano_description }\n"
|
||||
f"erc721MetaData={ str(settings.erc721MetaData) }\n"
|
||||
f"erc721_description={ settings.erc721_description }\n"
|
||||
f"solanaMetaDataBool={ str(settings.solanaMetaDataBool) }\n"
|
||||
f"solana_description={ settings.solana_description }\n"
|
||||
f"cardanoMetaDataBool={str(settings.cardanoMetaDataBool)}\n"
|
||||
f"cardano_description={settings.cardano_description}\n"
|
||||
f"erc721MetaData={str(settings.erc721MetaData)}\n"
|
||||
f"erc721_description={settings.erc721_description}\n"
|
||||
f"solanaMetaDataBool={str(settings.solanaMetaDataBool)}\n"
|
||||
f"solana_description={settings.solana_description}\n"
|
||||
"\n"
|
||||
"#Enable Custom Fields\n"
|
||||
f"enableCustomFields={ str(settings.enableCustomFields) }\n"
|
||||
f"customfieldsFile={ settings.customfieldsFile }\n"
|
||||
f"enableCustomFields={str(settings.enableCustomFields)}\n"
|
||||
f"customfieldsFile={settings.customfieldsFile}\n"
|
||||
"\n"
|
||||
"#Enable Materials\n"
|
||||
f"enableMaterials={ str(settings.enableMaterials) }\n"
|
||||
f"materialsFile={ settings.materialsFile }\n"
|
||||
f"enableMaterials={str(settings.enableMaterials)}\n"
|
||||
f"materialsFile={settings.materialsFile}\n"
|
||||
)
|
||||
|
||||
print(output, file=config)
|
||||
|
@ -788,7 +841,8 @@ class BMNFTS_PT_GenerateNFTs(bpy.types.Panel):
|
|||
|
||||
rows = 2
|
||||
row = layout.row()
|
||||
row.template_list("CUSTOM_UL_custom_metadata_fields_items", "", scn, "custom_metadata_fields", scn, "custom_metadata_fields_index", rows=rows)
|
||||
row.template_list("CUSTOM_UL_custom_metadata_fields_items", "", scn, "custom_metadata_fields", scn,
|
||||
"custom_metadata_fields_index", rows=rows)
|
||||
|
||||
col = row.column(align=True)
|
||||
col.operator("custom_metadata_fields_uilist.list_action", icon='ZOOM_IN', text="").action = 'ADD'
|
||||
|
@ -843,7 +897,6 @@ class BMNFTS_PT_Refactor(bpy.types.Panel):
|
|||
layout.label(text="Ensure all batches have been created before refactoring.")
|
||||
layout.label(text="Refactoring combines all batches into one easy to manage folder.")
|
||||
|
||||
|
||||
row = layout.row()
|
||||
self.layout.operator("refactor.batches", icon='FOLDER_REDIRECT', text="Refactor Batches")
|
||||
|
||||
|
@ -861,10 +914,59 @@ 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)
|
||||
|
@ -913,7 +1015,7 @@ classes = (
|
|||
BMNFTS_PT_GenerateNFTs,
|
||||
BMNFTS_PT_Refactor,
|
||||
BMNFTS_PT_Other,
|
||||
) + Custom_Metadata_UIList.classes_Custom_Metadata_UIList + Logic_UIList.classes_Logic_UIList
|
||||
) + Custom_Metadata_UIList.classes_Custom_Metadata_UIList + Logic_UIList.classes_Logic_UIList
|
||||
|
||||
|
||||
def register():
|
||||
|
@ -922,12 +1024,14 @@ def register():
|
|||
|
||||
bpy.types.Scene.input_tool = bpy.props.PointerProperty(type=BMNFTS_PGT_Input_Properties)
|
||||
|
||||
bpy.types.Scene.custom_metadata_fields = CollectionProperty(type=Custom_Metadata_UIList.CUSTOM_custom_metadata_fields_objectCollection)
|
||||
bpy.types.Scene.custom_metadata_fields = CollectionProperty(
|
||||
type=Custom_Metadata_UIList.CUSTOM_custom_metadata_fields_objectCollection)
|
||||
bpy.types.Scene.custom_metadata_fields_index = IntProperty()
|
||||
|
||||
bpy.types.Scene.logic_fields = CollectionProperty(type=Logic_UIList.CUSTOM_logic_objectCollection)
|
||||
bpy.types.Scene.logic_fields_index = IntProperty()
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
# 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"]
|
||||
|
||||
|
@ -34,3 +35,17 @@ 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')
|
||||
|
|
|
@ -82,7 +82,7 @@ def get_hierarchy():
|
|||
allAttDataList = {}
|
||||
for i in attributeVariants:
|
||||
# Check if name follows naming conventions:
|
||||
if i.count("_") > 2:
|
||||
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"
|
||||
|
@ -90,30 +90,11 @@ def get_hierarchy():
|
|||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
|
||||
)
|
||||
|
||||
def getName(i):
|
||||
"""
|
||||
Returns the name of "i" attribute variant
|
||||
"""
|
||||
|
||||
try:
|
||||
number = i.split("_")[1]
|
||||
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:
|
||||
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"
|
||||
|
@ -121,18 +102,7 @@ def get_hierarchy():
|
|||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n"
|
||||
)
|
||||
|
||||
try:
|
||||
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"
|
||||
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"
|
||||
)
|
||||
|
||||
eachObject = {"name": name, "number": number, "rarity": rarity}
|
||||
allAttDataList[i] = eachObject
|
||||
allAttDataList[i] = {"name": name, "number": number, "rarity": rarity}
|
||||
return allAttDataList
|
||||
|
||||
variantMetaData = attributeData(attributeVariants)
|
||||
|
@ -153,6 +123,7 @@ 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.
|
||||
|
@ -163,7 +134,7 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
|
|||
# DNA random, Rarity and Logic methods:
|
||||
DataDictionary = {}
|
||||
|
||||
def createDNArandom():
|
||||
def createDNArandom(hierarchy):
|
||||
"""Creates a single DNA randomly without Rarity or Logic."""
|
||||
dnaStr = ""
|
||||
dnaStrList = []
|
||||
|
@ -188,24 +159,28 @@ 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()
|
||||
singleDNA = createDNArandom(hierarchy)
|
||||
# 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)
|
||||
# print(f"Original DNA: {singleDNA}")
|
||||
# print("============\n")
|
||||
singleDNA = Logic.logicafyDNAsingle(hierarchy, singleDNA, logicFile, enableRarity, enableMaterials)
|
||||
# print(f"Logic DNA: {singleDNA}")
|
||||
|
||||
if enableMaterials:
|
||||
singleDNA = Material_Generator.apply_materials(hierarchy, singleDNA, materialsFile)
|
||||
singleDNA = Material_Generator.apply_materials(hierarchy, singleDNA, materialsFile, enableRarity)
|
||||
# print(f"Materials DNA: {singleDNA}")
|
||||
|
||||
# print("============\n")
|
||||
|
||||
return singleDNA
|
||||
|
@ -248,6 +223,7 @@ 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.
|
||||
|
@ -303,6 +279,7 @@ 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):
|
||||
"""
|
||||
|
@ -322,13 +299,14 @@ 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. Rules listed in {logicFile} will be taken into account.\n{bcolors.RESET}")
|
||||
print(f"{bcolors.OK}Logic is ON. {len(list(logicFile.keys()))} rules detected and applied.\n{bcolors.RESET}")
|
||||
|
||||
time_start = time.time()
|
||||
|
||||
|
@ -345,7 +323,8 @@ 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(
|
||||
|
|
105
main/Exporter.py
105
main/Exporter.py
|
@ -4,9 +4,12 @@
|
|||
|
||||
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
|
||||
|
@ -112,18 +115,20 @@ 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
|
||||
|
@ -177,12 +182,14 @@ def render_and_save_NFTs(input):
|
|||
if hierarchy[attribute][var]['number'] == variant:
|
||||
variant = var
|
||||
|
||||
if material != '0':
|
||||
if material != '0': # If material is not empty
|
||||
for variant_m in materialsFile:
|
||||
if variant == variant_m:
|
||||
for mat in materialsFile[variant_m]["Material List"]:
|
||||
if mat.split('_')[1] == material:
|
||||
material = mat
|
||||
# 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
|
||||
|
||||
full_dna_dict[variant] = material
|
||||
|
||||
|
@ -357,20 +364,31 @@ def render_and_save_NFTs(input):
|
|||
for obj in bpy.data.collections['Script_Ignore'].all_objects:
|
||||
obj.select_set(True)
|
||||
|
||||
# Remove objects from 3D model export:
|
||||
# remove_objects: list = [
|
||||
# ]
|
||||
#
|
||||
# for obj in bpy.data.objects:
|
||||
# if obj.name in remove_objects:
|
||||
# obj.select_set(False)
|
||||
|
||||
if input.modelFileFormat == 'GLB':
|
||||
bpy.ops.export_scene.gltf(filepath=f"{modelPath}.glb",
|
||||
check_existing=True,
|
||||
export_format='GLB',
|
||||
export_keep_originals=True,
|
||||
use_selection=True)
|
||||
if input.modelFileFormat == 'GLTF_SEPARATE':
|
||||
bpy.ops.export_scene.gltf(filepath=f"{modelPath}",
|
||||
check_existing=True,
|
||||
export_format='GLTF_SEPARATE',
|
||||
export_keep_originals=True,
|
||||
use_selection=True)
|
||||
if input.modelFileFormat == 'GLTF_EMBEDDED':
|
||||
bpy.ops.export_scene.gltf(filepath=f"{modelPath}.gltf",
|
||||
check_existing=True,
|
||||
export_format='GLTF_EMBEDDED',
|
||||
export_keep_originals=True,
|
||||
use_selection=True)
|
||||
elif input.modelFileFormat == 'FBX':
|
||||
bpy.ops.export_scene.fbx(filepath=f"{modelPath}.fbx",
|
||||
|
@ -392,7 +410,7 @@ def render_and_save_NFTs(input):
|
|||
bpy.ops.export_vox.some_data(filepath=f"{modelPath}.vox")
|
||||
|
||||
# Loading Animation:
|
||||
loading = Loader(f'Rendering Animation {x}/{NFTs_in_Batch}...', '').start()
|
||||
loading = Loader(f'Generating 3D model {x}/{NFTs_in_Batch}...', '').start()
|
||||
generate_models()
|
||||
loading.stop()
|
||||
|
||||
|
@ -406,20 +424,23 @@ def render_and_save_NFTs(input):
|
|||
if input.cardanoMetaDataBool:
|
||||
if not os.path.exists(cardanoMetadataPath):
|
||||
os.makedirs(cardanoMetadataPath)
|
||||
createCardanoMetadata(name, Order_Num, full_single_dna, dnaDictionary, metadataMaterialDict, input.custom_Fields,
|
||||
createCardanoMetadata(name, Order_Num, full_single_dna, dnaDictionary, metadataMaterialDict,
|
||||
input.custom_Fields,
|
||||
input.enableCustomFields, input.cardano_description, cardanoMetadataPath)
|
||||
|
||||
if input.solanaMetaDataBool:
|
||||
if not os.path.exists(solanaMetadataPath):
|
||||
os.makedirs(solanaMetadataPath)
|
||||
createSolanaMetaData(name, Order_Num, full_single_dna, dnaDictionary, metadataMaterialDict, input.custom_Fields,
|
||||
input.enableCustomFields, input.cardano_description, solanaMetadataPath)
|
||||
createSolanaMetaData(name, Order_Num, full_single_dna, dnaDictionary, metadataMaterialDict,
|
||||
input.custom_Fields,
|
||||
input.enableCustomFields, input.solana_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.cardano_description, erc721MetadataPath)
|
||||
createErc721MetaData(name, Order_Num, full_single_dna, dnaDictionary, metadataMaterialDict,
|
||||
input.custom_Fields,
|
||||
input.enableCustomFields, input.erc721_description, erc721MetadataPath)
|
||||
|
||||
if not os.path.exists(BMNFT_metaData_Folder):
|
||||
os.makedirs(BMNFT_metaData_Folder)
|
||||
|
@ -457,3 +478,63 @@ 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)
|
||||
|
|
|
@ -3,8 +3,8 @@ import bpy
|
|||
|
||||
from main import DNA_Generator, Exporter
|
||||
|
||||
def send_To_Record_JSON(input, reverse_order=False):
|
||||
|
||||
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))
|
||||
|
@ -24,10 +24,10 @@ def send_To_Record_JSON(input, reverse_order=False):
|
|||
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(',')
|
||||
"IF": item_list1.split(','),
|
||||
rule_type: item_list2.split(',')
|
||||
}
|
||||
print(rule_type)
|
||||
num += 1
|
||||
else:
|
||||
input.logicFile = {}
|
||||
|
@ -37,13 +37,14 @@ def send_To_Record_JSON(input, reverse_order=False):
|
|||
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(',')
|
||||
"IF": item_list1.split(','),
|
||||
rule_type: item_list2.split(',')
|
||||
}
|
||||
print(rule_type)
|
||||
|
||||
num += 1
|
||||
|
||||
DNA_Generator.send_To_Record_JSON( input.collectionSize,
|
||||
DNA_Generator.send_To_Record_JSON(input.collectionSize,
|
||||
input.nftsPerBatch,
|
||||
input.save_path,
|
||||
input.enableRarity,
|
||||
|
@ -55,21 +56,23 @@ def send_To_Record_JSON(input, reverse_order=False):
|
|||
input.batch_json_save_path
|
||||
)
|
||||
|
||||
def render_and_save_NFTs(input, reverse_order=False):
|
||||
|
||||
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.")
|
||||
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.")
|
||||
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
|
||||
|
||||
|
|
404
main/Logic.py
404
main/Logic.py
|
@ -2,207 +2,283 @@
|
|||
# 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 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
|
||||
|
||||
# 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
|
||||
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 = ""
|
||||
|
||||
num_List1 = strip_empty_variant(num_List1)
|
||||
num_List2 = strip_empty_variant(num_List2)
|
||||
for a in hierarchy:
|
||||
for var in list(hierarchy[a].keys()):
|
||||
if var == variant:
|
||||
attribute = a
|
||||
break
|
||||
attribute_index = list(hierarchy.keys()).index(attribute)
|
||||
|
||||
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
|
||||
return [name, order_number, rarity_number, attribute, attribute_index] # list of Var info sent back
|
||||
|
||||
|
||||
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"
|
||||
)
|
||||
else:
|
||||
violates_rule = False
|
||||
return violates_rule
|
||||
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])
|
||||
|
||||
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
|
||||
return deconstructed_DNA
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
violates_rule = False
|
||||
return violates_rule
|
||||
continue
|
||||
break
|
||||
|
||||
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
|
||||
return violates_rule
|
||||
# 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:
|
||||
violates_rule = True
|
||||
|
||||
elif if_bool and result_bool and result_dict_type == "NOT":
|
||||
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
|
||||
|
||||
|
||||
# Main Function
|
||||
def logicafyDNAsingle(hierarchy, singleDNA, logicFile):
|
||||
def create_dicts(hierarchy, rule_list_items, result_dict_type):
|
||||
# Example of output structure:
|
||||
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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
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_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)
|
||||
# Items from 'IF' key for a given rule
|
||||
if_dict = create_dicts(hierarchy, logicFile[rule]["IF"], "IF")
|
||||
|
||||
if logicFile[rule]["Rule-Type"] == "Never With":
|
||||
if never_with_Rule_Check(hierarchy, deconstructed_DNA, num_List1, num_List2):
|
||||
result_dict_type = ""
|
||||
if "THEN" in logicFile[rule]:
|
||||
result_dict_type = "THEN"
|
||||
|
||||
rand_bool = bool(random.getrandbits(1))
|
||||
if "NOT" in logicFile[rule]:
|
||||
result_dict_type = "NOT"
|
||||
|
||||
if rand_bool:
|
||||
deconstructed_DNA = rar_selectVar(hierarchy, items_List2, deconstructed_DNA)
|
||||
result_dict = create_dicts(hierarchy, logicFile[rule][result_dict_type], result_dict_type)
|
||||
|
||||
if not rand_bool:
|
||||
deconstructed_DNA = rar_selectVar(hierarchy, items_List1, 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======")
|
||||
|
||||
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)
|
||||
deconstructed_DNA = apply_rules_to_dna(
|
||||
hierarchy, deconstructed_DNA, if_dict, result_dict, result_dict_type, enableRarity
|
||||
)
|
||||
|
||||
newDNA = reconstructDNA(deconstructed_DNA)
|
||||
if newDNA != originalDNA:
|
||||
|
|
|
@ -7,36 +7,58 @@ import bpy
|
|||
|
||||
import json
|
||||
import random
|
||||
from .Constants import bcolors, removeList, remove_file_by_extension, save_result
|
||||
|
||||
|
||||
def select_material(materialList):
|
||||
def select_material(materialList, variant, enableRarity):
|
||||
"""Selects a material from a passed material list. """
|
||||
|
||||
number_List_Of_i = []
|
||||
material_List_Of_i = [] # List of Material names instead of order numbers
|
||||
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_order_num = material.split("_")[1]
|
||||
number_List_Of_i.append(material_order_num)
|
||||
material_List_Of_i.append(material)
|
||||
|
||||
material_rarity_percent = material.split("_")[1]
|
||||
material_rarity_percent = materialList[material]
|
||||
rarity_List_Of_i.append(float(material_rarity_percent))
|
||||
|
||||
for x in rarity_List_Of_i:
|
||||
if x == 0:
|
||||
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:
|
||||
ifZeroBool = True
|
||||
break
|
||||
elif x != 0:
|
||||
elif b != 0:
|
||||
ifZeroBool = False
|
||||
|
||||
if enableRarity:
|
||||
try:
|
||||
if ifZeroBool:
|
||||
selected_material = random.choices(number_List_Of_i, k=1)
|
||||
selected_material = random.choices(material_List_Of_i, k=1)
|
||||
elif not ifZeroBool:
|
||||
selected_material = random.choices(number_List_Of_i, weights=rarity_List_Of_i, k=1)
|
||||
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"
|
||||
)
|
||||
|
||||
return selected_material[0]
|
||||
return selected_material[0], materialList
|
||||
|
||||
def get_variant_att_index(variant, hierarchy):
|
||||
variant_attribute = None
|
||||
|
@ -69,7 +91,7 @@ def match_DNA_to_Variant(hierarchy, singleDNA):
|
|||
dnaDictionary.update({x: k})
|
||||
return dnaDictionary
|
||||
|
||||
def apply_materials(hierarchy, singleDNA, materialsFile):
|
||||
def apply_materials(hierarchy, singleDNA, materialsFile, enableRarity):
|
||||
"""
|
||||
DNA with applied material example: "1-1:1-1" <Normal DNA>:<Selected Material for each Variant>
|
||||
|
||||
|
@ -85,12 +107,24 @@ def apply_materials(hierarchy, singleDNA, materialsFile):
|
|||
complete = False
|
||||
for b in materialsFile:
|
||||
if singleDNADict[a] == b:
|
||||
mat = select_material(materialsFile[b]['Material List'])
|
||||
deconstructed_MaterialDNA[a] = mat
|
||||
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)
|
||||
complete = True
|
||||
if not complete:
|
||||
deconstructed_MaterialDNA[a] = "0"
|
||||
|
||||
# This section is now incorrect and needs updating:
|
||||
|
||||
# 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])
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
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
|
||||
|
@ -14,7 +17,6 @@ def createDNArarity(hierarchy):
|
|||
for i in hierarchy:
|
||||
number_List_Of_i = []
|
||||
rarity_List_Of_i = []
|
||||
count = 0
|
||||
ifZeroBool = None
|
||||
|
||||
for k in hierarchy[i]:
|
||||
|
@ -24,18 +26,24 @@ 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))
|
||||
|
|
Ładowanie…
Reference in New Issue