so much stuff

This commit is contained in:
ABelliqueux 2021-03-01 20:05:38 +01:00
parent 9e1139ec6d
commit a8c6cfb1b1
1 changed files with 164 additions and 26 deletions

View File

@ -1,5 +1,6 @@
import os
import bpy
# bpy. app. debug = True
bl_info = {
"name": "PSX TMesh exporter",
@ -10,9 +11,11 @@ bl_info = {
"description": "Export psx data format",
"category": "Import-Export"
}
import os
import bpy
import unicodedata
from math import radians
from bpy.props import (CollectionProperty,
StringProperty,
@ -43,6 +46,13 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
min=1, max=1000,
default=65,
)
exp_Precalc = BoolProperty(
name="Use precalculated BGs",
description="Set the BGs UV to black",
default=False,
)
def execute(self, context):
import bmesh
from math import degrees, floor, cos, sin
@ -82,6 +92,56 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
scale = self.exp_Scale
# get working directory path
filepath = bpy.data.filepath
folder = os.path.dirname(bpy.path.abspath(filepath))
dirpath = os.path.join(folder, "TIM")
camAngles = []
# if using precalculated BG, render and export them to ./TIM/
if self.exp_Precalc:
# create folder if !exist
os.makedirs(dirpath, exist_ok = 1)
# file format config
bpy.context.scene.render.image_settings.file_format = 'PNG'
bpy.context.scene.render.image_settings.quality = 100
bpy.context.scene.render.image_settings.compression = 0
bpy.context.scene.render.image_settings.color_depth = '8'
# get current cam
cam = bpy.context.scene.camera
# store cam location and rot for restoration later
# ~ originLoc = cam.location
# ~ originRot = cam.rotation_euler
for o in bpy.data.objects:
if o.type == 'CAMERA' and o.name.startswith("camPath"):
# set cam as active - could be useful if multiple cam are present
bpy.context.scene.camera = o
# set cam Rot/Loc to empty rot/loc
# ~ cam.location = o.location
# ~ cam.rotation_euler = o.rotation_euler
# apply 90degrees rotation on local X axis, as EMPTYs are pointing to -Z (bottom of the screen) by default
# ~ cam.rotation_euler.rotate_axis('X', radians(90))
# render and save image
bpy.ops.render.render()
bpy.data.images["Render Result"].save_render(folder + os.sep + "TIM" + os.sep + "bg_" + CleanName(o.name) + "." + str(bpy.context.scene.render.image_settings.file_format).lower())
camAngles.append(o)
# set cam back to original pos/rot
# ~ cam.location = originLoc
# ~ cam.rotation_euler = originRot
f = open(os.path.normpath(self.filepath),"w+")
# write BODY struct def
@ -89,15 +149,21 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\tVECTOR gForce;\n" +
"\tVECTOR position;\n" +
"\tSVECTOR velocity;\n" +
"\tint mass;\n" +
"\tint invMass;\n" +
"\tVECTOR min; \n" +
"\tVECTOR max; \n" +
"\tint restitution; \n" +
"\t} BODY;\n\n")
# VERTEX ANIM struct
f.write("typedef struct { \n" +
"\tint nframes; // number of frames e.g 20\n" +
"\tint nvert; // number of vertices e.g 21\n" +
"\tint cursor; // anim cursor\n" +
"\tint lerpCursor; // anim cursor\n" +
"\tint dir; // playback direction (1 or -1)\n" +
"\tint interpolate; // use lerp to interpolate keyframes\n" +
"\tSVECTOR data[]; // vertex pos as SVECTORs e.g 20 * 21 SVECTORS\n" +
# ~ "\tSVECTOR normals[]; // vertex pos as SVECTORs e.g 20 * 21 SVECTORS\n" +
"\t} VANIM;\n\n")
@ -113,7 +179,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\tTMESH * tmesh;\n" +
"\tPRIM * index;\n" +
"\tTIM_IMAGE * tim; \n" +
"\tu_long * tim_data;\n"+
"\tunsigned long * tim_data;\n"+
"\tMATRIX * mat;\n" +
"\tVECTOR * pos;\n" +
"\tSVECTOR * rot;\n" +
@ -123,7 +189,9 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\tshort * isAnim;\n" +
"\tshort * isActor;\n" +
"\tshort * isLevel;\n" +
"\tshort * isBG;\n" +
"\tlong * p;\n" +
"\tlong * OTz;\n" +
"\tBODY * body;\n" +
"\tVANIM * anim;\n" +
"\t} MESH;\n\n")
@ -133,28 +201,39 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\tVECTOR pos;\n" +
"\tSVECTOR rot;\n" +
"\t} CAMPOS;\n\n" +
"\n// Blender cam ~= PSX cam with these settings : TV NTSC 4:3, Cam focal length : 100° ( 13.43 mm ))\n")
"\n// Blender cam ~= PSX cam with these settings : NTSC - 320x240, PAL 320x256, pixel ratio 1:1, cam focal length : perspective 90° ( 16 mm ))\n\n")
# CAM ANGLE
f.write("typedef struct {\n" +
"\tCAMPOS * campos;\n" +
"\tTIM_IMAGE * BGtim;\n" +
"\tunsigned long * tim_data;\n" +
"\t} CAMANGLE;\n\n")
# CAM PATH struct
f.write("typedef struct {\n" +
"\tshort len, cursor;\n" +
"\tshort len, cursor, pos;\n" +
"\tVECTOR points[];\n" +
"\t} CAMPATH;\n\n")
camPathPoints = []
defaultCam = 'NULL'
first_mesh = CleanName(bpy.data.meshes[0].name)
# set camera position and rotation in the scene
for o in range(len(bpy.data.objects)):
if bpy.data.objects[o].type == 'CAMERA' and bpy.data.objects[o].data.get('isDefault'):
defaultCam = bpy.data.objects[o].name
if bpy.data.objects[o].type == 'CAMERA':
f.write("CAMPOS camStartPos = {\n" +
f.write("CAMPOS camPos_" + CleanName(bpy.data.objects[o].name) + " = {\n" +
"\t{" + str(round(-bpy.data.objects[o].location.x * scale)) + "," + str(round(bpy.data.objects[o].location.z * scale)) + "," +str(round(-bpy.data.objects[o].location.y * scale)) + "},\n" +
"\t{" + str(round(-(degrees(bpy.data.objects[o].rotation_euler.x)-90)/360 * 4096)) + "," + str(round(degrees(bpy.data.objects[o].rotation_euler.z)/360 * 4096)) + "," + str(round(-(degrees(bpy.data.objects[o].rotation_euler.y))/360 * 4096)) + "},\n" +
"\t{" + str(round(-(degrees(bpy.data.objects[o].rotation_euler.x)-90)/360 * 4096)) + "," + str(round(degrees(bpy.data.objects[o].rotation_euler.z)/360 * 4096)) + "," + str(round(-(degrees(bpy.data.objects[o].rotation_euler.y))/360 * 4096)) + "}\n" +
"};\n\n")
# find camStart and camEnd empties for camera trajectory
if bpy.data.objects[o].type == 'EMPTY' :
if bpy.data.objects[o].name.startswith("camPath"):
if bpy.data.objects[o].type == 'CAMERA' :
if bpy.data.objects[o].name.startswith("camPath") and not bpy.data.objects[o].data.get('isDefault'):
camPathPoints.append(bpy.data.objects[o].name)
if camPathPoints:
@ -166,6 +245,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
f.write("CAMPATH camPath = {\n" +
"\t" + str(len(camPathPoints)) + ",\n" +
"\t0,\n" +
"\t0,\n" +
"\t{\n")
f.write("\t\t{" + str(round(-bpy.data.objects[camPathPoints[p]].location.x * scale)) + "," + str(round(bpy.data.objects[camPathPoints[p]].location.z * scale)) + "," +str(round(-bpy.data.objects[camPathPoints[p]].location.y * scale)) + "}")
if p != len(camPathPoints) - 1:
@ -296,6 +376,8 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# get image size x, y
# print(bpy.data.meshes[0].uv_textures[0].data[0].image.size[0]) # x
# print(bpy.data.meshes[0].uv_textures[0].data[0].image.size[1]) # y
if len(m.uv_textures) != None:
for t in range(len(m.uv_textures)):
if m.uv_textures[t].data[0].image != None:
@ -308,7 +390,10 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
u = uv_layer[i].uv
ux = u.x * tex_width
uy = u.y * tex_height
f.write("\t"+str(max(0, min( round(ux) , 255 )))+","+str(max(0, min(round(tex_height - uy) , 255 )))+", 0, 0") # Clamp values to 0-255 to avoid tpage overflow
if self.exp_Precalc and m.get('isBG'):
f.write("\t255, 255, 0, 0") # Clamp values to 0-255 to avoid tpage overflow
else:
f.write("\t"+str(max(0, min( round(ux) , 255 )))+","+str(max(0, min(round(tex_height - uy) , 255 )))+", 0, 0") # Clamp values to 0-255 to avoid tpage overflow
if i != len(uv_layer) - 1:
f.write(",")
f.write("\n")
@ -316,7 +401,8 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# save uv tex if needed - still have to convert them to tim...
if texture_image.filepath == '':
texture_image.filepath_raw = "./" + CleanName(texture_image.name) + "." + texture_image.file_format
os.makedirs(dirpath, exist_ok = 1)
texture_image.filepath_raw = folder + os.sep + "TIM" + os.sep + CleanName(texture_image.name) + "." + texture_image.file_format
texture_image.save()
# Write vertex colors vectors
@ -366,8 +452,10 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
'isPrism':0,
'isActor':0,
'isLevel':0,
'isBG':0,
'mass': 1,
'restitution': 0
'restitution': 0,
'lerp': 0
}
for prop in chkProp:
@ -390,8 +478,10 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
o = bpy.data.objects[m.name]
frame_start = bpy.context.scene.frame_start
frame_end = bpy.context.scene.frame_end
# ~ frame_start = bpy.context.scene.frame_start
frame_start = int(bpy.data.actions[m.name].frame_range[0])
# ~ frame_end = bpy.context.scene.frame_end
frame_end = int(bpy.data.actions[m.name].frame_range[1])
nFrame = frame_end - frame_start
@ -402,7 +492,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
tmp_meshes = []
for i in range(frame_start - 1, frame_end):
for i in range(frame_start, frame_end):
bpy.context.scene.frame_set(i)
bpy.context.scene.update()
@ -413,6 +503,10 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
f.write("VANIM model"+cleanName+"_anim = {\n" +
"\t" + str(nFrame) + ",\n" +
"\t" + str(len(nm.vertices)) + ",\n" +
"\t0,\n" +
"\t0,\n" +
"\t1,\n" +
"\t" + str(chkProp['lerp']) + ",\n" +
"\t{\n"
)
for v in range(len(nm.vertices)):
@ -420,7 +514,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# ~ f.write("{\n")
f.write("\t\t//Frame %d\n" % i)
f.write("\t\t{ " + str(round(nm.vertices[v].co.x*scale)) + "," + str(round(-nm.vertices[v].co.z*scale)) + "," + str(round(nm.vertices[v].co.y*scale)) + " }")
if c != len(nm.vertices) * (nFrame + 1) * 3 - 3:
if c != len(nm.vertices) * (nFrame) * 3 - 3:
f.write(",\n")
if v == len(nm.vertices) - 1:
f.write("\n")
@ -477,18 +571,21 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
#write object matrix, rot and pos vectors
f.write("MATRIX model"+cleanName+"_matrix = {0};\n" +
"VECTOR model"+cleanName+"_pos = {"+ str(round(bpy.data.objects[m.name].location.x * scale)) + "," + str(round(-bpy.data.objects[m.name].location.z * scale)) + "," + str(round(bpy.data.objects[m.name].location.y * scale)) + ", 0};\n" +
"SVECTOR model"+cleanName+"_rot = {"+ str(round(degrees(bpy.data.objects[m.name].rotation_euler.x)/360 * 4096)) + "," + str(round(degrees(-bpy.data.objects[m.name].rotation_euler.z)/360 * 4096)) + "," + str(round(degrees(bpy.data.objects[m.name].rotation_euler.y)/360 * 4096)) + "};\n" +
"short model"+cleanName+"_isRigidBody =" + str(int(chkProp['isRigidBody'])) + ";\n" +
"short model"+cleanName+"_isStaticBody =" + str(int(chkProp['isStaticBody'])) + ";\n" +
"short model"+cleanName+"_isPrism =" + str(int(chkProp['isPrism'])) + ";\n" +
"short model"+cleanName+"_isAnim =" + str(int(chkProp['isAnim'])) + ";\n" +
"short model"+cleanName+"_isActor =" + str(int(chkProp['isActor'])) + ";\n" +
"short model"+cleanName+"_isLevel =" + str(int(chkProp['isLevel'])) + ";\n" +
"SVECTOR model"+cleanName+"_rot = {"+ str(round(degrees(bpy.data.objects[m.name].rotation_euler.x)/360 * 4096)) + "," + str(round(degrees(bpy.data.objects[m.name].rotation_euler.z)/360 * 4096)) + "," + str(round(degrees(bpy.data.objects[m.name].rotation_euler.y)/360 * 4096)) + "};\n" +
"short model"+cleanName+"_isRigidBody = " + str(int(chkProp['isRigidBody'])) + ";\n" +
"short model"+cleanName+"_isStaticBody = " + str(int(chkProp['isStaticBody'])) + ";\n" +
"short model"+cleanName+"_isPrism = " + str(int(chkProp['isPrism'])) + ";\n" +
"short model"+cleanName+"_isAnim = " + str(int(chkProp['isAnim'])) + ";\n" +
"short model"+cleanName+"_isActor = " + str(int(chkProp['isActor'])) + ";\n" +
"short model"+cleanName+"_isLevel = " + str(int(chkProp['isLevel'])) + ";\n" +
"short model"+cleanName+"_isBG = " + str(int(chkProp['isBG'])) + ";\n" +
"long model"+cleanName+"_p = 0;\n" +
"long model"+cleanName+"_OTz = 0;\n" +
"BODY model"+cleanName+"_body = {\n" +
"\t{0, 0, 0, 0},\n" +
"\t" + str(round(bpy.data.objects[m.name].location.x * scale)) + "," + str(round(-bpy.data.objects[m.name].location.z * scale)) + "," + str(round(bpy.data.objects[m.name].location.y * scale)) + ", 0,\n" +
"\t"+ str(round(degrees(bpy.data.objects[m.name].rotation_euler.x)/360 * 4096)) + "," + str(round(degrees(-bpy.data.objects[m.name].rotation_euler.z)/360 * 4096)) + "," + str(round(degrees(bpy.data.objects[m.name].rotation_euler.y)/360 * 4096)) + ", 0,\n" +
"\t" + str(int(chkProp['mass'])) + ",\n" +
"\tONE/" + str(int(chkProp['mass'])) + ",\n" +
# write min and max values of AABBs on each axis
"\t" + str(round(min(Xvals) * scale)) + "," + str(round(min(Zvals) * scale)) + "," + str(round(min(Yvals) * scale)) + ", 0,\n" +
@ -556,7 +653,9 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\t&model"+cleanName+"_isAnim,\n" +
"\t&model"+cleanName+"_isActor,\n" +
"\t&model"+cleanName+"_isLevel,\n" +
"\t&model"+cleanName+"_isBG,\n" +
"\t&model"+cleanName+"_p,\n" +
"\t&model"+cleanName+"_OTz,\n" +
"\t&model"+cleanName+"_body")
if m.get("isAnim") is not None and m["isAnim"] != 0:
f.write(",\n\t&model"+cleanName+"_anim\n")
@ -574,11 +673,50 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
if k != len(bpy.data.meshes) - 1:
f.write(",\n")
f.write("\n}; \n")
# nothing in camAngles, use default camera
if not camAngles:
f.write("CAMANGLE camAngle_" + CleanName(defaultCam) + " = {\n" +
"\t&camPos_" + CleanName(defaultCam) + ",\n" +
"\t0,\n" +
"\t0\n" +
"};\n\n")
# cam angles is populated
for o in camAngles:
prefix = CleanName(o.name)
# include Tim data
f.write("extern unsigned long "+"_binary_TIM_bg_" + prefix + "_tim_start[];\n")
f.write("extern unsigned long "+"_binary_TIM_bg_" + prefix + "_tim_end[];\n")
f.write("extern unsigned long "+"_binary_TIM_bg_" + prefix + "_tim_length;\n\n")
# write corresponding TIM_IMAGE struct
f.write("TIM_IMAGE tim_bg_" + prefix + ";\n\n")
# write corresponding CamAngle struct
f.write("CAMANGLE camAngle_" + prefix + " = {\n" +
"\t&camPos_" + prefix + ",\n" +
"\t&tim_bg_" + prefix + ",\n" +
"\t_binary_TIM_bg_" + prefix + "_tim_start\n" +
"};\n\n")
# write cam angles array for loops
f.write("CAMANGLE * camAngles[" + str(len(camAngles)) + "] = {\n")
for o in camAngles:
prefix = CleanName(o.name)
f.write("\t&camAngle_" + prefix + ",\n")
f.write("};\n\n")
f.write("MESH * actorPtr = &mesh" + actorPtr + ";\n")
f.write("MESH * levelPtr = &mesh" + levelPtr + ";\n")
f.write("MESH * propPtr = &mesh" + propPtr + ";\n")
f.write("MESH * propPtr = &mesh" + propPtr + ";\n\n")
# ~ if self.exp_Precalc:
f.write("CAMANGLE * camPtr = &camAngle_" + CleanName(defaultCam) + ";\n\n")
# ~ else :
# ~ f.write("CAMPOS * camPtr = &camPos_" + CleanName(defaultCam) + ";\n\n")
f.close()
return {'FINISHED'};