845 lines
31 KiB
Python
845 lines
31 KiB
Python
import bge
|
|
import math
|
|
import numpy
|
|
import mathutils
|
|
|
|
r = math.radians
|
|
|
|
from Scripts import Reuse
|
|
from Scripts.Common import *
|
|
|
|
# Chunks to store data about parts of the world
|
|
chunks = {}
|
|
bge.logic.globalDict["Opt.chunks"] = chunks
|
|
|
|
camspeed = 0.0
|
|
camposition = mathutils.Vector((0,0,0))
|
|
camspeedframe = 0.0
|
|
|
|
impact = {"test":None,
|
|
"lastTestTime":0,
|
|
"data":{},
|
|
"frame":0,
|
|
"timer":1,
|
|
"estimate":None}
|
|
|
|
def CalculateImpact(previous, current):
|
|
|
|
result = {}
|
|
for i in previous:
|
|
p = previous[i][0]
|
|
c = current [i][0]
|
|
result[i] = c - p
|
|
|
|
return result
|
|
|
|
def GoodFPS(func="", factor=1.0, boolean=True, traceback=False ):
|
|
|
|
# This will be used in an if statement before executing
|
|
# some, potentially intense work. And if the performance
|
|
# is dropped below a certain amount. It will stop executing
|
|
# that command.
|
|
|
|
settings = bge.logic.globalDict.get("settings", {})
|
|
frame = round(bge.logic.getRealTime(), 2)
|
|
currentMetrics = bge.logic.getProfileInfo()
|
|
targetFPS = settings.get("fps", 30)
|
|
|
|
if not boolean:
|
|
return bge.logic.getAverageFrameRate() / targetFPS
|
|
|
|
# We need to let the game initialize for a few seconds.
|
|
# So we limit all executions in that time.
|
|
if frame < 5: return 0
|
|
|
|
# Controlling the amount of how aggressively this function is executed.
|
|
optset = settings.get("opt", 1.0) ** 0.1
|
|
choice = numpy.random.choice([False, True], p=[optset, 1-optset])
|
|
if choice:
|
|
# Restraining over-bombardment.
|
|
if func in impact["data"]:
|
|
if impact["data"][func].get("lastframe",0) + impact["timer"] > frame:
|
|
return 0.0
|
|
return 1.0
|
|
|
|
# Calculating impact timer ( which will time how often one function can be
|
|
# exectuted ).
|
|
impact["timer"] = 10 - (10 * ( bge.logic.getAverageFrameRate() / targetFPS ))
|
|
|
|
# Recalculating if FPS goes too low.
|
|
# TODO: figure out a way to actually do that reasonably.
|
|
#if bge.logic.getAverageFrameRate() < targetFPS * 0.5:
|
|
# impact["data"] = {}
|
|
|
|
# We want to record FPS impacts of various executed things.
|
|
|
|
# 1 | On first 2 frames it should tell everyone to do nothing.
|
|
# 2 | On second frame it should record profile. And execute.
|
|
# 3 | Then on third comparing the profiles should give us impact.
|
|
|
|
if not impact["test"] and func not in impact["data"] and \
|
|
impact["lastTestTime"] + impact["timer"] < frame:
|
|
|
|
impact["test"] = {
|
|
"func" :func,
|
|
"stage" :0,
|
|
"frame" :frame,
|
|
"profile":None
|
|
}
|
|
|
|
# This is needed to not bombard the player with slow tests
|
|
# immediately.
|
|
impact["lastTestTime"] = frame
|
|
|
|
if impact["test"]:
|
|
|
|
test = impact["test"]
|
|
|
|
if test["stage"] == 0 and test["frame"] < frame:
|
|
test["frame"] = frame
|
|
test["stage"] = 1
|
|
|
|
if test["stage"] == 1:
|
|
test["frame"] = frame
|
|
test["profile"] = currentMetrics.copy()
|
|
test["stage"] = 2
|
|
return 1.0
|
|
|
|
if test["stage"] == 2 and test["frame"] < frame:
|
|
impactIs = CalculateImpact(test["profile"], currentMetrics)
|
|
impact["test"] = None
|
|
impact["data"][func] = impactIs
|
|
|
|
return 0.0
|
|
|
|
elif func in impact["data"]:
|
|
|
|
# If we have data about how agressive the changes are,
|
|
# we can use that data to estimate next frames FPS with it.
|
|
|
|
# If the function was recently activate, skip it.
|
|
if impact["data"][func].get("lastframe",0) + impact["timer"] > frame:
|
|
return 0.0
|
|
|
|
# Clearing out the estimates on new frames
|
|
if impact["frame"] != frame:
|
|
impact["frame"] = frame
|
|
impact["estimate"] = None
|
|
|
|
# Making a fresh estimate
|
|
if not impact["estimate"]:
|
|
impact["estimate"] = {}
|
|
for i in currentMetrics:
|
|
impact["estimate"][i] = currentMetrics[i][0]
|
|
|
|
# Creating a temporary estimate only for this function
|
|
tempEstimate = impact["estimate"].copy()
|
|
|
|
# Calculating it's function impact
|
|
totalMS = 0
|
|
for i in tempEstimate:
|
|
tempEstimate[i] += impact["data"][func][i]
|
|
totalMS += tempEstimate[i]
|
|
|
|
# Deciding whether we have speed to spare on this calculation
|
|
targetMS = 1 / ( ( targetFPS ) / 1000 )
|
|
if totalMS > targetMS:
|
|
return 0.0
|
|
else:
|
|
impact["estimate"] = tempEstimate
|
|
impact["data"][func]["lastframe"] = frame
|
|
return 1.0
|
|
|
|
|
|
|
|
def CameraSpeed():
|
|
|
|
# This function gives the average speed of the
|
|
# camera at any given frame.
|
|
frame = round(bge.logic.getRealTime(), 2)
|
|
|
|
global camspeed
|
|
global camposition
|
|
global camspeedframe
|
|
|
|
if frame != camspeedframe:
|
|
camspeedframe = frame
|
|
|
|
# Getting velocity of the camera
|
|
p = bge.logic.getCurrentScene().active_camera.worldPosition
|
|
v = p - camposition
|
|
camposition = p.copy()
|
|
|
|
# Averaging the velocity
|
|
av = 0
|
|
for i in v:
|
|
if i > 0: av += i
|
|
else : av -= i
|
|
av /= 3
|
|
|
|
# Storing the value
|
|
camspeed = av
|
|
|
|
# Returning the value
|
|
return camspeed
|
|
|
|
def ToFPS():
|
|
|
|
# Return a fraction that is useful to adjust FPS
|
|
# sensitive values like gravity.
|
|
|
|
return 60/bge.logic.getAverageFrameRate()
|
|
|
|
def Address(location, precision):
|
|
|
|
# This function will return an adress of any given location
|
|
# Which could be used to compare between objects to see if they
|
|
# are close enough together.
|
|
|
|
ret = ""
|
|
for axis in location[:2]:
|
|
ret = ret + str(round(axis/precision))+":"
|
|
return ret
|
|
|
|
def Surround(location, precision, camera=None):
|
|
|
|
# This function will give a list of addresses around a certain point.
|
|
|
|
ret = []
|
|
ORL = []
|
|
addedtypes = []
|
|
for axis in location[:2]:
|
|
ORL.append(round(axis/precision))
|
|
|
|
ret.append(Address(ORL, 1))
|
|
|
|
|
|
|
|
if not camera:
|
|
# X
|
|
if (location[0]/precision) - round(location[0]/precision) < 0.5:
|
|
ret.append(Address([ORL[0]-1, ORL[1]], 1))
|
|
addedtypes.append("-x")
|
|
elif (location[0]/precision) - round(location[0]/precision) > 0.5:
|
|
ret.append(Address([ORL[0]+1, ORL[1]], 1))
|
|
addedtypes.append("+x")
|
|
|
|
# Y
|
|
if (location[1]/precision) - round(location[1]/precision) < 0.5:
|
|
ret.append(Address([ORL[0], ORL[1]-1], 1))
|
|
addedtypes.append("-y")
|
|
elif (location[1]/precision) - round(location[1]/precision) > 0.5:
|
|
ret.append(Address([ORL[0], ORL[1]+1], 1))
|
|
addedtypes.append("+y")
|
|
|
|
# Diagonals
|
|
if "+x" in addedtypes and "+y" in addedtypes:
|
|
ret.append(Address([ORL[0]+1, ORL[1]+1], 1))
|
|
elif "-x" in addedtypes and "-y" in addedtypes:
|
|
ret.append(Address([ORL[0]-1, ORL[1]-1], 1))
|
|
elif "-x" in addedtypes and "+y" in addedtypes:
|
|
ret.append(Address([ORL[0]-1, ORL[1]+1], 1))
|
|
elif "+x" in addedtypes and "-y" in addedtypes:
|
|
ret.append(Address([ORL[0]+1, ORL[1]-1], 1))
|
|
|
|
else:
|
|
if r(70) >= camera[2] >= r(-70):
|
|
ret.append(Address([ORL[0], ORL[1]+1], 1))
|
|
|
|
if r(25) >= camera[2] >= r(-115):
|
|
ret.append(Address([ORL[0]+1, ORL[1]+1], 1))
|
|
|
|
if r(-20) >= camera[2] >= r(-160):
|
|
ret.append(Address([ORL[0]+1, ORL[1]], 1))
|
|
|
|
if r(-65) >= camera[2] >= r(-180) or r(180) > camera[2] >= r(155):
|
|
ret.append(Address([ORL[0]+1, ORL[1]-1], 1))
|
|
|
|
if r(-110) >= camera[2] >= r(-180) or r(180) > camera[2] >= r(110):
|
|
ret.append(Address([ORL[0], ORL[1]-1], 1))
|
|
|
|
if r(-155) >= camera[2] >= r(180) or r(180) > camera[2] >= r(65):
|
|
ret.append(Address([ORL[0]-1, ORL[1]-1], 1))
|
|
|
|
if r(160) >= camera[2] >= r(20):
|
|
ret.append(Address([ORL[0]-1, ORL[1]], 1))
|
|
|
|
if r(115) >= camera[2] >= r(-25):
|
|
ret.append(Address([ORL[0]-1, ORL[1]+1], 1))
|
|
|
|
|
|
|
|
return ret
|
|
|
|
previousSurround = {}
|
|
|
|
def SurroundChanged(key, surround):
|
|
|
|
# This returns whether surround was changed between frames
|
|
|
|
if key not in previousSurround:
|
|
previousSurround[key] = surround.copy()
|
|
|
|
changed = surround != previousSurround[key]
|
|
|
|
previousSurround[key] = surround.copy()
|
|
|
|
return changed
|
|
|
|
def RegisterObjects(object, precision, Type="LightSpawn"):
|
|
|
|
# Precalculate dynamically loading objects
|
|
# into their chunks, so they could be
|
|
# dynamically loaded.
|
|
|
|
settings = bge.logic.globalDict.get("settings", {})
|
|
|
|
if Type == "LightSpawn":
|
|
|
|
name = object["LightSpawn"]
|
|
|
|
if name == "LightStand":
|
|
if settings.get("poles"):
|
|
spawnObject = RegisterObject(object, precision)
|
|
spawnObject["name"] = object["LightSpawn"]
|
|
spawnObject["scaling"] = [1,1,1]
|
|
spawnObject["suspendDynamics"] = object.get("suspendDynamics", True)
|
|
else:
|
|
object.endObject()
|
|
|
|
elif "Gate" in name:
|
|
if settings.get("fences"):
|
|
spawnObject = RegisterObject(object, precision)
|
|
spawnObject["name"] = object["LightSpawn"]
|
|
spawnObject["scaling"] = [1,1,1]
|
|
spawnObject["suspendDynamics"] = object.get("suspendDynamics", True)
|
|
else:
|
|
object.endObject()
|
|
|
|
else:
|
|
spawnObject = RegisterObject(object, precision)
|
|
spawnObject["name"] = object["LightSpawn"]
|
|
spawnObject["scaling"] = [1,1,1]
|
|
spawnObject["suspendDynamics"] = object.get("suspendDynamics", True)
|
|
|
|
elif Type == "AreaLamp":
|
|
|
|
obj = object.blenderObject.data
|
|
spawnObject = RegisterObject(object, precision)
|
|
spawnObject["name"] = "Area_Lamp"
|
|
spawnObject["light_data"] = obj
|
|
|
|
elif Type == "Tree":
|
|
|
|
if settings.get("trees"):
|
|
treeObject = RegisterObject(object, precision)
|
|
treeObject["lods"] = {
|
|
"NormalTreeTrunk": 100,
|
|
#"TreeLowres": 200, # The model is ugly
|
|
"TreeBillboard": 5000
|
|
}
|
|
treeObject["name"] = "TreeBillboard"
|
|
treeObject["scaling"] /= 1.603
|
|
else:
|
|
object.endObject()
|
|
|
|
elif Type == "Palm":
|
|
|
|
if settings.get("trees"):
|
|
treeObject = RegisterObject(object, precision)
|
|
treeObject["lods"] = {
|
|
"NormalPalmTrunk": 100,
|
|
"PalmLow": 200,
|
|
"PalmCutout": 5000
|
|
}
|
|
treeObject["name"] = "PalmCutout"
|
|
treeObject["scaling"] /= 1.445
|
|
else:
|
|
object.endObject()
|
|
|
|
def RegisterObject(object, precision, delete=True):
|
|
|
|
# This will move an object from scene.objects into
|
|
# chunks. So that they could be stored without
|
|
# effecting the BGE Depsgraph performance.
|
|
|
|
# Creating chunk
|
|
addr = Address(object.position, precision)
|
|
if addr not in chunks:
|
|
chunks[addr] = {"loaded":False,
|
|
"objects":[]}
|
|
|
|
# Adding properties
|
|
virtualObject = {}
|
|
virtualObject["name"] = object.name
|
|
virtualObject["position"] = object.position.copy()
|
|
virtualObject["orientation"] = object.orientation.to_euler()
|
|
virtualObject["scaling"] = object.scaling.copy()
|
|
|
|
for i in object.blenderObject.game.properties:
|
|
virtualObject[i.name] = i.value
|
|
|
|
|
|
# Adding the object to the chunk
|
|
chunks[addr]["objects"].append(virtualObject)
|
|
|
|
# Deleting real object
|
|
if delete:
|
|
object.endObject()
|
|
|
|
# Returning
|
|
return virtualObject
|
|
|
|
def SortByDistance(l, cam, withdistance=False):
|
|
|
|
# This sorts a chunk by the distance
|
|
distanced = []
|
|
for n, i in enumerate(l):
|
|
d = cam.getDistanceTo(i["position"])
|
|
distanced.append([d, n, i])
|
|
|
|
distanced = sorted(distanced)
|
|
|
|
if withdistance:
|
|
return distanced
|
|
|
|
ret = []
|
|
for i in distanced:
|
|
ret.append(i[2])
|
|
return ret
|
|
|
|
def UpdateScene(camobject, precision, camera=None):
|
|
|
|
# This function will update the scene. According to the camera
|
|
# view.
|
|
|
|
location = camobject.position
|
|
|
|
# Getting addresses
|
|
addrs = Surround(location, precision, camera)
|
|
|
|
# Checking if any of addresses are not up to date.
|
|
for addr in chunks:
|
|
|
|
# Delete object if address not in view
|
|
|
|
# It is done in a separate loop and first,
|
|
# because we might need elements from it
|
|
# to spawn later. Otherwise we might cache
|
|
# way too many objects into memory.
|
|
|
|
if addr not in addrs and chunks[addr]["loaded"]:
|
|
for object in chunks[addr]["objects"]:
|
|
if object.get("object"):
|
|
Reuse.Delete(object["object"])
|
|
object["object"] = None
|
|
chunks[addr]["loaded"] = False
|
|
|
|
# Breaking to skip to next frame and
|
|
# re-evaluate whether the FPS is good
|
|
return
|
|
|
|
lookthroughchunks = chunks
|
|
if addrs[0] in chunks:
|
|
lookthroughchunks = [chunks[addrs[0]]]+list(chunks.keys())
|
|
|
|
|
|
# Fixing random items at the chunks.
|
|
for i in range(5):
|
|
addr = random.choice(list(chunks.keys()))
|
|
item = random.choice(chunks[addr]["objects"])
|
|
if item.get("broken"):
|
|
item["broken"] = False
|
|
print(consoleForm(item["name"]), "Fixed")
|
|
|
|
|
|
for addr in chunks:
|
|
|
|
# Create objects when addres in view
|
|
if addr in addrs and not chunks[addr]["loaded"]:
|
|
|
|
for object in SortByDistance(chunks[addr]["objects"], camobject):
|
|
if not object.get("object") and not object.get("broken"):
|
|
object["object"] = Reuse.Create(object["name"])
|
|
#object["object"].position = object["position"]
|
|
Reuse.Move(object["object"], object["position"])
|
|
object["object"].orientation = object["orientation"]
|
|
object["object"].scaling = object["scaling"]
|
|
|
|
# Some objects have a height adjustment value
|
|
#object["object"].position[2] += object["object"].get("movez", 0)
|
|
object["object"].applyMovement( (0, 0, object["object"].get("movez", 0) ) )
|
|
|
|
# Some effects require a reference point
|
|
object["object"]["spawned_by"] = object
|
|
|
|
# Some objects need dynamics to be suspended
|
|
if object.get("suspendDynamics"):
|
|
object["object"].suspendDynamics(True)
|
|
|
|
# Some objects might change mesh during game
|
|
if object["object"].get("good"):
|
|
object["object"].replaceMesh(object["object"]["good"])
|
|
|
|
# Some objects are lights and have light data
|
|
if object.get("light_data"):
|
|
object["object"].blenderObject.data = object["light_data"]
|
|
|
|
chunks[addr]["loaded"] = True
|
|
|
|
# Breaking to skip to next frame and
|
|
# re-evaluate whether the FPS is good
|
|
return
|
|
|
|
# Level Of Details for spawn objects
|
|
|
|
# This is needed because normal LOD system
|
|
# with those objects sometimes gives a
|
|
# segmentation fault.
|
|
|
|
elif addr in addrs and chunks[addr]["loaded"]:
|
|
|
|
sortedObjects = SortByDistance(chunks[addr]["objects"], camobject, True)
|
|
|
|
# closestUnspawned = [1000000, 0, {}]
|
|
# for object in sortedObjects:
|
|
# if not object[2].get("object") and object[0] < closestUnspawned[0]:
|
|
# closestUnspawned = object
|
|
#
|
|
# if Reuse.reuse.get(closestUnspawned[2].get("name")):
|
|
# closestUnspawned[2]["object"] = Reuse.Create(closestUnspawned[2].get("name"))
|
|
# closestUnspawned[2]["object"].position = closestUnspawned[2]["position"]
|
|
# closestUnspawned[2]["object"].orientation = closestUnspawned[2]["orientation"]
|
|
# closestUnspawned[2]["object"].scaling = closestUnspawned[2]["scaling"]
|
|
#
|
|
# # Some objects have a height adjustment value
|
|
# closestUnspawned[2]["object"].position[2] += closestUnspawned[2]["object"].get("movez", 0)
|
|
#
|
|
# # Some effects require a reference point
|
|
# closestUnspawned[2]["object"]["spawned_by"] = closestUnspawned[2]
|
|
#
|
|
# # Some objects need dynamics to be suspended
|
|
# if closestUnspawned[2].get("suspendDynamics"):
|
|
# closestUnspawned[2]["object"].suspendDynamics(True)
|
|
|
|
|
|
|
|
for distance, n, object in sortedObjects:
|
|
#
|
|
# if object["name"] == closestUnspawned[2].get("name") and distance > closestUnspawned[0] and object.get("object"):
|
|
# Reuse.Delete(object["object"])
|
|
# object["object"] = None
|
|
|
|
# Optimizing Dani not being to go through benches
|
|
if object.get("tempDynamics"):
|
|
object["object"].suspendDynamics()
|
|
object["tempDynamics"] = False
|
|
|
|
if "lods" in object and object.get("object"):
|
|
distance = object["object"].getDistanceTo(location)
|
|
for lod in object["lods"]:
|
|
|
|
lodDistance = object["lods"][lod]
|
|
if int(distance) <= lodDistance:
|
|
break
|
|
|
|
if object["object"].name != lod:
|
|
|
|
Reuse.Delete(object["object"])
|
|
object["object"] = Reuse.Create(lod)
|
|
Reuse.Move(object["object"], object["position"])
|
|
#object["object"].position = object["position"]
|
|
object["object"].orientation = object["orientation"]
|
|
object["object"].scaling = object["scaling"]
|
|
|
|
# Some objects have a height adjustment value
|
|
object["object"].applyMovement( (0, 0, object["object"].get("movez", 0) ) )
|
|
#object["object"].position[2] += object["object"].get("movez", 0)
|
|
|
|
# Some effects require a reference point
|
|
object["object"]["spawned_by"] = object
|
|
|
|
# Some objects need dynamics to be suspended
|
|
if object.get("suspendDynamics"):
|
|
object["object"].suspendDynamics(True)
|
|
|
|
taskschedule = {}
|
|
|
|
def ScheduleTask(taskname, FPSfactor, task, *args):
|
|
|
|
# This function adds a function to the schedule
|
|
|
|
taskObject = {"FPSfactor":FPSfactor,
|
|
"task":task,
|
|
"args":args}
|
|
taskschedule[taskname] = taskObject
|
|
|
|
def ExecuteScheduled():
|
|
|
|
# This function executes a scheduled task
|
|
|
|
# for taskname in taskschedule:
|
|
# print("Scheduled:", taskname)
|
|
# print()
|
|
|
|
# Trying to execute something
|
|
for taskname in taskschedule:
|
|
|
|
taskObject = taskschedule[taskname]
|
|
|
|
if GoodFPS("[Scheduled] "+taskname, taskObject["FPSfactor"]):
|
|
del taskschedule[taskname]
|
|
taskObject["task"](*taskObject["args"])
|
|
return
|
|
|
|
def Force(value):
|
|
|
|
# This function adjusts acceleration
|
|
# to fps
|
|
|
|
return value * ( ToFPS() ** 2 )
|
|
|
|
def Velocity(value):
|
|
|
|
# This function adjusts velocity
|
|
# to fps
|
|
|
|
return value * ToFPS()
|
|
|
|
def PrintTime():
|
|
# This function is used for testing
|
|
print(bge.logic.getRealTime())
|
|
|
|
def BuildingsLOD():
|
|
|
|
# Function deals with levels of details
|
|
# of buildings.
|
|
|
|
scene = bge.logic.getCurrentScene()
|
|
dani = scene.objects["Dani_Box"]
|
|
cam = scene.active_camera
|
|
|
|
if "LODchunks" not in bge.logic.globalDict:
|
|
# bge.logic.globalDict["LODchunks"] = {"TheRacetrack":{
|
|
# "object":scene.objects["TheRacetrack"],
|
|
# "high":"TheRacetrackHigh",
|
|
# "radius":1000,
|
|
# "now":"high",
|
|
# "name":"Papses Racetrack"
|
|
# }, #
|
|
|
|
# "TheCity":{
|
|
# "object":scene.objects["TheCity"],
|
|
# "high":"TheCityHigh",
|
|
# "radius":1500,
|
|
# "now":"high",
|
|
# "name":"Dune Town"
|
|
# }, #
|
|
|
|
# "TheHouse":{
|
|
# "object":scene.objects["TheHouse"],
|
|
# "high":"TheHouseGood",
|
|
# "low":"TheHouseBorked",
|
|
# "parent":"TheCity",
|
|
# "radius":200,
|
|
# "now":"high",
|
|
# "name":"Looparound 8\nDani's Home"
|
|
# }, #
|
|
|
|
# "HallwayPictures":{
|
|
# "object":scene.objects["HallwayPictures"],
|
|
# "high":"HallwayPictures",
|
|
# "parent":"TheHouse",
|
|
# "radius":180,
|
|
# "now":"high"
|
|
# },
|
|
|
|
# "Computer":{
|
|
# "object":scene.objects["Computer"],
|
|
# "high":"Computer",
|
|
# "parent":"TheHouse",
|
|
# "radius":180,
|
|
# "now":"high"
|
|
# },
|
|
|
|
# "Just3000Wreck":{
|
|
# "object":scene.objects["Just3000Wreck"],
|
|
# "high":"Just3000Wreck",
|
|
# "parent":"TheHouse",
|
|
# "radius":180,
|
|
# "now":"high"
|
|
# },
|
|
|
|
# "KartingTrack":{
|
|
# "object":scene.objects["KartingTrack"],
|
|
# "high":"KartingTrack_High",
|
|
# "low":"KartingTrack_Low",
|
|
# "parent":"TheCity",
|
|
# "radius":200,
|
|
# "now":"high",
|
|
# "name":"Karting Track"
|
|
# },
|
|
|
|
# "Karting_Decorations":{
|
|
# "object":scene.objects["Karting_Decorations"],
|
|
# "high":"Karting_Decorations",
|
|
# "parent":"KartingTrack",
|
|
# "radius":180,
|
|
# "now":"high"
|
|
# },
|
|
|
|
# "KartingGamesCollider":{
|
|
# "object":scene.objects["KartingGamesCollider"],
|
|
# "high":"KartingGamesCollider",
|
|
# "parent":"KartingTrack",
|
|
# "radius":180,
|
|
# "now":"high"
|
|
# },
|
|
|
|
# "KartingGamesObject":{
|
|
# "object":scene.objects["KartingGamesObject"],
|
|
# "high":"KartingGamesObject",
|
|
# "parent":"KartingTrack",
|
|
# "radius":180,
|
|
# "now":"high"
|
|
# },
|
|
|
|
# "PoliceStation":{
|
|
# "object":scene.objects["PoliceStation"],
|
|
# "high":"PoliceStationHigh",
|
|
# "low":"PoliceStationLow",
|
|
# "parent":"TheCity",
|
|
# "radius":180,
|
|
# "now":"high",
|
|
# "name":"Police Station"
|
|
# },
|
|
|
|
# "PoliceStation_Ghost":{
|
|
# "object":scene.objects["PoliceStation_Ghost"],
|
|
# "high":"PoliceStation_Ghost",
|
|
# "parent":"PoliceStation",
|
|
# "radius":180,
|
|
# "now":"high"
|
|
# },
|
|
|
|
|
|
# }
|
|
bge.logic.globalDict["LODchunks"] = {}
|
|
|
|
for object in scene.objects:
|
|
if "LOD" in object:
|
|
lod = {}
|
|
for key in ["high", "low", "parent", "radius", "name"]:
|
|
if "LOD_"+key in object:
|
|
lod[key] = object["LOD_"+key]
|
|
|
|
lod["now"] = "high"
|
|
lod["object"] = object
|
|
lod["name_radius"] = lod.get("radius", 100)*0.8
|
|
|
|
bge.logic.globalDict["LODchunks"][object["LOD"]] = lod
|
|
|
|
bge.logic.globalDict["LODcurrent"] = "Home"
|
|
|
|
chunks = bge.logic.globalDict["LODchunks"]
|
|
|
|
for chunkname in chunks:
|
|
chunk = chunks[chunkname]
|
|
|
|
# Making sure to not overdo the LOD system
|
|
if chunk.get("timer", 0) + 5 > bge.logic.getRealTime():
|
|
continue
|
|
chunk["timer"] = bge.logic.getRealTime()
|
|
|
|
|
|
# Making low when out of bounds
|
|
forcehide = False
|
|
if chunks.get(chunk.get("parent", ""), {}).get("now") == "low":
|
|
forcehide = True
|
|
if chunk["object"].visible:
|
|
chunk["now"] = "high"
|
|
|
|
if chunk["now"] == "low" and dani.getDistanceTo(chunk["object"]) < chunk["radius"]:
|
|
print(consoleForm(chunk["object"]), "is now highres.")
|
|
if "low" in chunk:
|
|
chunk["object"].replaceMesh(chunk["high"])
|
|
|
|
chunk["object"].visible = True
|
|
chunk["object"].restorePhysics()
|
|
chunk["now"] = "high"
|
|
|
|
# if "name" in chunk:
|
|
# bge.logic.globalDict["print"] = chunk["name"]
|
|
|
|
elif chunk["now"] == "high" and ( dani.getDistanceTo(chunk["object"]) > chunk["radius"] or forcehide):
|
|
print(consoleForm(chunk["object"]), "is now lowres.")
|
|
if "low" in chunk and not forcehide:
|
|
print("making mesh low")
|
|
chunk["object"].replaceMesh(chunk["low"])
|
|
else:
|
|
print("hiding mesh")
|
|
chunk["object"].visible = False
|
|
|
|
if not dani.get("race"):
|
|
chunk["object"].suspendPhysics()
|
|
|
|
chunk["now"] = "low"
|
|
|
|
if chunk["now"] == "high" and chunk.get("name") \
|
|
and bge.logic.globalDict["LODcurrent"] != chunk.get("name")\
|
|
and dani.getDistanceTo(chunk["object"]) < chunk["name_radius"]:
|
|
|
|
bge.logic.globalDict["print"] = chunk["name"]
|
|
bge.logic.globalDict["LODcurrent"] = chunk["name"]
|
|
|
|
def Precalculate():
|
|
|
|
# Precalculating objects for smoothness
|
|
|
|
settings = bge.logic.globalDict.get("settings", {})
|
|
|
|
preData = {
|
|
"House_Shelf":4,
|
|
"Moria's Bed":1,
|
|
"Moria's Bed.001":1,
|
|
"Sparkle": 100,
|
|
"Smoke": 20,
|
|
"Fire": 200,
|
|
"NitroCone":5,
|
|
"Road Blocker": 12,
|
|
|
|
"Metal_Bench_Seat":15,
|
|
"Metal_Bench_Beam":3,
|
|
"Metal_Bench_Handle":6
|
|
|
|
}
|
|
|
|
if settings.get("trees"):
|
|
|
|
preData["NormalTreeTrunk"] = 30
|
|
preData["TreeBillboard"] = 50
|
|
preData["PalmCutout"] = 32
|
|
preData["PalmLow"] = 16
|
|
preData["NormalPalmTrunk"] = 16
|
|
|
|
if settings.get("poles"):
|
|
|
|
preData["LightStand"] = 50
|
|
preData["LightStand.Borked.Head"] = 5
|
|
preData["LightStand.Borked.Tail"] = 5
|
|
|
|
if settings.get("fences"):
|
|
|
|
preData["MetalGate_good"] = 100
|
|
preData["GatePart"] = 100
|
|
preData["GatePart.Level0"] = 50
|
|
preData["GatePart.Level1"] = 50
|
|
|
|
|
|
bge.logic.globalDict["preData"] = preData
|
|
|
|
print("Precalculating... ")
|
|
|
|
for n, object in enumerate(preData):
|
|
for i in range( preData[object] ):
|
|
Reuse.Create(object, selfDestructFrames = 1, selfDestructInactive = False )
|