mirror of
https://github.com/Theaninova/CraftStudioExport.git
synced 2025-12-10 08:26:19 +00:00
Initial commit
This commit is contained in:
8
.idea/CraftStudioExport.iml
generated
Normal file
8
.idea/CraftStudioExport.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
4
.idea/encodings.xml
generated
Normal file
4
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||
</project>
|
||||
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/CraftStudioExport.iml" filepath="$PROJECT_DIR$/.idea/CraftStudioExport.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
185
__init__.py
Normal file
185
__init__.py
Normal file
@@ -0,0 +1,185 @@
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
bl_info = {
|
||||
"name" : "CraftStudio (.csmodel)",
|
||||
"author" : "Thea Schöbl",
|
||||
"blender" : (2, 80, 0),
|
||||
"version" : (0, 0, 1),
|
||||
"location" : "File > Export > CraftStudio (.csmodel)",
|
||||
"description" : "Export mesh to CraftStudio Model (.csmodel)",
|
||||
"warning" : "",
|
||||
"category" : "Import-Export"
|
||||
}
|
||||
|
||||
import bpy
|
||||
from bpy_extras.io_utils import ExportHelper
|
||||
from mathutils import Vector
|
||||
import time
|
||||
import struct
|
||||
|
||||
FILE_SIGNATURE = bytes([0x00, 0x05, 0x00])
|
||||
|
||||
# This is where the magic happenes
|
||||
def do_export(context, props, filepath):
|
||||
file = open(filepath, "wb")
|
||||
|
||||
file.write(FILE_SIGNATURE) #File signature
|
||||
|
||||
model_collection = bpy.data.collections['CSMODEL']
|
||||
current_id = 0
|
||||
|
||||
#TODO: determine the amount of blocks
|
||||
amount_of_blocks = len(model_collection.all_objects)
|
||||
file.write(amount_of_blocks.to_bytes(2, byteorder='little', signed=False))
|
||||
file.write(amount_of_blocks.to_bytes(2, byteorder='little', signed=False))
|
||||
#Yes, we actually need it twice for whatever reason. I didn't invent this format.
|
||||
|
||||
name_id_dict = {}
|
||||
name_pivot_dict = {}
|
||||
|
||||
for obj in model_collection.all_objects:
|
||||
name_id_dict[obj.data.name] = current_id #Save the object to the dictonary for parenting
|
||||
name_pivot_dict[obj.data.name] = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector()) #Calculate the center of the object
|
||||
current_id = current_id + 1 #Increment ID, as each one is unique
|
||||
|
||||
#START DOING BLOCK STUFF
|
||||
for obj in model_collection.all_objects:
|
||||
file.write(name_id_dict[obj.data.name].to_bytes(2, byteorder='little', signed=False)) #Write the ID
|
||||
|
||||
#Check Parents
|
||||
if obj.parent is not None and obj.parent.data is not None:
|
||||
#Check and write parent ID
|
||||
file.write(name_id_dict[obj.parent.data.name].to_bytes(2, byteorder='little', signed=False))
|
||||
else:
|
||||
file.write(0xFFFF.to_bytes(2, byteorder='little', signed=False)) #No parent
|
||||
|
||||
#Write Name
|
||||
file.write(len(obj.data.name).to_bytes(1, byteorder='little', signed=False))
|
||||
if len(obj.data.name) >= 0xFF: #TODO: check if that is correct
|
||||
print('FATAL ERROR: name is longer than 16 characters')
|
||||
return False
|
||||
file.write(str.encode(obj.data.name))
|
||||
|
||||
#Write Position
|
||||
obj_location = obj.location if obj.parent is None or obj.parent.data is None else obj.location - name_pivot_dict[obj.parent.data.name]
|
||||
for val in obj_location:
|
||||
file.write(bytearray(struct.pack("f", val/16)))
|
||||
|
||||
#Write Pivot Offset
|
||||
object_center = name_pivot_dict[obj.data.name]
|
||||
for val in object_center:
|
||||
file.write(bytearray(struct.pack("f", val/16)))
|
||||
|
||||
#Size as Float
|
||||
size = obj.dimensions #Just take the dimensions
|
||||
for val in size:
|
||||
file.write(bytearray(struct.pack("f", val)))
|
||||
|
||||
#Rotation
|
||||
quaternion = obj.rotation_euler.to_quaternion()
|
||||
for val in quaternion:
|
||||
file.write(bytearray(struct.pack("f", val)))
|
||||
|
||||
#TODO: Proper Size
|
||||
block_size = [1, 1, 1]
|
||||
for val in block_size:
|
||||
file.write(val.to_bytes(2, byteorder='little', signed=False))
|
||||
|
||||
file.write(0x1.to_bytes(1, byteorder='little', signed=False))
|
||||
|
||||
#TODO: Proper UV Map
|
||||
def_uv = [0, 0]
|
||||
for i in range(0, 6):
|
||||
for val in def_uv:
|
||||
file.write(val.to_bytes(4, byteorder='little', signed=False)) #No parent
|
||||
|
||||
file.write(0x000000.to_bytes(3, byteorder='little', signed=False)) #TODO: figure out what this does
|
||||
file.write(0x000000.to_bytes(3, byteorder='little', signed=False)) #TODO: figure out what this does
|
||||
|
||||
#End of file or something like that, idk.
|
||||
file.write(0xB500.to_bytes(2, byteorder='big', signed=False)) #DON'T CHANGE BYTE ORDER
|
||||
file.write(0x0000.to_bytes(2, byteorder='little', signed=False))
|
||||
|
||||
file.flush()
|
||||
file.close()
|
||||
|
||||
return True
|
||||
|
||||
class Export_csmodel(bpy.types.Operator, ExportHelper):
|
||||
"""Export the active Object as a .csmodel file"""
|
||||
bl_idname = "export_shape.csmodel"
|
||||
bl_label = "Export CraftStudio Model (.csmodel)"
|
||||
|
||||
filename_ext = ".csmodel"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
return (
|
||||
obj is not None
|
||||
and obj.type in {'MESH'}
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
start_time = time.time()
|
||||
|
||||
props = self.properties
|
||||
filepath = self.filepath
|
||||
filepath = bpy.path.ensure_ext(filepath, self.filename_ext)
|
||||
|
||||
exported = do_export(context, props, filepath)
|
||||
|
||||
if exported:
|
||||
print('finished export in %s seconds' %
|
||||
((time.time() - start_time)))
|
||||
print(filepath)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
|
||||
if True:
|
||||
wm.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
elif True:
|
||||
wm.invoke_search_popup(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
elif False:
|
||||
return wm.invoke_props_popup(self, event)
|
||||
elif False:
|
||||
return self.execute(context)
|
||||
|
||||
def menu_func_export_button(self, context):
|
||||
self.layout.operator(Export_csmodel.bl_idname, text="CraftStudio Model (.csmodel)")
|
||||
|
||||
classes = [
|
||||
Export_csmodel,
|
||||
]
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export_button)
|
||||
|
||||
def unregister():
|
||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_button)
|
||||
|
||||
for cls in classes:
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
Reference in New Issue
Block a user