finish cleanup
This commit is contained in:
parent
55af8e8406
commit
c0ba209f87
@ -1,7 +1,5 @@
|
||||
# bpy. app. debug = True
|
||||
|
||||
|
||||
|
||||
bl_info = {
|
||||
"name": "PSX TMesh exporter",
|
||||
"author": "Schnappy, TheDukeOfZill",
|
||||
@ -13,12 +11,19 @@ bl_info = {
|
||||
}
|
||||
|
||||
import os
|
||||
|
||||
import bpy
|
||||
|
||||
import bmesh
|
||||
|
||||
import unicodedata
|
||||
|
||||
from math import radians, degrees, floor, cos, sin
|
||||
|
||||
from mathutils import Vector
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from bpy.props import (CollectionProperty,
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
@ -31,122 +36,282 @@ from bpy_extras.io_utils import (ExportHelper,
|
||||
)
|
||||
|
||||
class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
|
||||
bl_idname = "export_psx.c";
|
||||
|
||||
bl_label = "PSX compatible scene exporter";
|
||||
|
||||
bl_options = {'PRESET'};
|
||||
|
||||
filename_ext = ".c";
|
||||
|
||||
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,
|
||||
|
||||
)
|
||||
|
||||
exp_Precalc = BoolProperty(
|
||||
|
||||
name="Use precalculated BGs",
|
||||
|
||||
description="Set the BGs UV to black",
|
||||
|
||||
default=False,
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
def triangulate_object(obj): # Stolen from here : https://blender.stackexchange.com/questions/45698/triangulate-mesh-in-python/45722#45722
|
||||
def triangulate_object(obj):
|
||||
|
||||
# Triangulate an object's mesh
|
||||
# Source : 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()
|
||||
# ~ return bm
|
||||
|
||||
def CleanName(strName):
|
||||
|
||||
# Removes specials characters, dots ans space from string
|
||||
|
||||
name = strName.replace(' ','_')
|
||||
|
||||
name = name.replace('.','_')
|
||||
|
||||
name = unicodedata.normalize('NFKD',name).encode('ASCII', 'ignore').decode()
|
||||
|
||||
return name
|
||||
|
||||
def isInPlane(plane, obj):
|
||||
|
||||
# Checks if 'obj' has its coordinates contained between the plane's coordinate.
|
||||
# If 'obj' is partly contained, returns which side (S, W, N, E) it's overlapping.
|
||||
# If 'obj' is not contained in 'plane', returns 0.
|
||||
|
||||
if (
|
||||
(LvlPlanes[plane]['x1'] < LvlObjects[obj]['x1'] and LvlPlanes[plane]['x2'] > LvlObjects[obj]['x2']) and
|
||||
(LvlPlanes[plane]['y1'] < LvlObjects[obj]['y1'] and LvlPlanes[plane]['y2'] > LvlObjects[obj]['y2'])
|
||||
):
|
||||
|
||||
return 1
|
||||
|
||||
# Overlap on the West side of the plane
|
||||
if (
|
||||
( LvlPlanes[plane]['x1'] > LvlObjects[obj]['x1'] and LvlPlanes[plane]['x1'] < LvlObjects[obj]['x2'] ) and
|
||||
( LvlPlanes[plane]['y1'] < LvlObjects[obj]['y1'] and LvlPlanes[plane]['y2'] > LvlObjects[obj]['y2'] )
|
||||
):
|
||||
|
||||
return 4
|
||||
|
||||
# Overlap on the East side of the plane
|
||||
if (
|
||||
( LvlPlanes[plane]['x2'] < LvlObjects[obj]['x2'] and LvlPlanes[plane]['x2'] > LvlObjects[obj]['x1'] ) and
|
||||
( LvlPlanes[plane]['y1'] < LvlObjects[obj]['y1'] and LvlPlanes[plane]['y2'] > LvlObjects[obj]['y2'] )
|
||||
):
|
||||
|
||||
return 6
|
||||
|
||||
# Overlap on the North side of the plane
|
||||
if (
|
||||
( LvlPlanes[plane]['y2'] < LvlObjects[obj]['y2'] and LvlPlanes[plane]['y2'] > LvlObjects[obj]['y1'] ) and
|
||||
( LvlPlanes[plane]['x1'] < LvlObjects[obj]['x1'] and LvlPlanes[plane]['x2'] > LvlObjects[obj]['x2'] )
|
||||
):
|
||||
|
||||
return 8
|
||||
|
||||
# Overlap on the South side of the plane
|
||||
if (
|
||||
( LvlPlanes[plane]['y1'] > LvlObjects[obj]['y1'] and LvlPlanes[plane]['y1'] < LvlObjects[obj]['y2'] ) and
|
||||
( LvlPlanes[plane]['x1'] < LvlObjects[obj]['x1'] and LvlPlanes[plane]['x2'] > LvlObjects[obj]['x2'] )
|
||||
):
|
||||
|
||||
return 2
|
||||
|
||||
else:
|
||||
|
||||
return 0
|
||||
|
||||
def getSepLine(plane, side):
|
||||
|
||||
# Construct the line used for BSP generation from 'plane' 's coordinates, on specified side (S, W, N, E)
|
||||
# Returns an array of 3 values
|
||||
|
||||
if side == 'S':
|
||||
|
||||
return [ LvlPlanes[plane]['x1'], LvlPlanes[plane]['y1'], LvlPlanes[plane]['x2'], LvlPlanes[plane]['y1'] ]
|
||||
|
||||
if side == 'N':
|
||||
|
||||
return [ LvlPlanes[plane]['x1'], LvlPlanes[plane]['y2'], LvlPlanes[plane]['x2'], LvlPlanes[plane]['y2'] ]
|
||||
|
||||
if side == 'W':
|
||||
|
||||
return [ LvlPlanes[plane]['x1'], LvlPlanes[plane]['y1'], LvlPlanes[plane]['x1'], LvlPlanes[plane]['y2'] ]
|
||||
|
||||
if side == 'E':
|
||||
|
||||
return [ LvlPlanes[plane]['x2'], LvlPlanes[plane]['y1'], LvlPlanes[plane]['x2'], LvlPlanes[plane]['y2'] ]
|
||||
|
||||
def checkLine(lineX1, lineY1 ,lineX2 ,lineY2, objX1, objY1, objX2, objY2):
|
||||
|
||||
# Returns wether object spanning from objXY1 to objXY2 is Back, Front, Same or Intersecting the line
|
||||
# defined by points (lineXY1, lineXY2)
|
||||
|
||||
val1 = ( objX1 - lineX1 ) * ( lineY2-lineY1 ) - ( objY1 - lineY1 ) * ( lineX2 - lineX1 )
|
||||
|
||||
# rounding to avoid false positives
|
||||
|
||||
val1 = round(val1, 4)
|
||||
|
||||
val2 = ( objX2 - lineX1 ) * ( lineY2-lineY1 ) - ( objY2 - lineY1 ) * ( lineX2 - lineX1 )
|
||||
|
||||
val2 = round(val2, 4)
|
||||
|
||||
if ( (val1 > 0) and (val2 > 0) ):
|
||||
|
||||
return "front"
|
||||
|
||||
elif ( (val1 < 0) and (val2 < 0) ):
|
||||
|
||||
return "back"
|
||||
|
||||
elif ( (val1 == 0) and (val2 == 0) ):
|
||||
|
||||
return "same"
|
||||
|
||||
elif (
|
||||
( (val1>0) and (val2==0) ) or
|
||||
( (val1==0) and (val2>0) )
|
||||
):
|
||||
|
||||
return "front"
|
||||
|
||||
elif (
|
||||
( (val1<0) and (val2==0) ) or
|
||||
( (val1==0) and (val2<0) )
|
||||
):
|
||||
|
||||
return "back"
|
||||
|
||||
elif (
|
||||
( (val1<0) and (val2>0) ) or
|
||||
( (val1>0) and (val2<0) )
|
||||
):
|
||||
|
||||
return "intersect"
|
||||
|
||||
# Leave edit mode to avoid errors
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# triangulate objects of type mesh
|
||||
# If set, triangulate objects of type mesh
|
||||
|
||||
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)
|
||||
# Set Scale
|
||||
|
||||
scale = self.exp_Scale
|
||||
|
||||
# get working directory path
|
||||
# Get working directory path
|
||||
|
||||
filepath = bpy.data.filepath
|
||||
|
||||
folder = os.path.dirname(bpy.path.abspath(filepath))
|
||||
|
||||
dirpath = os.path.join(folder, "TIM")
|
||||
|
||||
### Export pre-calculated backgrounds
|
||||
|
||||
camAngles = []
|
||||
|
||||
defaultCam = 'NULL'
|
||||
|
||||
# if using precalculated BG, render and export them to ./TIM/
|
||||
# If using precalculated BG, render and export them to ./TIM/
|
||||
|
||||
if self.exp_Precalc:
|
||||
|
||||
# create folder if !exist
|
||||
# Create folder if it doesn't exist
|
||||
|
||||
os.makedirs(dirpath, exist_ok = 1)
|
||||
|
||||
# file format config
|
||||
# Set 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
|
||||
# Get active cam
|
||||
|
||||
cam = bpy.context.scene.camera
|
||||
|
||||
# store cam location and rot for restoration later
|
||||
# ~ originLoc = cam.location
|
||||
# ~ originRot = cam.rotation_euler
|
||||
# Find default cam, and cameras in camPath
|
||||
|
||||
for o in bpy.data.objects:
|
||||
|
||||
if o.type == 'CAMERA' and o.data.get('isDefault'):
|
||||
|
||||
defaultCam = o.name
|
||||
|
||||
if o.type == 'CAMERA' and o.name.startswith("camPath"):
|
||||
|
||||
# set cam as active - could be useful if multiple cam are present
|
||||
# Set camera as active
|
||||
|
||||
bpy.context.scene.camera = o
|
||||
|
||||
# set cam Rot/Loc to empty rot/loc
|
||||
# ~ cam.location = o.location
|
||||
# ~ cam.rotation_euler = o.rotation_euler
|
||||
# Render and save image
|
||||
|
||||
# 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())
|
||||
|
||||
# Add camera object to camAngles
|
||||
|
||||
camAngles.append(o)
|
||||
|
||||
|
||||
# set cam back to original pos/rot
|
||||
# ~ cam.location = originLoc
|
||||
# ~ cam.rotation_euler = originRot
|
||||
### Start writing output file
|
||||
|
||||
# Open file
|
||||
|
||||
f = open(os.path.normpath(self.filepath),"w+")
|
||||
|
||||
# write BODY struct def
|
||||
## Add C structures definitions
|
||||
|
||||
# BODY
|
||||
|
||||
f.write("typedef struct {\n" +
|
||||
"\tVECTOR gForce;\n" +
|
||||
"\tVECTOR position;\n" +
|
||||
@ -158,7 +323,8 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
"\tint restitution; \n" +
|
||||
"\t} BODY;\n\n")
|
||||
|
||||
# VERTEX ANIM struct
|
||||
# VANIM
|
||||
|
||||
f.write("typedef struct { \n" +
|
||||
"\tint nframes; // number of frames e.g 20\n" +
|
||||
"\tint nvert; // number of vertices e.g 21\n" +
|
||||
@ -170,13 +336,15 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
# ~ "\tSVECTOR normals[]; // vertex pos as SVECTORs e.g 20 * 21 SVECTORS\n" +
|
||||
"\t} VANIM;\n\n")
|
||||
|
||||
# PRIM struc
|
||||
# PRIM
|
||||
|
||||
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")
|
||||
|
||||
# MESH struct
|
||||
# MESH
|
||||
|
||||
f.write("typedef struct { \n"+
|
||||
"\tTMESH * tmesh;\n" +
|
||||
"\tPRIM * index;\n" +
|
||||
@ -198,7 +366,8 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
"\tVANIM * anim;\n" +
|
||||
"\t} MESH;\n\n")
|
||||
|
||||
# CAM POSITION struct
|
||||
# CAMPOS
|
||||
|
||||
f.write("typedef struct {\n" +
|
||||
"\tVECTOR pos;\n" +
|
||||
"\tSVECTOR rot;\n" +
|
||||
@ -206,30 +375,40 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
"\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")
|
||||
|
||||
# CAMANGLE
|
||||
|
||||
f.write("typedef struct {\n" +
|
||||
"\tCAMPOS * campos;\n" +
|
||||
"\tTIM_IMAGE * BGtim;\n" +
|
||||
"\tunsigned long * tim_data;\n" +
|
||||
"\t} CAMANGLE;\n\n")
|
||||
|
||||
# CAM PATH struct
|
||||
# CAMPATH
|
||||
|
||||
f.write("typedef struct {\n" +
|
||||
"\tshort len, cursor, pos;\n" +
|
||||
"\tVECTOR points[];\n" +
|
||||
"\t} CAMPATH;\n\n")
|
||||
|
||||
# NODE struc
|
||||
f.Write("typedef struct {\n" +
|
||||
# NODE
|
||||
|
||||
f.write("typedef struct {\n" +
|
||||
"\tMESH * curPlane;\n" +
|
||||
"\tMESH * siblings[];" +
|
||||
"\tMESH * objects[];" +
|
||||
"\tMESH * siblings[];\n" +
|
||||
"\tMESH * objects[];\n" +
|
||||
"\t} NODE;\n\n")
|
||||
|
||||
## Camera setup
|
||||
|
||||
# List of points defining the camera path
|
||||
|
||||
camPathPoints = []
|
||||
|
||||
# Define first mesh. Will be used as default if no properties are found in meshes
|
||||
|
||||
first_mesh = CleanName(bpy.data.meshes[0].name)
|
||||
|
||||
# set camera position and rotation in the scene
|
||||
# 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
|
||||
@ -239,13 +418,18 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
"\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
|
||||
# Find camera path points and append them to camPathPoints[]
|
||||
|
||||
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)
|
||||
|
||||
# Write the CAMPATH structure
|
||||
|
||||
if camPathPoints:
|
||||
|
||||
# Populate with points found above
|
||||
|
||||
# ~ camPathPoints = list(reversed(camPathPoints))
|
||||
|
||||
for p in range(len(camPathPoints)):
|
||||
@ -259,15 +443,24 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
if p != len(camPathPoints) - 1:
|
||||
f.write(",\n")
|
||||
f.write("\n\t}\n};\n\n")
|
||||
|
||||
else:
|
||||
|
||||
# If no camera path points are found, use default
|
||||
|
||||
f.write("CAMPATH camPath = {\n" +
|
||||
"\t0,\n" +
|
||||
"\t0,\n" +
|
||||
"\t0\n" +
|
||||
"};\n\n")
|
||||
# Lights : max 3 sunlamp, no space coords
|
||||
|
||||
## Lighting setup
|
||||
|
||||
# Light sources will be similar to Blender's sunlamp
|
||||
# A maximum of 3 light sources will be used
|
||||
|
||||
# LLM : Local Light Matrix
|
||||
|
||||
if len(bpy.data.lamps) is not None:
|
||||
|
||||
# ~ f.write( "static MATRIX lgtmat = {\n" +
|
||||
@ -282,15 +475,15 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
f.write( "static MATRIX lgtmat = {\n")
|
||||
|
||||
for l in range(len(bpy.data.lamps)):
|
||||
## intensity
|
||||
|
||||
# Lightsource energy
|
||||
|
||||
energy = int(bpy.data.lamps[l].energy * 4096)
|
||||
|
||||
# Euler based
|
||||
# Get lightsource's world orientation
|
||||
|
||||
# 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)) + "," +
|
||||
@ -300,6 +493,8 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
if l != len(bpy.data.lamps) - 1:
|
||||
f.write(",\n")
|
||||
|
||||
# If less than 3 light sources exist in blender, fill the matrix with 0s.
|
||||
|
||||
if pad:
|
||||
f.write(",\n")
|
||||
while cnt < pad:
|
||||
@ -311,6 +506,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
f.write("\n\t};\n\n")
|
||||
|
||||
# LCM : Local Color Matrix
|
||||
|
||||
f.write( "static MATRIX cmat = {\n")
|
||||
|
||||
LCM = []
|
||||
@ -323,12 +519,16 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
while len(LCM) < 9:
|
||||
LCM.append('0')
|
||||
|
||||
# Write LC matrix
|
||||
|
||||
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")
|
||||
|
||||
## Meshes
|
||||
|
||||
actorPtr = first_mesh
|
||||
levelPtr = first_mesh
|
||||
propPtr = first_mesh
|
||||
@ -338,23 +538,24 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
|
||||
for m in bpy.data.meshes:
|
||||
|
||||
# Write vertices vectors
|
||||
# Store vertices coordinates by axis to find max/min coordinates
|
||||
|
||||
# AABB : Store vertices coordinates by axis
|
||||
Xvals = []
|
||||
Yvals = []
|
||||
Zvals = []
|
||||
|
||||
# remove '.' from mesh name
|
||||
cleanName = CleanName(m.name)
|
||||
# ~ cleanName = m.name.replace('.','_')
|
||||
# ~ cleanName = unicodedata.normalize('NFKD',cleanName).encode('ASCII', 'ignore').decode()
|
||||
|
||||
# Write vertices vectors
|
||||
|
||||
f.write("SVECTOR "+"model"+cleanName+"_mesh[] = {\n")
|
||||
|
||||
for i in range(len(m.vertices)):
|
||||
|
||||
v = m.vertices[i].co
|
||||
|
||||
# AABB : append vertices coords by axis
|
||||
# Append vertex coords to lists
|
||||
|
||||
Xvals.append(v.x)
|
||||
Yvals.append(v.y)
|
||||
Zvals.append(-v.z)
|
||||
@ -390,72 +591,124 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
|
||||
|
||||
if len(m.uv_textures) != None:
|
||||
|
||||
for t in range(len(m.uv_textures)):
|
||||
|
||||
if m.uv_textures[t].data[0].image != None:
|
||||
|
||||
f.write("SVECTOR "+"model"+cleanName+"_uv[] = {\n")
|
||||
|
||||
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
|
||||
|
||||
# ~ 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")
|
||||
|
||||
f.write("};\n\n")
|
||||
|
||||
# save uv tex if needed - still have to convert them to tim...
|
||||
# Save UV texture to a file in ./TIM
|
||||
# It will have to be converted to a tim file
|
||||
|
||||
if texture_image.filepath == '':
|
||||
|
||||
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
|
||||
|
||||
f.write("CVECTOR "+"model"+cleanName+"_color[] = {\n")
|
||||
|
||||
# If vertex colors exist, use them
|
||||
|
||||
if len(m.vertex_colors) != 0:
|
||||
|
||||
colors = m.vertex_colors[0].data
|
||||
|
||||
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:
|
||||
f.write("\t80,80,80,0") # Let's add a bit more relief with a shade of grey
|
||||
|
||||
f.write("\t80,80,80,0")
|
||||
|
||||
else:
|
||||
|
||||
f.write("\t128,128,128,0")
|
||||
|
||||
if i != (len(m.polygons) * 3) - 1:
|
||||
|
||||
f.write(",")
|
||||
|
||||
f.write("\n")
|
||||
|
||||
f.write("};\n\n")
|
||||
|
||||
# Write polygons index + type
|
||||
|
||||
f.write("PRIM "+"model"+cleanName+"_index[] = {\n")
|
||||
|
||||
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]))
|
||||
|
||||
if len(poly.vertices) > 3:
|
||||
|
||||
f.write("," + str(poly.vertices[3]) + ",8")
|
||||
|
||||
else:
|
||||
|
||||
f.write(",0,4")
|
||||
|
||||
if i != len(m.polygons) - 1:
|
||||
|
||||
f.write(",")
|
||||
|
||||
f.write("\n")
|
||||
|
||||
f.write("};\n\n")
|
||||
|
||||
# get custom properties isRigidBody, isPrism, isAnim
|
||||
# Get object's custom properties
|
||||
|
||||
chkProp = {
|
||||
'isAnim':0,
|
||||
'isRigidBody':0,
|
||||
@ -488,16 +741,24 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
|
||||
if m.get('isLevel'):
|
||||
nodePtr = cleanName
|
||||
# write vertex anim if isAnim != 0 # https://stackoverflow.com/questions/9138637/vertex-animation-exporter-for-blender
|
||||
|
||||
## Vertex animation
|
||||
|
||||
# write vertex anim if isAnim != 0
|
||||
# Source : https://stackoverflow.com/questions/9138637/vertex-animation-exporter-for-blender
|
||||
|
||||
if m.get("isAnim") is not None and m["isAnim"] != 0:
|
||||
|
||||
#write vertex pos
|
||||
# Write vertex pos
|
||||
|
||||
o = bpy.data.objects[m.name]
|
||||
|
||||
# ~ 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
|
||||
@ -509,11 +770,13 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
for i in range(frame_start, frame_end):
|
||||
|
||||
bpy.context.scene.frame_set(i)
|
||||
|
||||
bpy.context.scene.update()
|
||||
|
||||
nm = o.to_mesh(bpy.context.scene, True, 'PREVIEW')
|
||||
|
||||
if i == 0 :
|
||||
|
||||
f.write("VANIM model"+cleanName+"_anim = {\n" +
|
||||
"\t" + str(nFrame) + ",\n" +
|
||||
"\t" + str(len(nm.vertices)) + ",\n" +
|
||||
@ -523,28 +786,41 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
"\t" + str(chkProp['lerp']) + ",\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) * 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)
|
||||
|
||||
f.write("\n\t}\n};\n")
|
||||
|
||||
# Remove meshe's working copies
|
||||
|
||||
for nm in tmp_meshes:
|
||||
|
||||
bpy.data.meshes.remove(nm)
|
||||
|
||||
#Stuff # ~ bpy.data.objects[bpy.data.meshes[0].name].active_shape_key.value : access shape_key
|
||||
# bpy.data.objects[bpy.data.meshes[0].name].active_shape_key.value : access shape_key
|
||||
|
||||
## Mesh world transform setup
|
||||
|
||||
# Write object matrix, rot and pos vectors
|
||||
|
||||
#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" +
|
||||
@ -570,67 +846,102 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
"\t};\n\n")
|
||||
|
||||
# Write TMESH struct
|
||||
|
||||
f.write("TMESH "+"model"+cleanName+" = {\n")
|
||||
|
||||
f.write("\t"+"model"+cleanName+"_mesh, \n")
|
||||
|
||||
f.write("\t"+"model"+cleanName+"_normal,\n")
|
||||
|
||||
if len(m.uv_textures) != None:
|
||||
|
||||
for t in range(len(m.uv_textures)):
|
||||
|
||||
if m.uv_textures[0].data[0].image != None:
|
||||
|
||||
f.write("\t"+"model"+cleanName+"_uv,\n")
|
||||
|
||||
else:
|
||||
|
||||
f.write("\t0,\n")
|
||||
else:
|
||||
|
||||
f.write("\t0,\n")
|
||||
|
||||
f.write("\t"+"model"+cleanName+"_color, \n")
|
||||
|
||||
# According to libgte.h, TMESH.len should be # of vertices. Meh...
|
||||
|
||||
f.write("\t"+str(len(m.polygons))+"\n")
|
||||
|
||||
f.write("};\n\n")
|
||||
|
||||
# write texture binary name and declare TIM_IMAGE
|
||||
# by default, load the file from the TIM folder
|
||||
# ~ if len(m.uv_textures) != 0:
|
||||
# Write texture binary name and declare TIM_IMAGE
|
||||
# By default, loads the file from the ./TIM folder
|
||||
|
||||
if len(m.uv_textures) != None:
|
||||
|
||||
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('-','_')
|
||||
|
||||
prefix = CleanName(prefix)
|
||||
# add Tex name to array if !exist
|
||||
|
||||
# Add Tex name to list if it's not in there already
|
||||
|
||||
if prefix in timList:
|
||||
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
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")
|
||||
|
||||
timList.append(prefix)
|
||||
|
||||
f.write("MESH mesh"+cleanName+" = {\n")
|
||||
|
||||
f.write("\t&model"+ cleanName +",\n")
|
||||
|
||||
f.write("\tmodel" + cleanName + "_index,\n")
|
||||
|
||||
|
||||
|
||||
if len(m.uv_textures) != None:
|
||||
|
||||
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('-','_')
|
||||
|
||||
prefix = CleanName(prefix)
|
||||
|
||||
f.write("\t&tim_"+ prefix + ",\n")
|
||||
|
||||
f.write("\t_binary_TIM_" + prefix + "_tim_start,\n")
|
||||
|
||||
else:
|
||||
|
||||
f.write("\t0,\n" +
|
||||
|
||||
"\t0,\n")
|
||||
else:
|
||||
|
||||
f.write("\t0,\n" +
|
||||
|
||||
"\t0,\n")
|
||||
|
||||
f.write("\t&model"+cleanName+"_matrix,\n" +
|
||||
@ -646,83 +957,115 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
|
||||
"\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")
|
||||
else:
|
||||
|
||||
f.write("\n")
|
||||
|
||||
f.write("};\n\n")
|
||||
|
||||
f.write("MESH * meshes[" + str(len(bpy.data.meshes)) + "] = {\n")
|
||||
|
||||
for k in range(len(bpy.data.meshes)):
|
||||
|
||||
cleanName = CleanName(bpy.data.meshes[k].name)
|
||||
|
||||
f.write("\t&mesh" + cleanName)
|
||||
|
||||
if k != len(bpy.data.meshes) - 1:
|
||||
|
||||
f.write(",\n")
|
||||
|
||||
f.write("\n}; \n")
|
||||
|
||||
# nothing in camAngles, use default camera
|
||||
# If camAngles is empty, use default camera, and do not include pre-calculated backgrounds
|
||||
|
||||
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
|
||||
# If camAngles is populated, use backgrounds and camera angles
|
||||
|
||||
for o in camAngles:
|
||||
|
||||
prefix = CleanName(o.name)
|
||||
|
||||
# include Tim data
|
||||
# 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
|
||||
# Write corresponding TIM_IMAGE struct
|
||||
|
||||
f.write("TIM_IMAGE tim_bg_" + prefix + ";\n\n")
|
||||
|
||||
# write corresponding CamAngle struct
|
||||
# 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
|
||||
# Write camera angles in an 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\n")
|
||||
|
||||
f.write("CAMANGLE * camPtr = &camAngle_" + CleanName(defaultCam) + ";\n\n")
|
||||
|
||||
f.write("NODE * curNode = &node_" + nodePtr + ";\n\n")
|
||||
f.write("NODE * curNode = &node" + nodePtr + ";\n\n")
|
||||
|
||||
# Set default camera back in Blender
|
||||
|
||||
# set default cam back
|
||||
if defaultCam != 'NULL':
|
||||
|
||||
bpy.context.scene.camera = bpy.data.objects[defaultCam]
|
||||
|
||||
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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user