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 import unicodedata
from math import radians, degrees, floor, cos, sin from math import radians, degrees, floor, cos, sin, sqrt
from mathutils import Vector from mathutils import Vector
@ -78,26 +78,6 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
def execute(self, context): 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): def triangulate_object(obj):
# Triangulate an object's mesh # Triangulate an object's mesh
@ -129,6 +109,26 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
return name 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): def isInPlane(plane, obj):
# Checks if 'obj' has its coordinates contained between the plane's coordinate. # Checks if 'obj' has its coordinates contained between the plane's coordinate.
@ -248,6 +248,48 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
return "intersect" 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 # Leave edit mode to avoid errors
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
@ -349,7 +391,9 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"struct CAMANGLE;\n" + "struct CAMANGLE;\n" +
"struct SIBLINGS;\n" + "struct SIBLINGS;\n" +
"struct CHILDREN;\n" + "struct CHILDREN;\n" +
"struct NODE;\n\n") "struct NODE;\n" +
"struct QUAD;\n" +
"\n")
# BODY # BODY
@ -382,7 +426,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
f.write("typedef struct PRIM {\n" + f.write("typedef struct PRIM {\n" +
"\tVECTOR order;\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") "\t} PRIM;\n\n")
# MESH # MESH
@ -408,16 +452,27 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\tBODY * body;\n" + "\tBODY * body;\n" +
"\tVANIM * anim;\n" + "\tVANIM * anim;\n" +
"\tstruct NODE * node;\n" + "\tstruct NODE * node;\n" +
"\tSVECTOR pos2D;\n" + "\tVECTOR pos2D;\n" +
"\t} MESH;\n\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 # CAMPOS
f.write("typedef struct CAMPOS {\n" + f.write("typedef struct CAMPOS {\n" +
"\tVECTOR pos;\n" + "\tVECTOR pos;\n" +
"\tSVECTOR rot;\n" + "\tSVECTOR rot;\n" +
"\t} CAMPOS;\n\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 # CAMANGLE
@ -425,6 +480,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\tCAMPOS * campos;\n" + "\tCAMPOS * campos;\n" +
"\tTIM_IMAGE * BGtim;\n" + "\tTIM_IMAGE * BGtim;\n" +
"\tunsigned long * tim_data;\n" + "\tunsigned long * tim_data;\n" +
"\tQUAD bw, fw;\n" +
"\tint index;\n" + "\tint index;\n" +
"\tMESH * objects[];\n" + "\tMESH * objects[];\n" +
"\t} CAMANGLE;\n\n") "\t} CAMANGLE;\n\n")
@ -656,6 +712,8 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
for m in bpy.data.meshes: for m in bpy.data.meshes:
if not m.get('isPortal') :
# Store vertices coordinates by axis to find max/min coordinates # Store vertices coordinates by axis to find max/min coordinates
Xvals = [] Xvals = []
@ -1131,15 +1189,36 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\n};\n\n" "\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) f.write("\t&mesh" + cleanName)
if k != len(bpy.data.meshes) - 1: if k != len(meshList) - 1:
f.write(",\n") f.write(",\n")
@ -1153,9 +1232,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\t&camPos_" + CleanName(defaultCam) + ",\n" + "\t&camPos_" + CleanName(defaultCam) + ",\n" +
"\t0,\n" + "\t0,\n 0,\n {0},\n {0},\n 0,\n 0\n" +
"\t0\n" +
"};\n\n") "};\n\n")
@ -1163,12 +1240,82 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
for camera in camAngles: for camera in camAngles:
# Cast a ray from camera to each Rigid/Static body to determine visibility
# Get current scene # Get current scene
scene = bpy.context.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 # List of target found visible
visibleTarget = [] visibleTarget = []
@ -1177,9 +1324,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# Chech object is in view frame # Chech object is in view frame
inViewFrame = isInFrame(scene, camera, target) if isInFrame(scene, camera, target):
if inViewFrame:
# Get normalized direction vector between camera and object # Get normalized direction vector between camera and object
@ -1188,7 +1333,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
dirToTarget.normalize() dirToTarget.normalize()
# Cast ray from camera to object # Cast ray from camera to object
# Unpack results in several variables. # Unpack results into several variables.
# We're only interested in 'hitObject' though # We're only interested in 'hitObject' though
result, location, normal, index, hitObject, matrix = scene.ray_cast( camera.location, dirToTarget ) 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: if hitObject in rayTargets:
print(camera.name + ":" + hitObject.name + " - " + target.name )
visibleTarget.append(target) visibleTarget.append(target)
if bpy.data.objects[ actorPtr ] not in visibleTarget:
visibleTarget.append( bpy.data.objects[ actorPtr ] )
prefix = CleanName(camera.name) prefix = CleanName(camera.name)
# Include Tim data # Include Tim data
@ -1227,7 +1375,33 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
"\t_binary_TIM_bg_" + prefix + "_tim_start,\n" + "\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") "\t{\n")
@ -1257,9 +1431,6 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
## Spatial Partitioning ## Spatial Partitioning
# ToDo :
# Auto-detect which plane the actor is on and set that as curNode
# Planes in the level - dict of strings # Planes in the level - dict of strings
LvlPlanes = {} LvlPlanes = {}
@ -1292,7 +1463,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# Only loop through meshes # Only loop through meshes
if o.type == 'MESH': if o.type == 'MESH' and not o.data.get('isPortal'):
# Get Level planes coordinates # Get Level planes coordinates
@ -1326,7 +1497,6 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
# For each object not a plane, get its coordinates # For each object not a plane, get its coordinates
# ~ if not o.data.get('isLevel'):
if not o.data.get('isLevel'): if not o.data.get('isLevel'):
# World matrix is used to convert local to global coordinates # World matrix is used to convert local to global coordinates
@ -1416,7 +1586,7 @@ class ExportMyFormat(bpy.types.Operator, ExportHelper):
levelPtr = p levelPtr = p
nodePtr = p nodePtr = p
# Add actor in every plane # Add moveable objects in every plane
for moveable in Moveables: for moveable in Moveables: