Compare commits

...

83 Commits
0.0.5 ... main

Author SHA1 Message Date
bozarre 6bab53b555 Update README.md
UE5.5
2025-02-25 20:56:53 +00:00
Jérémie GABOLDE 8f9217bf6f add new SFX and ability to place a beacon and respawn on it 2025-02-20 00:39:59 +01:00
FFlombs59 5dd86517c7 Add variant couldown mode
Press G to switch between "GCharge refilling independently" or "GCharge wait for previous GCharge refilling"
2025-02-12 01:37:27 +01:00
FFlombs59 839602bce3 Fix G Charge Calcul and plug the system
Player has 3 GCharge which are the Grappling Ammos. Making special move increase the GCharge couldown
2025-02-11 01:46:01 +01:00
Jérémie GABOLDE a93d473f3d .zip to LFS 2025-02-08 00:41:23 +01:00
Jérémie GABOLDE 8a8216dc4d adding some SFX 2025-02-08 00:32:38 +01:00
bozarre a72a008600 revert f3ce5e2fd5
revert test
2025-02-07 21:02:26 +00:00
FFlombs59 f3ce5e2fd5 test 2025-02-07 21:59:42 +01:00
Jérémie GABOLDE bbe112b604 removing binaries and intermediate folders from plugins 2025-02-07 21:34:28 +01:00
FFlombs59 1c63605918 Work on G-Charge:
UI Feedback + Functional with 3 bullets + R&D on couldown modifier on specific movement (no functional yet). Unplugged everything to not break the controller.
2025-01-25 01:53:20 +01:00
Jérémie GABOLDE 55bc9c3f54 renamed widget Cross>h<air 2025-01-22 22:09:25 +01:00
sdfsdf 30b2c1465c remove electronic nodes binaries 2025-01-22 21:51:25 +01:00
sdfsdf 42764ccd59 Remove eletronic nodes intermediates 2025-01-22 21:49:29 +01:00
FFlombs59 49bbbc2dce Compile electronic node 2025-01-22 21:35:46 +01:00
Jérémie GABOLDE 69ac02169c git ignore plugins/marketplace intermediate and binaries 2025-01-22 21:22:53 +01:00
Jérémie GABOLDE c31c441684 terminus fonts 2025-01-22 21:16:25 +01:00
Jérémie GABOLDE c14c5432e7 Plugins BlueprintAssist & ElectronicNodes 2025-01-20 22:54:16 +01:00
Jérémie GABOLDE 98c9ace5d8 update but sorry i forgot what i did 2025-01-20 22:53:42 +01:00
Jérémie GABOLDE 8d4e4501e2 more 3C tweaks 2025-01-08 00:51:38 +01:00
Jérémie GABOLDE d07fa2d299 Tweaking 3C friction acceleration and stamina cost 2025-01-07 21:41:21 +01:00
Jérémie GABOLDE 8974148824 Some bug fixing of Friction and tweaking metrics 2025-01-07 01:01:43 +01:00
Jérémie GABOLDE 844e03760b wire fix collision + dustySand lighting 2025-01-04 21:02:37 +01:00
Jérémie GABOLDE 5b59ec1ba3 big update : grapplinghook + stamina + health refacto 2025-01-04 19:45:41 +01:00
Jérémie GABOLDE f1f0318b54 dune 2 ost 2025-01-04 19:44:36 +01:00
Jérémie GABOLDE 0a6c810f0b refacto grappling hook & stamina 2025-01-04 16:16:09 +01:00
Jérémie GABOLDE 035cfbed04 dustySand biome tests 2025-01-04 01:25:50 +01:00
Jérémie GABOLDE 7289d51563 env meshes imported 2025-01-04 00:35:31 +01:00
Jérémie GABOLDE ae736ee8c4 you can now grapple anywhere (can break the game) 2025-01-04 00:35:14 +01:00
Jérémie GABOLDE 291aee7fe5 enable electronic node plugin 2025-01-04 00:33:41 +01:00
Jérémie GABOLDE f1f879097d new materials DustySand & DamageRock 2025-01-04 00:33:19 +01:00
Jérémie GABOLDE bd0ac82b99 blender meshes env 2025-01-04 00:32:43 +01:00
Jérémie GABOLDE bc4d0a2bcd force ElectronicNode plugin to be 5.5 compatible 2025-01-04 00:32:20 +01:00
Jérémie GABOLDE ff78241314 minor fix pawn + imported new mesh like the superdupperComputer 2024-12-29 01:03:01 +01:00
Jérémie GABOLDE e689430fc8 Plugins FAB and Electronic Nodes Added + fixed compilation 2024-12-28 23:55:36 +01:00
Jérémie GABOLDE 7697680b48 Modified character gravity behavior 2024-12-20 23:19:55 +01:00
FFlombs59 6ba0413c63 Planetarium start R&D + BP_Feedback
Creating a new map to build the foundatuion of a puzzle around the Gravitium
2024-12-18 01:03:23 +01:00
Jérémie GABOLDE e4fcacd862 BETTER CAMERA WHEN CHANGING GRAVITY 2024-12-09 23:50:59 +01:00
Jérémie GABOLDE 75d718f880 Blender exporter bugfixes + Gravitium v1 2024-12-09 23:00:10 +01:00
Jérémie GABOLDE 8736d3f4b7 Env stuff 2024-12-09 00:57:09 +01:00
Jérémie GABOLDE 97a025dce7 Gym env update 2024-12-07 17:07:22 +01:00
Jérémie GABOLDE 7cc84a24c4 volcanic rock fbx 2024-12-07 00:26:53 +01:00
Jérémie GABOLDE db0b04271a gym env 2024-12-07 00:25:51 +01:00
Jérémie GABOLDE 771b23154e volcanic rock 2024-12-07 00:25:26 +01:00
Jérémie GABOLDE b26f988ad7 volcanic rock update ? 2024-12-04 23:42:15 +01:00
Jérémie GABOLDE 020db04f31 volcanic rocks update 2024-12-04 23:39:26 +01:00
Jérémie GABOLDE 5a3ae3be48 Volcanic Rocks Blender 2024-12-04 23:28:39 +01:00
Jérémie GABOLDE 3a475f1139 blender exporter python addon + convex collision gn 2024-11-22 23:40:44 +01:00
Jérémie GABOLDE a2b32c4481 test stuff 2024-11-20 00:33:29 +01:00
Jérémie GABOLDE 4c3d991d29 UE 5.5 migration 2024-11-20 00:33:29 +01:00
Jérémie GABOLDE 1cb85eab8f Blender tool for exporting 2024-11-20 00:33:29 +01:00
FFlombs59 fa05591cd0 Merge branch 'main' of https://bozlab.freeboxos.fr/bozarre/GrapplingGravity 2024-11-19 22:28:28 +01:00
Jérémie GABOLDE 26972e89b0 Rocks with a special shape ;) 2024-11-19 21:53:59 +01:00
FFlombs59 2ab7d1568c Gym Metrics
Start to create tools to define metrics and control the level design composition
2024-11-19 01:40:41 +01:00
Jérémie GABOLDE b7f252160f minor bug fixes 2024-11-07 00:44:15 +01:00
Jérémie GABOLDE 4f3916b70a Liquid Gate Tunnel v1 2024-11-06 23:24:55 +01:00
Jérémie GABOLDE bc89429a2c Liquid Volume tweaks + Gym level 2024-11-05 20:23:25 +01:00
Jérémie GABOLDE efda4128fd Liquid Volumes proto 2024-11-04 23:32:28 +01:00
Jérémie GABOLDE 54fcba4f6b update maps 2024-09-28 13:10:32 +02:00
Jérémie GABOLDE 018b64e56e adding coyote time to ground jumping 2024-09-28 13:10:11 +02:00
Jérémie GABOLDE 4efc99c292 cables 2024-09-28 12:22:50 +02:00
Jérémie GABOLDE e02111e871 cable mesh 2023-12-06 01:06:45 +01:00
Jeremie GABOLDE 12e38abae2 fix splinemeshes 2023-12-05 11:30:36 +01:00
Jérémie GABOLDE 8b4b5e54bf default map and mode 2023-12-05 00:22:31 +01:00
Jérémie GABOLDE 89c5df1a2a homescreen map cables 2023-12-05 00:21:30 +01:00
Jérémie GABOLDE cce17cae23 cableSplinemesh 2023-12-05 00:21:10 +01:00
Jérémie GABOLDE 274ee76526 player char update 2023-12-04 23:47:27 +01:00
Jérémie GABOLDE 1b1764dced main map update 2023-12-04 23:47:10 +01:00
Jérémie GABOLDE 1d0fa8e490 SplineMesh update 2023-12-04 23:46:29 +01:00
Jérémie GABOLDE 0b46c57a68 archi meshes collision and tweaks 2023-12-04 23:45:50 +01:00
Jérémie GABOLDE 635195c12f gravity comp tick bool 2023-12-04 23:45:23 +01:00
Jérémie GABOLDE dc0b7c2dcd cable meshes 2023-12-04 23:43:57 +01:00
Jérémie GABOLDE 65387ed4ec sequence homescreen 2023-12-01 21:02:37 +01:00
Jérémie GABOLDE 1f61ef05f6 home screen menu map 2023-12-01 20:22:09 +01:00
Jérémie GABOLDE fb02265dec move archi meshes 2023-12-01 20:18:38 +01:00
Jérémie GABOLDE abd10197ea meshes 2023-12-01 20:13:40 +01:00
Jérémie GABOLDE 3383b76238 home screen menu classes & UI 2023-12-01 20:08:39 +01:00
Jérémie GABOLDE 974cf50793 ship meshes 2023-12-01 20:05:04 +01:00
Jérémie GABOLDE 2facd88064 text cine 2023-11-20 20:38:47 +01:00
Jérémie GABOLDE 3cadd3b6ab Merge remote-tracking branch 'origin/main' 2023-11-20 20:36:51 +01:00
Jérémie GABOLDE 2276c7415a intro audio 2023-11-20 20:36:34 +01:00
Alex 757463a270 ADD RamsterZ traversal animation pack retargeted for UE5 rig 2023-11-20 20:14:30 +01:00
Jérémie GABOLDE 64effa5844 intro cine 2023-11-19 00:54:42 +01:00
Jérémie GABOLDE 1cfbc10731 Level tuto 2023-11-18 23:26:10 +01:00
995 changed files with 54229 additions and 359 deletions

1
.gitattributes vendored
View File

@ -12,3 +12,4 @@
*.xcf filter=lfs diff=lfs merge=lfs -text lockable *.xcf filter=lfs diff=lfs merge=lfs -text lockable
*.jpg filter=lfs diff=lfs merge=lfs -text lockable *.jpg filter=lfs diff=lfs merge=lfs -text lockable
*.blend filter=lfs diff=lfs merge=lfs -text lockable *.blend filter=lfs diff=lfs merge=lfs -text lockable
*.zip filter=lfs diff=lfs merge=lfs -text lockable

2
.gitignore vendored
View File

@ -50,6 +50,7 @@ SourceArt/**/*.tga
# Binary Files # Binary Files
Binaries/* Binaries/*
Plugins/*/Binaries/* Plugins/*/Binaries/*
Plugins/Marketplace/*/Binaries/*
# Packages # Packages
Packages/* Packages/*
@ -74,6 +75,7 @@ Saved/*
# Compiled source files for the engine to use # Compiled source files for the engine to use
Intermediate/* Intermediate/*
Plugins/*/Intermediate/* Plugins/*/Intermediate/*
Plugins/Marketplace/*/Intermediate/*
# Cache files for the editor to use # Cache files for the editor to use
DerivedDataCache/* DerivedDataCache/*

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
AssetSources/Meshes/STM_ARC_CubicPillar.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_ARC_CubicPillar.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Archi_Wall.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Cable_01.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Cable_01.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Cable_02.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Cable_02.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Cable_03.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Cable_03.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Cable_Plug_01.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Cable_Wire_01.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_ComputerTerminal.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_ComputerTerminal.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_CreppyHeadTubes.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_CreppyHeadTubes.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_DustySand_CubeCorner.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_DustySand_CubeCorner.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Gravitium_Box_01.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Gravitium_Rock_01.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Gravitium_Rock_01.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_HollowRock.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_HollowRock.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_IntroShip_Cockpit.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_IntroShip_Cockpit.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_IntroShip_Exterior.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_IntroShip_Exterior.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_RespawnBeacon.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_RespawnBeacon.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Rock_Flat_Einstein.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Rock_Flat_Einstein.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Rock_Flat_Einstein_1m.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Rock_Flat_Einstein_2m.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_Rock_Flat_Einstein_3m.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_TunelRocks_01.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_TunnelRocks.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_VEG_Tree_001.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_VEG_Tree_001.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_VolcanicRocks.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_VolcanicRocks_01.fbx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/Meshes/STM_VolcanicRocks_02.fbx (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,160 @@
import bpy
import os
from bpy.props import (StringProperty, PointerProperty, EnumProperty, BoolProperty)
from bpy.types import (Panel, Operator, AddonPreferences, PropertyGroup)
bl_info = \
{
"name": "Unreal Exporter",
"author": "Bozarre",
"version": (1, 0, 0),
"blender": (4, 2, 0),
"location": "View 3D > Object Mode > Unreal Exporter",
"description":
"Batch exporter for Unreal Engine",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Exporter",
}
class UEExporterSettings(PropertyGroup):
path : StringProperty(
#name="",
description="Path to Directory (empty means same as curren file)",
default="//",
maxlen=1024,
subtype='FILE_PATH')
source_collection : StringProperty(
description="Collection name (collisions don't have to be in the same collection",
default="Export",
maxlen=1024)
collisions: BoolProperty(
name="Export Collisions",
description="Whether to export the collision shapes",
default = True
)
local_origin: BoolProperty(
name="Export Origin",
description="Whether to use the objects origin as the export origin",
default = False
)
class VIEW3D_PT_PanelExportAll(bpy.types.Panel):
bl_label = "Export Selected"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "UE Exporter"
bl_context = "objectmode"
def draw(self, context):
layout = self.layout
scn = context.scene
layout.label(text="Export Path Location:")
layout.prop(scn.ue_exporter, "path", text="")
layout.label(text="Collection to export")
layout.prop(scn.ue_exporter, "source_collection", text="")
layout.prop(scn.ue_exporter, "local_origin", text=" Export Origin")
layout.prop(scn.ue_exporter, "collisions", text=" Export Collisions")
layout.operator("ue_exporter_obs.separate_export", text='Export Seperate', icon='TRIA_RIGHT')
layout.operator("ue_exporter_obs.combine_export", text='Export Combined', icon='TRIA_RIGHT')
class OBJECT_OT_SeparateExport(bpy.types.Operator):
bl_idname = "ue_exporter_obs.separate_export"
bl_label = "Export Selected"
bl_options = {"UNDO"}
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
SeparateExport(context.scene.ue_exporter.path)
self.report({'INFO'}, 'Exported')
return {'FINISHED'}
class OBJECT_OT_CombineExport(bpy.types.Operator):
bl_idname = "ue_exporter_obs.combine_export"
bl_label = "Export Selected"
bl_options = {"UNDO"}
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
CombineExport(context.scene.ue_exporter.path)
self.report({'INFO'}, 'Did nothing (yet)')
return {'FINISHED'}
def SeparateExport(exportFolder):
collection_collision_export = bpy.data.collections.new("Collision.Export")
bpy.context.scene.collection.children.link(collection_collision_export)
objects = bpy.data.collections[bpy.context.scene.ue_exporter.source_collection].all_objects
bpy.ops.object.select_all(action='DESELECT')
for object in list(objects):
if object.type not in ['MESH']:
continue
if (('UCX_' + object.name) in bpy.data.objects):
collision = bpy.data.objects['UCX_' + object.name]
export_collision = collision.copy()
export_collision.data = collision.data.copy()
export_collision.animation_data_clear()
bpy.context.collection.objects.link(export_collision)
collection_collision_export.objects.link(export_collision)
export_collision.select_set(True)
bpy.context.view_layer.objects.active = export_collision
bpy.ops.object.modifier_apply(modifier='GeometryNodes')
bpy.ops.mesh.separate(type='LOOSE')
object.select_set(True)
exportName = bpy.path.abspath(exportFolder) + object.name + '.fbx'
bpy.ops.export_scene.fbx(filepath=exportName, use_selection=True, mesh_smooth_type='FACE')
bpy.ops.object.select_all(action='DESELECT')
objects = collection_collision_export.all_objects
for object in objects:
object.select_set(True)
bpy.ops.object.delete()
bpy.data.collections.remove(collection_collision_export)
def CombineExport(exportFolder):
return
#TBD
classes = (
VIEW3D_PT_PanelExportAll,
OBJECT_OT_SeparateExport,
OBJECT_OT_CombineExport,
UEExporterSettings
)
def apply_modifiers(obj):
ctx = bpy.context.copy()
ctx['object'] = obj
for _, m in enumerate(obj.modifiers):
try:
ctx['modifier'] = m
bpy.ops.object.modifier_apply(ctx, modifier=m.name)
except RuntimeError:
print(f"Error applying {m.name} to {obj.name}, removing it instead.")
obj.modifiers.remove(m)
for m in obj.modifiers:
obj.modifiers.remove(m)
def register():
for c in classes:
bpy.utils.register_class(c)
bpy.types.Scene.ue_exporter = bpy.props.PointerProperty(type=UEExporterSettings)
def unregister():
for c in reversed(classes):
bpy.utils.unregister_class(c)
del bpy.types.Scene.ue_exporter
if __name__ == "__main__":
register()

View File

@ -0,0 +1,619 @@
##
# A script to split simple, architectural geometry into convex pieces.
#
# This script makes use of Blender's built-in "Split Concave Faces" clean-up
# algorithm to break-up the faces of an object into convex pieces. The script
# attempts to identify all the edges that represent convex boundaries, and then
# it splits objects up along those edges. Each resulting piece is then made into
# a closed object by converting it into a convex hull.
#
# Be sure to select the object you wish the split into convex pieces before
# running the script.
#
# NOTE: This script is expecting to work with flat, reasonably clean geometry.
# For example, it is expected to be used when generating collision on the
# ceiling and walls of an architectural visualization project, but is not
# expected to perform well with round or n-gon geometry. Using
# create_closed_objects=True and matchup_degenerates=True, in particular, does
# not work well with objects that have openings inside.
#
# If this script doesn't work for you, a plug-in like V-HACD may work better.
# This script was written to handle cases V-HACD did not handle well -- flat,
# reasonably rectangular arch. vis. geometry.
#
# @author Guy Elsmore-Paddock <guy.paddock@gmail.com>
#
import bmesh
import bpy
import operator
import re
from itertools import combinations, count
from math import atan2, pi, radians, degrees
from mathutils import Vector
def split_into_convex_pieces(ob, create_closed_objects=True,
matchup_degenerates=True):
object_name = ob.name
deselect_all_objects()
make_all_faces_convex(ob)
eliminated_piece_names = \
split_on_convex_boundaries(
ob,
create_closed_objects,
matchup_degenerates
)
rename_pieces(object_name, eliminated_piece_names)
# Deselect everything, for the convenience of the user.
deselect_all_objects()
def make_all_faces_convex(ob):
bpy.context.view_layer.objects.active = ob
bpy.ops.object.mode_set(mode='EDIT')
# This is what actually defines the new geometry -- Blender creates the
# convex shapes we need to split by.
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.vert_connect_concave()
bpy.ops.mesh.select_all(action='DESELECT')
##
# Splits an object into smaller pieces by its convex, planar edges.
#
# In an ideal world, we could just split the object by all the edges that are
# attached to -- and are co-planar with -- the faces of the object, since those
# edges are most likely to represent the convex boundaries of the object. But,
# Blender does not provide an easy way to find such edges.
#
# Instead, we use several heuristics to simulate this type of selection:
# 1. First, we select all the sharp edges of the object, since sharp edges are
# only co-planar with one of the faces they connect with and are therefore
# unlikely to represent convex boundary edges.
# 2. Second, we select all edges that are similar in angle to the sharp edges,
# to catch any edges that are almost steep enough to be sharp edges.
# 3. Third, we invert the selection, which should (hopefully) cause all the
# convex boundary edges we want to be selected.
# 4. Fourth, we seek out any sharp edges that connect with the convex boundary
# edges, since we will need to split on these edges as well so that our
# "cuts" go all the way around the object (e.g. if the convex boundary
# edges lay on the top and bottom faces of an object, we need to select
# sharp edges to connect the top and bottom edges on the left and right
# sides so that each split piece is a complete shape instead of just
# disconnected, detached planes).
# 4. Next, we split the object by all selected edges, which should result in
# creation of each convex piece we seek.
#
def split_on_convex_boundaries(ob, create_closed_objects=True,
matchup_degenerates=True):
bpy.ops.object.mode_set(mode='EDIT')
select_convex_boundary_edges(ob)
# Now perform an vertex + edge split along each selected edge, which should
# result in the object being broken-up along each planar edge and connected
# sharp edges (e.g. on corners).
bpy.ops.mesh.edge_split(type='VERT')
# Now, just break each loose part off into a separate object.
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.separate(type='LOOSE')
if create_closed_objects:
# And then make each piece fully enclosed.
return create_closed_shapes_from_pieces(ob, matchup_degenerates)
else:
return []
##
# Selects all edges that denote the boundaries of convex pieces.
#
# This is a multi-step process driven by heuristics:
# 1. First, we select all the sharp edges of the object, since sharp edges are
# only co-planar with one of the faces they connect with and are therefore
# unlikely to represent convex boundary edges.
# 2. Second, we select all edges that are similar in length to the sharp
# edges, to catch any edges that are almost steep enough to be sharp edges.
# 3. Third, we invert the selection, which should (hopefully) cause all the
# convex boundary edges we want to be selected.
#
def select_convex_boundary_edges(ob, max_edge_length_proportion=0.1):
bpy.ops.object.mode_set(mode='EDIT')
mesh = ob.data
bm = bmesh.from_edit_mesh(mesh)
# Enter "Edge" select mode
bpy.context.tool_settings.mesh_select_mode = [False, True, False]
# Find all sharp edges and edges of similar length
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.edges_select_sharp()
bpy.ops.mesh.select_similar(type='LENGTH', threshold=0.01)
# Invert the selection to find the convex boundary edges.
bpy.ops.mesh.select_all(action='INVERT')
bm.faces.ensure_lookup_table()
bm.edges.ensure_lookup_table()
edges_to_select = []
max_edge_length = max(ob.dimensions) * max_edge_length_proportion
for selected_edge in [e for e in bm.edges if e.select]:
edge_bridges =\
find_shortest_edge_bridges(
selected_edge,
max_edge_length=max_edge_length
)
for path in edge_bridges.values():
for edge in path:
edges_to_select.append(edge)
# Select the edges after we pick which edges we *want* to select, to ensure
# that we only base our decisions on the initial convex boundary edges.
for edge in edges_to_select:
edge.select = True
##
# Locate the shortest path of edges to connect already-selected edges.
#
# This is used to find the additional edges that must be selected for a cut
# along a convex boundary to create a complete, closed object shape.
#
# The max edge length argument can be provided to avoid trying to find
# connections between convex boundaries that are very far apart in the same
# object.
#
def find_shortest_edge_bridges(starting_edge, max_edge_length=None):
edge_bridges = find_bridge_edges(starting_edge, max_edge_length)
sorted_edge_bridges = sorted(edge_bridges, key=lambda eb: eb[0])
edge_solutions = {}
for edge_bridge in sorted_edge_bridges:
path_distance, final_edge, path = edge_bridge
# Skip edges we've already found a min-length path to
if final_edge not in edge_solutions.keys():
edge_solutions[final_edge] = path
print(f"Shortest edge bridges for starting edge '{str(starting_edge.index)}':")
if len(edge_solutions) > 0:
print(
" - " +
"\n - ".join(map(
lambda i: str(
(i[0].index, str(list(map(lambda e: e.index, i[1]))))
),
edge_solutions.items()
)))
print("")
print("")
return edge_solutions
##
# Performs a recursive, depth-first search from a selected edge to other edges.
#
# This returns all possible paths -- and distances of those paths -- to traverse
# the mesh from the starting, selected edge to another selected edge. To avoid
# a lengthy search, the max_depth parameter controls how many levels of edges
# are searched.
#
# The result is an array of tuples, where each tuple contains the total distance
# of the path, the already-selected edge that the path was able to reach, and
# the list of edges that would need to be selected in order to reach that
# destination edge.
#
def find_bridge_edges(edge, max_edge_length=None, max_depth=3, current_depth=0,
path_distance=0, edge_path=None, seen_verts=None):
if edge_path is None:
edge_path = []
if seen_verts is None:
seen_verts = []
# Don't bother searching edges we've seen
if edge in edge_path:
return []
if (current_depth > 0):
first_edge = edge_path[0]
edge_length = edge.calc_length()
# Don't bother searching edges along the same normal as the first edge.
# We want our cuts to be along convex boundaries that are perpendicular.
if have_common_face(first_edge, edge):
return []
if edge.select:
return [(path_distance, edge, edge_path)]
# Disqualify edges that are too long.
if max_edge_length is not None and edge_length > max_edge_length:
print(
f"Disqualifying edge {edge.index} because length [{edge_length}] > [{max_edge_length}"
)
return []
if current_depth == max_depth:
return []
new_edge_path = edge_path + [edge]
bridges = []
for edge_vert in edge.verts:
# Don't bother searching vertices we've already seen (no backtracking).
if edge_vert in seen_verts:
continue
new_seen_verts = seen_verts + [edge_vert]
for linked_edge in edge_vert.link_edges:
# Don't bother searching selected edges connected to the starting
# edge, since that gets us nowhere.
if linked_edge.select and current_depth == 0:
continue
edge_length = linked_edge.calc_length()
found_bridge_edges = find_bridge_edges(
edge=linked_edge,
max_edge_length=max_edge_length,
max_depth=max_depth,
current_depth=current_depth + 1,
path_distance=path_distance + edge_length,
edge_path=new_edge_path,
seen_verts=new_seen_verts
)
bridges.extend(found_bridge_edges)
return bridges
def create_closed_shapes_from_pieces(ob, matchup_degenerates=True,
min_volume=0.1):
print("Converting each piece into a closed object...")
degenerate_piece_names = []
for piece in name_duplicates_of(ob):
if not make_piece_convex(piece):
degenerate_piece_names.append(piece.name)
degenerate_count = len(degenerate_piece_names)
print("")
print(f"Total degenerate (flat) pieces: {degenerate_count}")
print("")
eliminated_piece_names = []
if matchup_degenerates:
if degenerate_count > 10:
# TODO: Hopefully, some day, find a good deterministic way to
# automatically match up any number of degenerate pieces using a
# heuristic that generates sane geometry.
print(
"There are too many degenerates for reliable auto-matching, so "
"it will not be performed. You will need to manually combine "
"degenerate pieces.")
print("")
else:
eliminated_piece_names.extend(
matchup_degenerate_pieces(degenerate_piece_names, min_volume)
)
eliminated_piece_names.extend(
eliminate_tiny_pieces(degenerate_piece_names, min_volume)
)
return eliminated_piece_names
def matchup_degenerate_pieces(degenerate_piece_names, min_volume=0.1):
pieces_eliminated = []
degenerate_volumes = find_degenerate_combos(degenerate_piece_names)
print("Searching for a way to match-up degenerates into volumes...")
for piece1_name, piece1_volumes in degenerate_volumes.items():
# Skip pieces already joined with degenerate pieces we've processed
if piece1_name not in degenerate_piece_names:
continue
piece1 = bpy.data.objects[piece1_name]
piece1_volumes_asc = dict(
sorted(
piece1_volumes.items(),
key=operator.itemgetter(1)
)
)
piece2 = None
for piece2_name, combo_volume in piece1_volumes_asc.items():
# Skip pieces that would make a volume that's too small, or that
# have been joined with degenerate pieces we've processed
if combo_volume < min_volume or piece2_name not in degenerate_piece_names:
continue
else:
piece2 = bpy.data.objects[piece2_name]
break
if piece2 is not None:
degenerate_piece_names.remove(piece2.name)
pieces_eliminated.append(piece2.name)
print(
f" - Combining parallel degenerate '{piece1.name}' with "
f"'{piece2.name}' to form complete mesh '{piece1.name}'."
)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = piece1
piece1.select_set(True)
piece2.select_set(True)
bpy.ops.object.join()
make_piece_convex(piece1)
return pieces_eliminated
def find_degenerate_combos(degenerate_piece_names):
volumes = {}
for piece_combo in combinations(degenerate_piece_names, 2):
piece1_name, piece2_name = piece_combo
piece1 = bpy.data.objects[piece1_name]
piece2 = bpy.data.objects[piece2_name]
if not volumes.get(piece1_name):
volumes[piece1_name] = {}
piece1_mesh = piece1.data
piece1_bm = bmesh.new()
piece1_bm.from_mesh(piece1_mesh)
piece2_mesh = piece2.data
piece2_bm = bmesh.new()
piece2_bm.from_mesh(piece2_mesh)
piece1_bm.faces.ensure_lookup_table()
piece2_bm.faces.ensure_lookup_table()
if (len(piece1_bm.faces) == 0) or (len(piece2_bm.faces) == 0):
continue
piece1_face = piece1_bm.faces[0]
piece2_face = piece2_bm.faces[0]
combo_angle_radians = piece1_face.normal.angle(piece2_face.normal)
combo_angle_degrees = int(round(degrees(combo_angle_radians)))
# We only combine faces that are parallel to each other
if combo_angle_degrees in [0, 180]:
combo_volume = convex_volume(piece1, piece2)
volumes[piece1.name][piece2.name] = combo_volume
return volumes
def eliminate_tiny_pieces(degenerate_piece_names, min_volume=0.1):
eliminated_piece_names = []
tiny_piece_names = [
n for n in degenerate_piece_names
if n not in eliminated_piece_names
and convex_volume(bpy.data.objects.get(n)) < min_volume
]
print("")
print(f"Total remaining tiny pieces: {len(tiny_piece_names)}")
# Delete tiny pieces that are too small to be useful
for tiny_piece_name in tiny_piece_names:
print(f" - Eliminating tiny piece '{tiny_piece_name}'...")
tiny_piece = bpy.data.objects[tiny_piece_name]
bpy.data.objects.remove(tiny_piece, do_unlink=True)
eliminated_piece_names.append(tiny_piece_name)
print("")
return eliminated_piece_names
def make_piece_convex(ob, min_volume=0.1):
print(
f" - Attempting to make '{ob.name}' into a closed, convex "
f"shape."
)
volume_before = convex_volume(ob)
make_convex_hull(ob)
volume_after = convex_volume(ob)
volume_delta = abs(volume_after - volume_before)
# If the volume of the piece is very small when we tried making it convex,
# then it's degenerate -- it's a plane or something flat that we need to
# remove.
is_degenerate = (volume_after < min_volume)
print(f" - Volume before: {volume_before}")
print(f" - Volume after: {volume_after}")
print(f" - Volume delta: {volume_delta}")
print(f" - Is degenerate: {is_degenerate}")
return not is_degenerate
def make_convex_hull(ob):
deselect_all_objects()
bpy.context.view_layer.objects.active = ob
ob.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.convex_hull()
mesh = ob.data
bm = bmesh.from_edit_mesh(mesh)
# Clean-up unnecessary edges
bmesh.ops.dissolve_limit(
bm,
angle_limit=radians(5),
verts=bm.verts,
edges=bm.edges,
)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
def have_common_normal(e1, e2):
e1_normals = map(lambda f: f.normal, e1.link_faces)
e2_normals = map(lambda f: f.normal, e2.link_faces)
common_normals = [n for n in e1_normals if n in e2_normals]
return len(common_normals) > 0
def have_common_face(e1, e2):
common_faces = [f for f in e1.link_faces if f in e2.link_faces]
return len(common_faces) > 0
def convex_volume(*obs):
meshes = []
verts = []
for ob in obs:
mesh = ob.data
bm = bmesh.new()
bm.from_mesh(mesh)
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
# Prevent early garbage collection.
meshes.append(bm)
geom = list(bm.verts) + list(bm.edges) + list(bm.faces)
for g in geom:
if hasattr(g, "verts"):
verts.extend(v.co for v in g.verts)
else:
verts.append(g.co)
return build_volume_from_verts(verts)
def build_volume_from_verts(verts):
# Based on code from:
# https://blender.stackexchange.com/questions/107357/how-to-find-if-geometry-linked-to-an-edge-is-coplanar
origin = sum(verts, Vector((0, 0, 0))) / len(verts)
bm = bmesh.new()
for v in verts:
bm.verts.new(v - origin)
bmesh.ops.convex_hull(bm, input=bm.verts)
return bm.calc_volume()
def deselect_all_objects():
try:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
except:
pass
def rename_pieces(object_name, name_skiplist=None):
if name_skiplist is None:
name_skiplist = []
for duplicate_name, old_index_str, new_index in dupe_name_sequence(object_name, name_skiplist):
piece = bpy.data.objects.get(duplicate_name)
if not piece:
break
old_name = piece.name
new_name = re.sub(fr"(?:01)?\.{old_index_str}$", f"{new_index:02d}", piece.name)
if old_name != new_name:
print(f"Renaming piece '{old_name}' to '{new_name}'.")
piece.name = new_name
def name_duplicates_of(ob):
duplicates = []
for duplicate_name, _, _ in dupe_name_sequence(ob.name):
piece = bpy.data.objects.get(duplicate_name)
if not piece:
break
else:
duplicates.append(piece)
return duplicates
def dupe_name_sequence(base_name, skiplist=None):
if skiplist is None:
skiplist = []
yield base_name, "", 1
new_index = 1
for old_name_index in count(start=1):
old_index_str = f"{old_name_index:03d}"
duplicate_name = f"{base_name}.{old_index_str}"
if duplicate_name in skiplist:
continue
else:
new_index = new_index + 1
yield duplicate_name, old_index_str, new_index
split_into_convex_pieces(bpy.context.view_layer.objects.active)
print("Done!")

BIN
AssetSources/SFX/Retro Swooosh 02.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/SFX/Retro Swooosh 07.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/SFX/Retro Swooosh 16.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/SFX/Retro Vehicle SciFi 01.wav (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
AssetSources/SFX/SD_SFX_Player_Death_Fall.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/VoiceLines/VL_Intro_Pilot.mp3 (Stored with Git LFS) Executable file

Binary file not shown.

BIN
AssetSources/VoiceLines/VL_Intro_Pilot.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
AssetSources/VoiceLines/VL_TrainingInstructor_0001.mp3 (Stored with Git LFS) Executable file

Binary file not shown.

BIN
AssetSources/VoiceLines/VL_TrainingInstructor_0001.wav (Stored with Git LFS) Executable file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
[/Script/EngineSettings.GameMapsSettings] [/Script/EngineSettings.GameMapsSettings]
GameDefaultMap=/Game/Maps/LVL_GravityPlanet_XXL.LVL_GravityPlanet_XXL GameDefaultMap=/Game/Maps/LVL_HomeScreenMenu.LVL_HomeScreenMenu
EditorStartupMap=/Game/Maps/LVL_GravityTestWorld.LVL_GravityTestWorld EditorStartupMap=None
GlobalDefaultGameMode=/Game/PawnControl/Player/BP_Gamemode_Gravity.BP_Gamemode_Gravity_C GlobalDefaultGameMode=/Game/PawnControl/Player/BP_Gamemode_Gravity.BP_Gamemode_Gravity_C
[/Script/Engine.RendererSettings] [/Script/Engine.RendererSettings]
@ -190,6 +190,9 @@ AppliedDefaultGraphicsPerformance=Scalable
+ActiveGameNameRedirects=(OldGameName="/Script/TP_ThirdPerson",NewGameName="/Script/GrapplingGravity") +ActiveGameNameRedirects=(OldGameName="/Script/TP_ThirdPerson",NewGameName="/Script/GrapplingGravity")
+ActiveClassRedirects=(OldClassName="TP_ThirdPersonGameMode",NewClassName="GrapplingGravityGameMode") +ActiveClassRedirects=(OldClassName="TP_ThirdPersonGameMode",NewClassName="GrapplingGravityGameMode")
+ActiveClassRedirects=(OldClassName="TP_ThirdPersonCharacter",NewClassName="GrapplingGravityCharacter") +ActiveClassRedirects=(OldClassName="TP_ThirdPersonCharacter",NewClassName="GrapplingGravityCharacter")
bUseFixedFrameRate=False
FixedFrameRate=100.000000
SmoothedFrameRateRange=(LowerBound=(Type=Inclusive,Value=22.000000),UpperBound=(Type=Exclusive,Value=144.000000))
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
bEnablePlugin=True bEnablePlugin=True
@ -212,11 +215,11 @@ ReverbPlugin=
OcclusionPlugin= OcclusionPlugin=
SoundCueCookQualityIndex=-1 SoundCueCookQualityIndex=-1
-TargetedRHIs=SF_VULKAN_SM5 -TargetedRHIs=SF_VULKAN_SM5
+TargetedRHIs=SF_VULKAN_SM5
+TargetedRHIs=SF_VULKAN_SM6 +TargetedRHIs=SF_VULKAN_SM6
[/Script/Engine.PhysicsSettings] [/Script/Engine.PhysicsSettings]
DefaultGravityZ=0.000000 DefaultGravityZ=0.000000
bTickPhysicsAsync=True
[/Script/Engine.CollisionProfile] [/Script/Engine.CollisionProfile]
-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False) -Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False)
@ -256,6 +259,7 @@ DefaultGravityZ=0.000000
+Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.") +Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.")
+Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") +Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ")
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False,Name="Grappable") +DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False,Name="Grappable")
+EditProfiles=(Name="BlockAll",CustomResponses=((Channel="Grappable")))
-ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") -ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall")
-ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") -ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn")
-ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") -ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Abilities/Properties/BPC_ActorProperty.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
Content/Abilities/Properties/BPC_ActorProperty_Health.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More