2020-12-27 17:13:58 +01:00
|
|
|
import os
|
|
|
|
import bpy
|
|
|
|
|
|
|
|
bl_info = {
|
|
|
|
"name": "PSX TMesh exporter",
|
|
|
|
"author": "Schnappy, TheDukeOfZill",
|
|
|
|
"blender": (2,7,9),
|
|
|
|
"version": (0,0,2),
|
|
|
|
"location": "File > Import-Export",
|
|
|
|
"description": "Export psx data format",
|
|
|
|
"category": "Import-Export"
|
|
|
|
}
|
|
|
|
|
|
|
|
import bpy
|
2021-02-19 18:00:42 +01:00
|
|
|
import unicodedata
|
|
|
|
|
|
|
|
from bpy.props import (CollectionProperty,
|
|
|
|
StringProperty,
|
|
|
|
BoolProperty,
|
|
|
|
EnumProperty,
|
|
|
|
FloatProperty
|
|
|
|
)
|
|
|
|
|
|
|
|
from bpy_extras.io_utils import (ExportHelper,
|
|
|
|
axis_conversion
|
|
|
|
)
|
2020-12-27 17:13:58 +01:00
|
|
|
|
|
|
|
class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
|
|
|
bl_idname = "export_psx.c";
|
2021-02-19 18:00:42 +01:00
|
|
|
bl_label = "PSX compatible scene exporter";
|
2020-12-27 17:13:58 +01:00
|
|
|
bl_options = {'PRESET'};
|
|
|
|
filename_ext = ".c";
|
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
exp_Triangulate = BoolProperty(
|
|
|
|
name="Triangulate meshes ( Destructive ! )",
|
|
|
|
description="Triangulate meshes (destructive ! Do not use your original file)",
|
|
|
|
default=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
exp_Scale = FloatProperty(
|
|
|
|
name="Scale",
|
|
|
|
description="Scale of exported mesh.",
|
|
|
|
min=1, max=1000,
|
|
|
|
default=65,
|
|
|
|
)
|
2020-12-27 17:13:58 +01:00
|
|
|
def execute(self, context):
|
2020-12-30 22:09:12 +01:00
|
|
|
import bmesh
|
2021-02-02 12:15:11 +01:00
|
|
|
from math import degrees, floor, cos, sin
|
|
|
|
from mathutils import Vector
|
2020-12-30 22:09:12 +01:00
|
|
|
|
|
|
|
def triangulate_object(obj): # Stolen from here : https://blender.stackexchange.com/questions/45698/triangulate-mesh-in-python/45722#45722
|
|
|
|
me = obj.data
|
|
|
|
# Get a BMesh representation
|
|
|
|
bm = bmesh.new()
|
|
|
|
bm.from_mesh(me)
|
|
|
|
bmesh.ops.triangulate(bm, faces=bm.faces[:], quad_method=0, ngon_method=0)
|
|
|
|
# Finish up, write the bmesh back to the mesh
|
|
|
|
bm.to_mesh(me)
|
|
|
|
bm.free()
|
2021-02-19 18:00:42 +01:00
|
|
|
# ~ return bm
|
2020-12-29 15:01:44 +01:00
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
def CleanName(strName):
|
|
|
|
name = strName.replace(' ','_')
|
|
|
|
name = name.replace('.','_')
|
|
|
|
name = unicodedata.normalize('NFKD',name).encode('ASCII', 'ignore').decode()
|
|
|
|
return name
|
|
|
|
|
2020-12-29 15:01:44 +01:00
|
|
|
# Leave edit mode to avoid errors
|
2020-12-29 15:53:47 +01:00
|
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
2020-12-30 22:09:12 +01:00
|
|
|
|
|
|
|
# triangulate objects of type mesh
|
2021-02-19 18:00:42 +01:00
|
|
|
if self.exp_Triangulate:
|
|
|
|
for o in range(len(bpy.data.objects)):
|
|
|
|
if bpy.data.objects[o].type == 'MESH':
|
|
|
|
triangulate_object(bpy.data.objects[o])
|
|
|
|
|
|
|
|
# ~ obj = bpy.data.objects[bpy.data.objects[o].name]
|
|
|
|
|
|
|
|
# ~ tm = obj.to_mesh(bpy.context.scene, False, 'PREVIEW')
|
|
|
|
|
|
|
|
# ~ work_meshes.append(tm)
|
|
|
|
|
|
|
|
scale = self.exp_Scale
|
2020-12-30 22:09:12 +01:00
|
|
|
|
2020-12-27 17:13:58 +01:00
|
|
|
f = open(os.path.normpath(self.filepath),"w+")
|
2020-12-30 11:23:36 +01:00
|
|
|
|
2021-01-14 17:22:44 +01:00
|
|
|
# write BODY struct def
|
|
|
|
f.write("typedef struct {\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\tVECTOR gForce;\n" +
|
2021-01-14 17:22:44 +01:00
|
|
|
"\tVECTOR position;\n" +
|
|
|
|
"\tSVECTOR velocity;\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\tint invMass;\n" +
|
2021-01-14 17:22:44 +01:00
|
|
|
"\tVECTOR min; \n" +
|
|
|
|
"\tVECTOR max; \n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\tint restitution; \n" +
|
2021-01-14 17:22:44 +01:00
|
|
|
"\t} BODY;\n\n")
|
2021-02-02 12:15:11 +01:00
|
|
|
# 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" +
|
|
|
|
"\tSVECTOR data[]; // vertex pos as SVECTORs e.g 20 * 21 SVECTORS\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
# ~ "\tSVECTOR normals[]; // vertex pos as SVECTORs e.g 20 * 21 SVECTORS\n" +
|
2021-02-02 12:15:11 +01:00
|
|
|
"\t} VANIM;\n\n")
|
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
# PRIM struc
|
|
|
|
f.write("typedef struct {\n" +
|
|
|
|
"\tVECTOR order;\n" +
|
|
|
|
"\tint code; // Same as POL3/POL4 codes : Code (F3 = 1, FT3 = 2, G3 = 3, GT3 = 4) Code (F4 = 5, FT4 = 6, G4 = 7, GT4 = 8)\n" +
|
|
|
|
"\t} PRIM;\n\n")
|
|
|
|
|
2021-02-02 12:15:11 +01:00
|
|
|
# MESH struct
|
2020-12-30 11:23:36 +01:00
|
|
|
f.write("typedef struct { \n"+
|
2021-01-04 18:28:27 +01:00
|
|
|
"\tTMESH * tmesh;\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\tPRIM * index;\n" +
|
2021-01-04 18:28:27 +01:00
|
|
|
"\tTIM_IMAGE * tim; \n" +
|
|
|
|
"\tu_long * tim_data;\n"+
|
|
|
|
"\tMATRIX * mat;\n" +
|
|
|
|
"\tVECTOR * pos;\n" +
|
|
|
|
"\tSVECTOR * rot;\n" +
|
2021-01-14 17:22:44 +01:00
|
|
|
"\tshort * isRigidBody;\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\tshort * isStaticBody;\n" +
|
2021-01-04 18:28:27 +01:00
|
|
|
"\tshort * isPrism;\n" +
|
2021-02-02 12:15:11 +01:00
|
|
|
"\tshort * isAnim;\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\tshort * isActor;\n" +
|
|
|
|
"\tshort * isLevel;\n" +
|
2021-01-04 18:28:27 +01:00
|
|
|
"\tlong * p;\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\tBODY * body;\n" +
|
|
|
|
"\tVANIM * anim;\n" +
|
2020-12-30 11:23:36 +01:00
|
|
|
"\t} MESH;\n\n")
|
|
|
|
|
2021-02-02 12:15:11 +01:00
|
|
|
# CAM POSITION struct
|
|
|
|
f.write("typedef struct {\n" +
|
|
|
|
"\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")
|
|
|
|
# CAM PATH struct
|
|
|
|
f.write("typedef struct {\n" +
|
|
|
|
"\tshort len, cursor;\n" +
|
|
|
|
"\tVECTOR points[];\n" +
|
|
|
|
"\t} CAMPATH;\n\n")
|
|
|
|
|
|
|
|
camPathPoints = []
|
2021-02-19 18:00:42 +01:00
|
|
|
|
|
|
|
first_mesh = CleanName(bpy.data.meshes[0].name)
|
2021-02-02 12:15:11 +01:00
|
|
|
|
|
|
|
# set camera position and rotation in the scene
|
|
|
|
for o in range(len(bpy.data.objects)):
|
|
|
|
if bpy.data.objects[o].type == 'CAMERA':
|
|
|
|
f.write("CAMPOS camStartPos = {\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" +
|
|
|
|
"};\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"):
|
|
|
|
camPathPoints.append(bpy.data.objects[o].name)
|
|
|
|
|
|
|
|
if camPathPoints:
|
|
|
|
|
|
|
|
# ~ camPathPoints = list(reversed(camPathPoints))
|
|
|
|
|
|
|
|
for p in range(len(camPathPoints)):
|
|
|
|
if p == 0:
|
|
|
|
f.write("CAMPATH camPath = {\n" +
|
|
|
|
"\t" + str(len(camPathPoints)) + ",\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:
|
|
|
|
f.write(",\n")
|
|
|
|
f.write("\n\t}\n};\n\n")
|
2021-02-19 18:00:42 +01:00
|
|
|
else:
|
|
|
|
f.write("CAMPATH camPath = {\n" +
|
|
|
|
"\t0,\n" +
|
|
|
|
"\t0,\n" +
|
|
|
|
"\t0\n" +
|
|
|
|
"};\n\n")
|
2021-02-02 12:15:11 +01:00
|
|
|
# Lights : max 3 sunlamp, no space coords
|
|
|
|
|
|
|
|
# LLM : Local Light Matrix
|
|
|
|
if len(bpy.data.lamps) is not None:
|
|
|
|
|
|
|
|
# ~ f.write( "static MATRIX lgtmat = {\n" +
|
|
|
|
# ~ "\t 4096, 4096, 4096,\n" +
|
|
|
|
# ~ "\t -4096, 4096, 4096,\n" +
|
|
|
|
# ~ "\t -4096, 4096, -4096\n" +
|
|
|
|
# ~ "};\n")
|
|
|
|
|
|
|
|
cnt = 0
|
|
|
|
pad = 3 - len(bpy.data.lamps)
|
|
|
|
|
|
|
|
f.write( "static MATRIX lgtmat = {\n")
|
|
|
|
|
|
|
|
for l in range(len(bpy.data.lamps)):
|
|
|
|
## intensity
|
|
|
|
energy = int(bpy.data.lamps[l].energy * 4096)
|
|
|
|
|
|
|
|
# Euler based
|
|
|
|
|
|
|
|
# get a direction vector from world matrix
|
|
|
|
lightdir = bpy.data.objects[bpy.data.lamps[l].name].matrix_world * Vector((0,0,-1,0))
|
|
|
|
|
|
|
|
|
|
|
|
f.write(
|
|
|
|
"\t" + str(int(lightdir.x * energy)) + "," +
|
|
|
|
"\t" + str(int(-lightdir.z * energy)) + "," +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\t" + str(int(lightdir.y * energy))
|
2021-02-02 12:15:11 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
if l != len(bpy.data.lamps) - 1:
|
|
|
|
f.write(",\n")
|
2021-02-19 18:00:42 +01:00
|
|
|
|
2021-02-02 12:15:11 +01:00
|
|
|
if pad:
|
2021-02-19 18:00:42 +01:00
|
|
|
f.write(",\n")
|
2021-02-02 12:15:11 +01:00
|
|
|
while cnt < pad:
|
|
|
|
f.write("\t0,0,0")
|
2021-02-19 18:00:42 +01:00
|
|
|
if cnt != pad:
|
2021-02-02 12:15:11 +01:00
|
|
|
f.write(",\n")
|
|
|
|
cnt += 1
|
|
|
|
|
|
|
|
f.write("\n\t};\n\n")
|
|
|
|
|
|
|
|
# LCM : Local Color Matrix
|
|
|
|
f.write( "static MATRIX cmat = {\n")
|
|
|
|
|
|
|
|
LCM = []
|
|
|
|
for l in bpy.data.lamps:
|
|
|
|
LCM.append(str(int(l.color.r * 4096) if l.color.r else 0))
|
|
|
|
LCM.append(str(int(l.color.g * 4096) if l.color.g else 0))
|
|
|
|
LCM.append(str(int(l.color.b * 4096) if l.color.b else 0))
|
|
|
|
|
|
|
|
if len(LCM) < 9:
|
|
|
|
while len(LCM) < 9:
|
|
|
|
LCM.append('0')
|
|
|
|
|
|
|
|
f.write(
|
|
|
|
"\t" + LCM[0] + "," + LCM[3] + "," + LCM[6] + ",\n" +
|
|
|
|
"\t" + LCM[1] + "," + LCM[4] + "," + LCM[7] + ",\n" +
|
|
|
|
"\t" + LCM[2] + "," + LCM[5] + "," + LCM[8] + "\n" )
|
|
|
|
f.write("\t};\n\n")
|
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
actorPtr = first_mesh
|
|
|
|
levelPtr = first_mesh
|
|
|
|
propPtr = first_mesh
|
|
|
|
|
2020-12-27 17:13:58 +01:00
|
|
|
for m in bpy.data.meshes:
|
|
|
|
|
2020-12-29 15:01:44 +01:00
|
|
|
# Write vertices vectors
|
2021-01-14 17:22:44 +01:00
|
|
|
|
|
|
|
# AABB : Store vertices coordinates by axis
|
|
|
|
Xvals = []
|
|
|
|
Yvals = []
|
|
|
|
Zvals = []
|
|
|
|
|
|
|
|
# remove '.' from mesh name
|
2021-02-19 18:00:42 +01:00
|
|
|
cleanName = CleanName(m.name)
|
|
|
|
# ~ cleanName = m.name.replace('.','_')
|
|
|
|
# ~ cleanName = unicodedata.normalize('NFKD',cleanName).encode('ASCII', 'ignore').decode()
|
2020-12-29 15:01:44 +01:00
|
|
|
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("SVECTOR "+"model"+cleanName+"_mesh[] = {\n")
|
2020-12-27 17:13:58 +01:00
|
|
|
for i in range(len(m.vertices)):
|
|
|
|
v = m.vertices[i].co
|
2021-01-14 17:22:44 +01:00
|
|
|
|
|
|
|
# AABB : append vertices coords by axis
|
|
|
|
Xvals.append(v.x)
|
|
|
|
Yvals.append(v.y)
|
2021-02-02 12:15:11 +01:00
|
|
|
Zvals.append(-v.z)
|
2021-01-14 17:22:44 +01:00
|
|
|
|
2021-02-02 12:15:11 +01:00
|
|
|
f.write("\t{"+str(round(v.x*scale))+","+str(round(-v.z*scale)) + "," + str(round(v.y*scale)) +"}")
|
2021-01-14 17:22:44 +01:00
|
|
|
|
2020-12-27 17:13:58 +01:00
|
|
|
if i != len(m.vertices) - 1:
|
|
|
|
f.write(",")
|
|
|
|
f.write("\n")
|
|
|
|
f.write("};\n\n")
|
2020-12-29 15:01:44 +01:00
|
|
|
|
|
|
|
# Write normals vectors
|
2020-12-27 17:13:58 +01:00
|
|
|
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("SVECTOR "+"model"+cleanName+"_normal[] = {\n")
|
2021-02-02 12:15:11 +01:00
|
|
|
for i in range(len(m.vertices)):
|
|
|
|
poly = m.vertices[i]
|
|
|
|
f.write("\t"+str(round(-poly.normal.x * 4096))+","+str(round(poly.normal.z * 4096))+","+str(round(-poly.normal.y * 4096))+",0")
|
2021-02-19 18:00:42 +01:00
|
|
|
if i != len(m.vertices) - 1:
|
2020-12-27 17:13:58 +01:00
|
|
|
f.write(",")
|
|
|
|
f.write("\n")
|
|
|
|
f.write("};\n\n")
|
|
|
|
|
2020-12-30 11:23:36 +01:00
|
|
|
# Write UVs vectors if a texture exists
|
|
|
|
|
2020-12-29 15:01:44 +01:00
|
|
|
# get name of texture image https://docs.blender.org/api/2.79b/bpy.types.Image.html#bpy.types.Image
|
|
|
|
# bpy.context.active_object.data.uv_textures.active.data[0].image.name
|
|
|
|
# bpy.context.active_object.data.uv_textures.active.data[0].image.filepath
|
|
|
|
# bpy.context.active_object.data.uv_textures.active.data[0].image.filepath_from_user()
|
|
|
|
#
|
|
|
|
# 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
|
2021-02-19 18:00:42 +01:00
|
|
|
if len(m.uv_textures) != None:
|
2021-01-04 18:28:27 +01:00
|
|
|
for t in range(len(m.uv_textures)):
|
|
|
|
if m.uv_textures[t].data[0].image != None:
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("SVECTOR "+"model"+cleanName+"_uv[] = {\n")
|
2021-01-04 18:28:27 +01:00
|
|
|
texture_image = m.uv_textures[t].data[0].image
|
|
|
|
tex_width = texture_image.size[0]
|
|
|
|
tex_height = texture_image.size[1]
|
|
|
|
uv_layer = m.uv_layers[0].data
|
|
|
|
for i in range(len(uv_layer)):
|
|
|
|
u = uv_layer[i].uv
|
|
|
|
ux = u.x * tex_width
|
|
|
|
uy = u.y * tex_height
|
2021-02-02 12:15:11 +01:00
|
|
|
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
|
2021-01-04 18:28:27 +01:00
|
|
|
if i != len(uv_layer) - 1:
|
|
|
|
f.write(",")
|
|
|
|
f.write("\n")
|
|
|
|
f.write("};\n\n")
|
2021-02-19 18:00:42 +01:00
|
|
|
|
|
|
|
# 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
|
|
|
|
texture_image.save()
|
|
|
|
|
2020-12-29 15:01:44 +01:00
|
|
|
# Write vertex colors vectors
|
|
|
|
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("CVECTOR "+"model"+cleanName+"_color[] = {\n")
|
2020-12-27 17:13:58 +01:00
|
|
|
# If vertex colors exist, use them
|
|
|
|
if len(m.vertex_colors) != 0:
|
2020-12-30 22:09:12 +01:00
|
|
|
colors = m.vertex_colors[0].data
|
2020-12-27 17:13:58 +01:00
|
|
|
for i in range(len(colors)):
|
|
|
|
f.write("\t"+str(int(colors[i].color.r*255))+","+str(int(colors[i].color.g*255))+","+str(int(colors[i].color.b*255))+", 0")
|
|
|
|
if i != len(colors) - 1:
|
|
|
|
f.write(",")
|
|
|
|
f.write("\n")
|
|
|
|
# If no vertex colors, default to 2 whites, 1 grey
|
|
|
|
else:
|
|
|
|
for i in range(len(m.polygons) * 3):
|
|
|
|
if i % 3 == 0:
|
2020-12-30 11:23:36 +01:00
|
|
|
f.write("\t80,80,80,0") # Let's add a bit more relief with a shade of grey
|
2020-12-27 17:13:58 +01:00
|
|
|
else:
|
2020-12-30 11:23:36 +01:00
|
|
|
f.write("\t128,128,128,0")
|
2020-12-27 17:13:58 +01:00
|
|
|
if i != (len(m.polygons) * 3) - 1:
|
|
|
|
f.write(",")
|
|
|
|
f.write("\n")
|
|
|
|
f.write("};\n\n")
|
2020-12-29 15:01:44 +01:00
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
# Write polygons index + type
|
|
|
|
f.write("PRIM "+"model"+cleanName+"_index[] = {\n")
|
2020-12-27 17:13:58 +01:00
|
|
|
for i in range(len(m.polygons)):
|
|
|
|
poly = m.polygons[i]
|
|
|
|
f.write("\t"+str(poly.vertices[0])+","+str(poly.vertices[1])+","+str(poly.vertices[2]))
|
2021-02-19 18:00:42 +01:00
|
|
|
|
|
|
|
if len(poly.vertices) > 3:
|
|
|
|
f.write("," + str(poly.vertices[3]) + ",8")
|
|
|
|
else:
|
|
|
|
f.write(",0,4")
|
|
|
|
|
2020-12-27 17:13:58 +01:00
|
|
|
if i != len(m.polygons) - 1:
|
|
|
|
f.write(",")
|
|
|
|
f.write("\n")
|
|
|
|
f.write("};\n\n")
|
2021-02-02 12:15:11 +01:00
|
|
|
|
|
|
|
# get custom properties isRigidBody, isPrism, isAnim
|
|
|
|
chkProp = {
|
|
|
|
'isAnim':0,
|
|
|
|
'isRigidBody':0,
|
2021-02-19 18:00:42 +01:00
|
|
|
'isStaticBody':0,
|
2021-02-02 12:15:11 +01:00
|
|
|
'isPrism':0,
|
2021-02-19 18:00:42 +01:00
|
|
|
'isActor':0,
|
|
|
|
'isLevel':0,
|
|
|
|
'mass': 1,
|
|
|
|
'restitution': 0
|
2021-02-02 12:15:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for prop in chkProp:
|
|
|
|
if m.get(prop) is not None:
|
|
|
|
chkProp[prop] = m[prop]
|
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
if m.get('isActor'):
|
|
|
|
actorPtr = cleanName
|
|
|
|
|
|
|
|
if m.get('isLevel'):
|
|
|
|
levelPtr = cleanName
|
|
|
|
|
|
|
|
if m.get('isProp'):
|
|
|
|
propPtr = cleanName
|
|
|
|
|
2021-02-02 12:15:11 +01:00
|
|
|
# write vertex anim if isAnim != 0 # https://stackoverflow.com/questions/9138637/vertex-animation-exporter-for-blender
|
|
|
|
if m.get("isAnim") is not None and m["isAnim"] != 0:
|
|
|
|
|
|
|
|
#write vertex pos
|
2021-02-19 18:00:42 +01:00
|
|
|
|
2021-02-02 12:15:11 +01:00
|
|
|
o = bpy.data.objects[m.name]
|
|
|
|
|
|
|
|
frame_start = bpy.context.scene.frame_start
|
|
|
|
frame_end = bpy.context.scene.frame_end
|
|
|
|
|
|
|
|
nFrame = frame_end - frame_start
|
|
|
|
|
|
|
|
# ~ f.write("int "+"model"+cleanName+"_anim_nframes = " + str(nFrame) + ";\n")
|
|
|
|
# ~ f.write("int "+"model"+cleanName+"_anim_nvert = {" + str(len(nm.vertices)) + "};")
|
|
|
|
# ~ f.write("SVECTOR "+"model"+cleanName+"_anim_data[")
|
|
|
|
c = 0;
|
|
|
|
|
|
|
|
tmp_meshes = []
|
|
|
|
|
|
|
|
for i in range(frame_start - 1, frame_end):
|
|
|
|
|
|
|
|
bpy.context.scene.frame_set(i)
|
|
|
|
bpy.context.scene.update()
|
|
|
|
|
|
|
|
nm = o.to_mesh(bpy.context.scene, True, 'PREVIEW')
|
2021-02-19 18:00:42 +01:00
|
|
|
|
2021-02-02 12:15:11 +01:00
|
|
|
if i == 0 :
|
2021-02-19 18:00:42 +01:00
|
|
|
f.write("VANIM model"+cleanName+"_anim = {\n" +
|
2021-02-02 12:15:11 +01:00
|
|
|
"\t" + str(nFrame) + ",\n" +
|
|
|
|
"\t" + str(len(nm.vertices)) + ",\n" +
|
|
|
|
"\t{\n"
|
|
|
|
)
|
|
|
|
for v in range(len(nm.vertices)):
|
|
|
|
if v == 0:
|
|
|
|
# ~ 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:
|
|
|
|
f.write(",\n")
|
|
|
|
if v == len(nm.vertices) - 1:
|
|
|
|
f.write("\n")
|
|
|
|
c += 3;
|
|
|
|
# ~ if i != (frame_end - frame_start):
|
|
|
|
# ~ f.write(",")
|
|
|
|
tmp_meshes.append(nm)
|
|
|
|
# ~ tmp_meshes.remove(nm)
|
2021-02-19 18:00:42 +01:00
|
|
|
f.write("\n\t}\n};\n")
|
2021-02-02 12:15:11 +01:00
|
|
|
|
|
|
|
for nm in tmp_meshes:
|
|
|
|
bpy.data.meshes.remove(nm)
|
2021-02-19 18:00:42 +01:00
|
|
|
|
|
|
|
# ~ c = 0
|
|
|
|
|
|
|
|
# ~ tmp_meshes_n = []
|
|
|
|
|
|
|
|
# ~ for i in range(frame_start - 1, frame_end):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ~ bpy.context.scene.frame_set(i)
|
|
|
|
# ~ bpy.context.scene.update()
|
|
|
|
|
|
|
|
# ~ nm_n = o.to_mesh(bpy.context.scene, True, 'PREVIEW')
|
|
|
|
|
|
|
|
# ~ if i == 0 :
|
|
|
|
# ~ f.write("\t{\n")
|
|
|
|
|
|
|
|
# ~ for v in range(len(nm_n.vertices)):
|
|
|
|
|
|
|
|
# ~ poly = m.polygons[v]
|
|
|
|
|
|
|
|
# ~ if v == 0:
|
|
|
|
# ~ f.write("\t\t//Frame %d\n" % i)
|
|
|
|
# ~ f.write("\t\t{ " + str(round(-poly.normal.x * 4096)) + "," + str(round(poly.normal.z * 4096)) + "," + str(round(-poly.normal.y * 4096) ) + ",0 }")
|
|
|
|
|
|
|
|
# ~ if c != len(nm_n.vertices) * (nFrame + 1) * 3 - 3:
|
|
|
|
# ~ f.write(",\n")
|
|
|
|
# ~ if v == len(nm_n.vertices) - 1:
|
|
|
|
# ~ f.write("\n")
|
|
|
|
# ~ c += 3;
|
|
|
|
|
|
|
|
# ~ tmp_meshes_n.append(nm_n)
|
|
|
|
# ~ f.write("\t\n}\n};\n")
|
|
|
|
|
|
|
|
|
|
|
|
# ~ for nm_n in tmp_meshes_n:
|
|
|
|
# ~ bpy.data.meshes.remove(nm_n)
|
2021-02-02 12:15:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
#Stuff # ~ bpy.data.objects[bpy.data.meshes[0].name].active_shape_key.value : access shape_key
|
|
|
|
|
2021-01-04 18:28:27 +01:00
|
|
|
#write object matrix, rot and pos vectors
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("MATRIX model"+cleanName+"_matrix = {0};\n" +
|
2021-02-02 12:15:11 +01:00
|
|
|
"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" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"short model"+cleanName+"_isStaticBody =" + str(int(chkProp['isStaticBody'])) + ";\n" +
|
2021-02-02 12:15:11 +01:00
|
|
|
"short model"+cleanName+"_isPrism =" + str(int(chkProp['isPrism'])) + ";\n" +
|
|
|
|
"short model"+cleanName+"_isAnim =" + str(int(chkProp['isAnim'])) + ";\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"short model"+cleanName+"_isActor =" + str(int(chkProp['isActor'])) + ";\n" +
|
|
|
|
"short model"+cleanName+"_isLevel =" + str(int(chkProp['isLevel'])) + ";\n" +
|
2021-01-14 17:22:44 +01:00
|
|
|
"long model"+cleanName+"_p = 0;\n" +
|
|
|
|
"BODY model"+cleanName+"_body = {\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\t{0, 0, 0, 0},\n" +
|
2021-02-02 12:15:11 +01:00
|
|
|
"\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" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\tONE/" + str(int(chkProp['mass'])) + ",\n" +
|
2021-01-14 17:22:44 +01:00
|
|
|
# write min and max values of AABBs on each axis
|
2021-02-02 12:15:11 +01:00
|
|
|
"\t" + str(round(min(Xvals) * scale)) + "," + str(round(min(Zvals) * scale)) + "," + str(round(min(Yvals) * scale)) + ", 0,\n" +
|
|
|
|
"\t" + str(round(max(Xvals) * scale)) + "," + str(round(max(Zvals) * scale)) + "," + str(round(max(Yvals) * scale)) + ", 0,\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\t" + str(int(chkProp['restitution'])) + "\n" +
|
2021-01-14 17:22:44 +01:00
|
|
|
"\t};\n\n")
|
2021-01-04 18:28:27 +01:00
|
|
|
|
2020-12-29 15:01:44 +01:00
|
|
|
# Write TMESH struct
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("TMESH "+"model"+cleanName+" = {\n")
|
|
|
|
f.write("\t"+"model"+cleanName+"_mesh, \n")
|
|
|
|
f.write("\t"+"model"+cleanName+"_normal,\n")
|
2020-12-30 22:09:12 +01:00
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
if len(m.uv_textures) != None:
|
2021-01-04 18:28:27 +01:00
|
|
|
for t in range(len(m.uv_textures)):
|
|
|
|
if m.uv_textures[0].data[0].image != None:
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("\t"+"model"+cleanName+"_uv,\n")
|
2021-02-19 18:00:42 +01:00
|
|
|
else:
|
|
|
|
f.write("\t0,\n")
|
2020-12-30 11:23:36 +01:00
|
|
|
else:
|
|
|
|
f.write("\t0,\n")
|
2020-12-30 22:09:12 +01:00
|
|
|
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("\t"+"model"+cleanName+"_color, \n")
|
2020-12-29 15:01:44 +01:00
|
|
|
|
2020-12-27 17:13:58 +01:00
|
|
|
# According to libgte.h, TMESH.len should be # of vertices. Meh...
|
|
|
|
f.write("\t"+str(len(m.polygons))+"\n")
|
2020-12-29 15:01:44 +01:00
|
|
|
f.write("};\n\n")
|
|
|
|
|
|
|
|
# write texture binary name and declare TIM_IMAGE
|
|
|
|
# by default, load the file from the TIM folder
|
2020-12-30 22:09:12 +01:00
|
|
|
# ~ if len(m.uv_textures) != 0:
|
2021-02-19 18:00:42 +01:00
|
|
|
if len(m.uv_textures) != None:
|
2021-01-04 18:28:27 +01:00
|
|
|
for t in range(len(m.uv_textures)):
|
|
|
|
if m.uv_textures[0].data[0].image != None:
|
|
|
|
tex_name = texture_image.name
|
|
|
|
prefix = str.partition(tex_name, ".")[0].replace('-','_')
|
2021-02-19 18:00:42 +01:00
|
|
|
prefix = CleanName(prefix)
|
|
|
|
|
2021-01-04 18:28:27 +01:00
|
|
|
f.write("extern unsigned long "+"_binary_TIM_" + prefix + "_tim_start[];\n")
|
|
|
|
f.write("extern unsigned long "+"_binary_TIM_" + prefix + "_tim_end[];\n")
|
|
|
|
f.write("extern unsigned long "+"_binary_TIM_" + prefix + "_tim_length;\n\n")
|
|
|
|
f.write("TIM_IMAGE tim_" + prefix + ";\n\n")
|
|
|
|
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("MESH mesh"+cleanName+" = {\n")
|
|
|
|
f.write("\t&model"+ cleanName +",\n")
|
|
|
|
f.write("\tmodel" + cleanName + "_index,\n")
|
2020-12-30 22:09:12 +01:00
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
if len(m.uv_textures) != None:
|
2021-01-04 18:28:27 +01:00
|
|
|
for t in range(len(m.uv_textures)):
|
|
|
|
if m.uv_textures[0].data[0].image != None:
|
|
|
|
f.write("\t&tim_"+ prefix + ",\n")
|
2021-02-19 18:00:42 +01:00
|
|
|
f.write("\t_binary_TIM_" + prefix + "_tim_start,\n")
|
|
|
|
else:
|
|
|
|
f.write("\t0,\n" +
|
|
|
|
"\t0,\n")
|
2020-12-30 22:09:12 +01:00
|
|
|
else:
|
2021-01-04 18:28:27 +01:00
|
|
|
f.write("\t0,\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\t0,\n")
|
|
|
|
|
2021-01-14 17:22:44 +01:00
|
|
|
f.write("\t&model"+cleanName+"_matrix,\n" +
|
|
|
|
"\t&model"+cleanName+"_pos,\n" +
|
|
|
|
"\t&model"+cleanName+"_rot,\n" +
|
|
|
|
"\t&model"+cleanName+"_isRigidBody,\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\t&model"+cleanName+"_isStaticBody,\n" +
|
2021-01-14 17:22:44 +01:00
|
|
|
"\t&model"+cleanName+"_isPrism,\n" +
|
2021-02-02 12:15:11 +01:00
|
|
|
"\t&model"+cleanName+"_isAnim,\n" +
|
2021-02-19 18:00:42 +01:00
|
|
|
"\t&model"+cleanName+"_isActor,\n" +
|
|
|
|
"\t&model"+cleanName+"_isLevel,\n" +
|
2021-02-02 12:15:11 +01:00
|
|
|
"\t&model"+cleanName+"_p,\n" +
|
|
|
|
"\t&model"+cleanName+"_body")
|
|
|
|
if m.get("isAnim") is not None and m["isAnim"] != 0:
|
|
|
|
f.write(",\n\t&model"+cleanName+"_anim\n")
|
|
|
|
else:
|
|
|
|
f.write("\n")
|
2021-01-04 18:28:27 +01:00
|
|
|
|
2020-12-30 11:23:36 +01:00
|
|
|
f.write("};\n\n")
|
|
|
|
|
|
|
|
f.write("MESH * meshes[" + str(len(bpy.data.meshes)) + "] = {\n")
|
2021-02-19 18:00:42 +01:00
|
|
|
for k in range(len(bpy.data.meshes)):
|
|
|
|
cleanName = CleanName(bpy.data.meshes[k].name)
|
|
|
|
# ~ cleanName = bpy.data.meshes[k].name.replace('.','_')
|
|
|
|
# ~ cleanName = unicodedata.normalize('NFKD',cleanName).encode('ASCII', 'ignore').decode()
|
|
|
|
f.write("\t&mesh" + cleanName)
|
2020-12-30 11:23:36 +01:00
|
|
|
if k != len(bpy.data.meshes) - 1:
|
|
|
|
f.write(",\n")
|
|
|
|
f.write("\n}; \n")
|
|
|
|
|
2021-02-19 18:00:42 +01:00
|
|
|
f.write("MESH * actorPtr = &mesh" + actorPtr + ";\n")
|
|
|
|
f.write("MESH * levelPtr = &mesh" + levelPtr + ";\n")
|
|
|
|
f.write("MESH * propPtr = &mesh" + propPtr + ";\n")
|
|
|
|
|
|
|
|
|
2020-12-27 17:13:58 +01:00
|
|
|
f.close()
|
|
|
|
return {'FINISHED'};
|
|
|
|
|
|
|
|
def menu_func(self, context):
|
|
|
|
self.layout.operator(ExportMyFormat.bl_idname, text="PSX Format(.c)");
|
|
|
|
|
|
|
|
def register():
|
|
|
|
bpy.utils.register_module(__name__);
|
|
|
|
bpy.types.INFO_MT_file_export.append(menu_func);
|
|
|
|
|
|
|
|
def unregister():
|
|
|
|
bpy.utils.unregister_module(__name__);
|
|
|
|
bpy.types.INFO_MT_file_export.remove(menu_func);
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
register()
|