add portals

This commit is contained in:
ABelliqueux 2021-03-31 20:28:02 +02:00
parent 7e7e524735
commit 39b660dbd8

View File

@ -18,7 +18,7 @@ import bmesh
import unicodedata
from math import radians, degrees, floor, cos, sin
from math import radians, degrees, floor, cos, sin, sqrt
from mathutils import Vector
@ -78,26 +78,6 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
def execute(self, context):
def isInFrame(scene, cam, target):
position = world_to_camera_view(scene, cam, target.location)
if (
(position.x < 0 or position.x > 1 ) or
(position.y < 0 or position.y > 1 ) or
(position.z < 0 )
) :
return False
else:
return True
def triangulate_object(obj):
# Triangulate an object's mesh
@ -129,6 +109,26 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
return name
def isInFrame(scene, cam, target):
position = world_to_camera_view(scene, cam, target.location)
if (
(position.x < 0 or position.x > 1 ) or
(position.y < 0 or position.y > 1 ) or
(position.z < 0 )
) :
return False
else:
return True
def isInPlane(plane, obj):
# Checks if 'obj' has its coordinates contained between the plane's coordinate.
@ -248,6 +248,48 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
return "intersect"
def objVertWtoS(scene, cam, target, toScale = 1):
screenPos = []
# Get objects world matrix
mw = target.matrix_world
# Get object's mesh
mesh = bpy.data.meshes[ target.name ]
# For each vertex in mesh, get screen coordinates
for vertex in mesh.vertices:
# Get meshes world coordinates
screenPos.append( world_to_camera_view( scene, cam, ( mw * vertex.co ) ) )
if toScale:
# Get current scene rsolution
resX = scene.render.resolution_x
resY = scene.render.resolution_y
# Scale values
for vector in screenPos:
# ~ vector.x = int( resX * vector.x ) < 0 ? 0 : int( resX * vector.x ) > 320 ? 320 : int( resX * vector.x )
vector.x = max ( 0, min ( resX, int( resX * vector.x ) ) )
vector.y = max ( 0, min ( resY, int( resY * vector.y ) ) )
vector.z = int( vector.z )
return screenPos
# Leave edit mode to avoid errors
bpy.ops.object.mode_set(mode='OBJECT')
@ -349,7 +391,9 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"struct CAMANGLE;\n" +
"struct SIBLINGS;\n" +
"struct CHILDREN;\n" +
"struct NODE;\n\n")
"struct NODE;\n" +
"struct QUAD;\n" +
"\n")
# BODY
@ -382,7 +426,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
f.write("typedef struct PRIM {\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" +
"\tint code; // Same as POL3/POL4 codes : Code (F3 = 1, FT3 = 2, G3 = 3,\n// GT3 = 4) Code (F4 = 5, FT4 = 6, G4 = 7, GT4 = 8)\n" +
"\t} PRIM;\n\n")
# MESH
@ -408,16 +452,27 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\tBODY * body;\n" +
"\tVANIM * anim;\n" +
"\tstruct NODE * node;\n" +
"\tSVECTOR pos2D;\n" +
"\tVECTOR pos2D;\n" +
"\t} MESH;\n\n")
#QUAD
f.write("typedef struct QUAD {\n" +
"\tVECTOR v0, v1;\n" +
"\tVECTOR v2, v3;\n" +
"\t} QUAD;\n\n")
# CAMPOS
f.write("typedef struct CAMPOS {\n" +
"\tVECTOR pos;\n" +
"\tSVECTOR rot;\n" +
"\t} CAMPOS;\n\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")
"\n// Blender cam ~= PSX cam with these settings : \n" +
"// NTSC - 320x240, PAL 320x256, pixel ratio 1:1,\n" +
"// cam focal length : perspective 90° ( 16 mm ))\n" +
"// With a FOV of 1/2, camera focal length is ~= 16 mm / 90°\n" +
"// Lower values mean wider angle\n\n")
# CAMANGLE
@ -425,6 +480,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\tCAMPOS * campos;\n" +
"\tTIM_IMAGE * BGtim;\n" +
"\tunsigned long * tim_data;\n" +
"\tQUAD bw, fw;\n" +
"\tint index;\n" +
"\tMESH * objects[];\n" +
"\t} CAMANGLE;\n\n")
@ -656,6 +712,8 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
for m in bpy.data.meshes:
if not m.get('isPortal') :
# Store vertices coordinates by axis to find max/min coordinates
Xvals = []
@ -1131,15 +1189,36 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\n};\n\n"
)
f.write("MESH * meshes[" + str(len(bpy.data.meshes)) + "] = {\n")
# Remove portals from mesh list as we don't want them to be exported
for k in range(len(bpy.data.meshes)):
meshList = list(bpy.data.meshes)
cleanName = CleanName(bpy.data.meshes[k].name)
portalList = []
for mesh in meshList:
if mesh.get('isPortal'):
meshList = [i for i in meshList if i != mesh]
# Nasty way of removing all occurrences of the mesh
# ~ try:
# ~ while True:
# ~ meshList.remove(mesh)
# ~ except ValueError:
# ~ pass
portalList.append( bpy.data.objects[mesh.name] )
f.write("MESH * meshes[" + str(len(meshList)) + "] = {\n")
for k in range(len(meshList)):
cleanName = CleanName(meshList[k].name)
f.write("\t&mesh" + cleanName)
if k != len(bpy.data.meshes) - 1:
if k != len(meshList) - 1:
f.write(",\n")
@ -1153,9 +1232,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\t&camPos_" + CleanName(defaultCam) + ",\n" +
"\t0,\n" +
"\t0\n" +
"\t0,\n 0,\n {0},\n {0},\n 0,\n 0\n" +
"};\n\n")
@ -1163,12 +1240,82 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
for camera in camAngles:
# Cast a ray from camera to each Rigid/Static body to determine visibility
# Get current scene
scene = bpy.context.scene
# List of portals
visiblePortal = []
for portal in portalList:
if isInFrame(scene, camera, portal):
# Get normalized direction vector between camera and portal
dirToTarget = portal.location - camera.location
dirToTarget.normalize()
# Cast a ray from camera to body to determine visibility
result, location, normal, index, hitObject, matrix = scene.ray_cast( camera.location, dirToTarget )
# If hitObject is portal, nothing is obstructing it's visibility
if hitObject is not None:
if hitObject in portalList:
if hitObject == portal:
# ~ print(str(camera) + str(hitObject))
visiblePortal.append(hitObject)
# ~ print(str(camera) + ":" + str(visiblePortal))
# If more than one portal is visible, only keep the two closest ones
if len( visiblePortal ) > 2:
# Store the tested portals distance to camera
testDict = {}
for tested in visiblePortal:
# Get distance between cam and tested portal
distToTested = sqrt( ( tested.location - camera.location ) * ( tested.location - camera.location ) )
# Store distance
testDict[distToTested] = tested
# If dictionary has more than 2 portals, remove the farthest ones
while len( testDict ) > 2:
del testDict[max(testDict)]
# Reset visible portal
visiblePortal.clear()
# Get the portals stored in the dict and store them in the list
for Dportal in testDict:
visiblePortal.append(testDict[Dportal])
# Revert to find original order back
visiblePortal.reverse()
# ~ print( str( camera ) + " : " + str( visiblePortal ) )
# List of target found visible
visibleTarget = []
@ -1177,9 +1324,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# Chech object is in view frame
inViewFrame = isInFrame(scene, camera, target)
if inViewFrame:
if isInFrame(scene, camera, target):
# Get normalized direction vector between camera and object
@ -1188,7 +1333,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
dirToTarget.normalize()
# Cast ray from camera to object
# Unpack results in several variables.
# Unpack results into several variables.
# We're only interested in 'hitObject' though
result, location, normal, index, hitObject, matrix = scene.ray_cast( camera.location, dirToTarget )
@ -1199,10 +1344,13 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
if hitObject in rayTargets:
print(camera.name + ":" + hitObject.name + " - " + target.name )
visibleTarget.append(target)
if bpy.data.objects[ actorPtr ] not in visibleTarget:
visibleTarget.append( bpy.data.objects[ actorPtr ] )
prefix = CleanName(camera.name)
# Include Tim data
@ -1227,7 +1375,33 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\t_binary_TIM_bg_" + prefix + "_tim_start,\n" +
"\t" + str( len( visibleTarget ) ) + ",\n" +
"\t// Write quad NW, NE, SE, SW\n")
# If visiblePoratl length is under 2, this means there's only one portal, hence a fw portal
if len( visiblePortal ) < 2 :
f.write("\t{\n\t\t{ 0, 0, 0, 0 },\n\t\t{ 0, 0, 0, 0 },\n\t\t{ 0, 0, 0, 0 },\n\t\t{ 0, 0, 0, 0 }\n\t},\n")
for portal in visiblePortal:
s = objVertWtoS( scene, camera, portal )
# Write quad NW, NE, SE, SW
f.write("\t{\n\t\t" +
"{ " + str( int (s[3].x ) ) + ", " + str( int (s[3].y ) ) + ", " + str( int (s[3].z ) ) + ", 0 },\n\t\t" +
"{ " + str( int (s[2].x ) ) + ", " + str( int (s[2].y ) ) + ", " + str( int (s[2].z ) ) + ", 0 },\n\t\t" +
"{ " + str( int (s[0].x ) ) + ", " + str( int (s[0].y ) ) + ", " + str( int (s[0].z ) ) + ", 0 },\n\t\t" +
"{ " + str( int (s[1].x ) ) + ", " + str( int (s[1].y ) ) + ", " + str( int (s[1].z ) ) + ", 0 }\n" +
"\t},\n" )
f.write("\t" + str( len( visibleTarget ) ) + ",\n" +
"\t{\n")
@ -1257,9 +1431,6 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
## Spatial Partitioning
# ToDo :
# Auto-detect which plane the actor is on and set that as curNode
# Planes in the level - dict of strings
LvlPlanes = {}
@ -1292,7 +1463,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# Only loop through meshes
if o.type == 'MESH':
if o.type == 'MESH' and not o.data.get('isPortal'):
# Get Level planes coordinates
@ -1326,7 +1497,6 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# For each object not a plane, get its coordinates
# ~ if not o.data.get('isLevel'):
if not o.data.get('isLevel'):
# World matrix is used to convert local to global coordinates
@ -1416,7 +1586,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
levelPtr = p
nodePtr = p
# Add actor in every plane
# Add moveable objects in every plane
for moveable in Moveables:
@ -1617,7 +1787,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
if defaultCam != 'NULL':
bpy.context.scene.camera = bpy.data.objects[defaultCam]
bpy.context.scene.camera = bpy.data.objects[ defaultCam ]
f.close()