Skip to content

Commit

Permalink
scene: Persist level references (#1227)
Browse files Browse the repository at this point in the history
* asset: Add AssetLevelRef type

* asset: Add data annotation for AssetLevelRef

* asset: Register LevelEntity property type

* asset: Add missing global var

* scene: Add placeholder LevelEntity handling

* asset: Document terrain ref behavior

* asset: Ensure sorted level objects

* asset: Add asset_level_find util

* scene: Refactor level prefab spawning

* scene: Pre-allocate level entities

* scene: Resolve level entity refs

* scene: Minor refactor

* scene: Refactor level id allocation

* scene: Update persistent ids on level save

* asset: Optional pos / rot in levels

* asset: Regenerate level schema

* game,asset: End files with a newline

* asset: Re-save all levels

* scene: Support saving level-entity properties

* scene: Allow executing scripts without a terrain
  • Loading branch information
BastianBlokland authored Jan 13, 2025
1 parent 1b81a7d commit 13d25e7
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 78 deletions.
1 change: 1 addition & 0 deletions apps/game/src/prefs.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ static void prefs_save(const GamePrefsComp* prefs) {
// Serialize the preferences to json.
const DataWriteJsonOpts writeOpts = data_write_json_opts();
data_write_json(g_dataReg, &dataBuffer, g_gamePrefsMeta, mem_var(*prefs), &writeOpts);
dynstring_append_char(&dataBuffer, '\n'); // End the file with a new-line.

// Save the data to disk.
const String filePath = prefs_path_scratch();
Expand Down
2 changes: 0 additions & 2 deletions assets/levels/test/pacing.level
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
"id": 1289832977,
"prefab": "TestPacing",
"faction": "A",
"position": { },
"rotation": { "w": 1 }
},
{
"id": 1998597524,
"prefab": "Sun",
"position": { },
"rotation": { "x": 0.2529, "y": 0.2314, "z": -0.0498, "w": 0.9381 }
}
]
Expand Down
18 changes: 15 additions & 3 deletions assets/schemas/level.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@
"items": { "title": "StringHash", "type": "string" }
}
},
"required": [ "prefab", "position", "rotation" ],
"required": [ "prefab" ],
"defaultSnippets": [
{ "label": "New", "body": "^{\n \"prefab\": \"placeholder\",\n \"position\": {},\n \"rotation\": {}\n}" }
{ "label": "New", "body": "^{\n \"prefab\": \"placeholder\"\n}" }
]
},
"AssetLevelFaction": {
Expand Down Expand Up @@ -155,6 +155,16 @@
},
"required": [ "$type", "$name", "$data" ]
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"$type": { "const": "AssetPropertyType_LevelEntity" },
"$name": { "type": "string" },
"$data": { "title": "AssetLevelRef", "description": "Asset level reference", "$ref": "#/$defs/AssetLevelRef" }
},
"required": [ "$type", "$name", "$data" ]
},
{
"type": "object",
"additionalProperties": false,
Expand All @@ -173,8 +183,10 @@
{ "label": "New AssetPropertyType_Quat", "body": "^{\n \"\\$type\": \"AssetPropertyType_Quat\",\n \"\\$name\": \"MyUnion\"\n}" },
{ "label": "New AssetPropertyType_Color", "body": "^{\n \"\\$type\": \"AssetPropertyType_Color\",\n \"\\$name\": \"MyUnion\"\n}" },
{ "label": "New AssetPropertyType_Str", "body": "^{\n \"\\$type\": \"AssetPropertyType_Str\",\n \"\\$name\": \"MyUnion\",\n \"\\$data\": \"\"\n}" },
{ "label": "New AssetPropertyType_LevelEntity", "body": "^{\n \"\\$type\": \"AssetPropertyType_LevelEntity\",\n \"\\$name\": \"MyUnion\",\n \"\\$data\": 1\n}" },
{ "label": "New AssetPropertyType_Asset", "body": "^{\n \"\\$type\": \"AssetPropertyType_Asset\",\n \"\\$name\": \"MyUnion\",\n \"\\$data\": \"placeholder\"\n}" }
]
}
},
"AssetLevelRef": { "title": "u32", "type": "integer", "exclusiveMinimum": 0, "maximum": 4294967295 }
}
}
1 change: 1 addition & 0 deletions libs/asset/include/asset.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ecs_comp_extern(AssetPrefabMapComp);
typedef enum eAssetGraphicPass AssetGraphicPass;
typedef enum eAssetPropertyType AssetPropertyType;
typedef struct sAssetGraphicOverride AssetGraphicOverride;
typedef struct sAssetLevelRef AssetLevelRef;
typedef struct sAssetProduct AssetProduct;
typedef struct sAssetProperty AssetProperty;
typedef struct sAssetRef AssetRef;
9 changes: 6 additions & 3 deletions libs/asset/include/asset_level.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ typedef enum {
} AssetLevelFog;

typedef struct {
u32 id; // Optional unique persistent object id.
u32 id; // Persistent object id.
StringHash prefab;
AssetLevelFaction faction;
f32 scale;
Expand All @@ -36,14 +36,17 @@ typedef struct {

typedef struct {
String name;
AssetRef terrain;
AssetRef terrain; // NOTE: Reference is not automatically resolved.
AssetLevelFog fogMode;
GeoVector startpoint;
HeapArray_t(AssetLevelObject) objects;
HeapArray_t(AssetLevelObject) objects; // Sorted on persistent id.
} AssetLevel;

ecs_comp_extern_public(AssetLevelComp) { AssetLevel level; };

extern DataMeta g_assetLevelDefMeta;

const AssetLevelObject* asset_level_find(const AssetLevel*, u32 persistentId);
u32 asset_level_find_index(const AssetLevel*, u32 persistentId);

bool asset_level_save(AssetManagerComp*, String id, const AssetLevel*);
16 changes: 9 additions & 7 deletions libs/asset/include/asset_property.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ typedef enum eAssetPropertyType {
AssetPropertyType_Quat,
AssetPropertyType_Color,
AssetPropertyType_Str,
AssetPropertyType_LevelEntity,
AssetPropertyType_Asset,

AssetPropertyType_Count,
Expand All @@ -21,13 +22,14 @@ typedef struct sAssetProperty {
StringHash name;
AssetPropertyType type;
union {
f64 data_num;
bool data_bool;
GeoVector data_vec3;
GeoQuat data_quat;
GeoColor data_color;
StringHash data_str;
AssetRef data_asset;
f64 data_num;
bool data_bool;
GeoVector data_vec3;
GeoQuat data_quat;
GeoColor data_color;
StringHash data_str;
AssetLevelRef data_levelEntity;
AssetRef data_asset;
};
} AssetProperty;

Expand Down
9 changes: 8 additions & 1 deletion libs/asset/include/asset_ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@
#include "ecs.h"

/**
* Asset reference.
* Reference to an asset.
*/
typedef struct sAssetRef {
StringHash id;
EcsEntityId entity;
} AssetRef;

/**
* Reference to an entity in a level.
*/
typedef struct sAssetLevelRef {
u32 persistentId;
} AssetLevelRef;

EcsEntityId asset_ref_resolve(EcsWorld*, AssetManagerComp*, const AssetRef*);
7 changes: 6 additions & 1 deletion libs/asset/src/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ typedef GeoVector GeoVector2;
typedef GeoVector GeoVector3;
typedef GeoVector GeoVector4;

DataType g_assetRefType;
DataType g_assetRefType, g_assetLevelRefType;
DataType g_assetGeoColor3Type, g_assetGeoColor4Type;
DataType g_assetGeoColor3NormType, g_assetGeoColor4NormType;
DataType g_assetGeoVec2Type, g_assetGeoVec3Type, g_assetGeoVec4Type;
Expand Down Expand Up @@ -127,6 +127,10 @@ static void asset_data_init_types(void) {
data_reg_field_t(g_dataReg, AssetRef, id, data_prim_t(StringHash), .flags = DataFlags_NotEmpty | DataFlags_InlineField);
data_reg_comment_t(g_dataReg, AssetRef, "Asset reference");

data_reg_struct_t(g_dataReg, AssetLevelRef);
data_reg_field_t(g_dataReg, AssetLevelRef, persistentId, data_prim_t(u32), .flags = DataFlags_NotEmpty | DataFlags_InlineField);
data_reg_comment_t(g_dataReg, AssetLevelRef, "Asset level reference");

data_reg_struct_t(g_dataReg, GeoColor3);
data_reg_field_t(g_dataReg, GeoColor3, r, data_prim_t(f32), .flags = DataFlags_Opt);
data_reg_field_t(g_dataReg, GeoColor3, g, data_prim_t(f32), .flags = DataFlags_Opt);
Expand Down Expand Up @@ -229,6 +233,7 @@ static void asset_data_init_types(void) {
// clang-format on

g_assetRefType = t_AssetRef;
g_assetLevelRefType = t_AssetLevelRef;
g_assetGeoColor3Type = t_GeoColor3;
g_assetGeoColor4Type = t_GeoColor4;
g_assetGeoColor3NormType = t_GeoColor3Norm;
Expand Down
2 changes: 1 addition & 1 deletion libs/asset/src/data_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void asset_data_init_tex(void);
void asset_data_init_vfx(void);
void asset_data_init_weapon(void);

extern DataType g_assetRefType;
extern DataType g_assetRefType, g_assetLevelRefType;
extern DataType g_assetGeoColor3Type, g_assetGeoColor4Type;
extern DataType g_assetGeoColor3NormType, g_assetGeoColor4NormType;
extern DataType g_assetGeoVec2Type, g_assetGeoVec3Type, g_assetGeoVec4Type;
Expand Down
38 changes: 36 additions & 2 deletions libs/asset/src/loader_level.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include "core_alloc.h"
#include "core_dynstring.h"
#include "core_path.h"
#include "core_search.h"
#include "core_sort.h"
#include "data_read.h"
#include "data_utils.h"
#include "data_write.h"
Expand All @@ -24,6 +26,10 @@ static void ecs_destruct_level_comp(void* data) {
data_destroy(g_dataReg, g_allocHeap, g_assetLevelDefMeta, mem_create(level, sizeof(AssetLevel)));
}

static i8 level_compare_object_id(const void* a, const void* b) {
return compare_u32(field_ptr(a, AssetLevelObject, id), field_ptr(b, AssetLevelObject, id));
}

ecs_view_define(LevelUnloadView) {
ecs_access_with(AssetLevelComp);
ecs_access_without(AssetLoadedComp);
Expand Down Expand Up @@ -65,8 +71,8 @@ void asset_data_init_level(void) {
data_reg_field_t(g_dataReg, AssetLevelObject, id, data_prim_t(u32), .flags = DataFlags_Opt | DataFlags_NotEmpty);
data_reg_field_t(g_dataReg, AssetLevelObject, prefab, data_prim_t(StringHash), .flags = DataFlags_NotEmpty);
data_reg_field_t(g_dataReg, AssetLevelObject, faction, t_AssetLevelFaction, .flags = DataFlags_Opt);
data_reg_field_t(g_dataReg, AssetLevelObject, position, g_assetGeoVec3Type);
data_reg_field_t(g_dataReg, AssetLevelObject, rotation, g_assetGeoQuatType);
data_reg_field_t(g_dataReg, AssetLevelObject, position, g_assetGeoVec3Type, .flags = DataFlags_Opt);
data_reg_field_t(g_dataReg, AssetLevelObject, rotation, g_assetGeoQuatType, .flags = DataFlags_Opt);
data_reg_field_t(g_dataReg, AssetLevelObject, scale, data_prim_t(f32), .flags = DataFlags_Opt | DataFlags_NotEmpty);
data_reg_field_t(g_dataReg, AssetLevelObject, properties, g_assetPropertyType, .container = DataContainer_HeapArray, .flags = DataFlags_Opt);
data_reg_field_t(g_dataReg, AssetLevelObject, sets, data_prim_t(StringHash), .container = DataContainer_InlineArray, .fixedCount = asset_level_sets_max, .flags = DataFlags_Opt);
Expand Down Expand Up @@ -97,6 +103,16 @@ void asset_load_level(
data_read_bin(g_dataReg, src->data, g_allocHeap, g_assetLevelDefMeta, mem_var(lvl), &readRes);
} else {
data_read_json(g_dataReg, src->data, g_allocHeap, g_assetLevelDefMeta, mem_var(lvl), &readRes);

/**
* Ensure the objects are sorted on their id. The editor always produces json files with sorted
* objects but external edits (for example source control merges) can cause non-sorted files.
*/
sort_quicksort_t(
lvl.objects.values,
lvl.objects.values + lvl.objects.count,
AssetLevelObject,
level_compare_object_id);
}
if (UNLIKELY(readRes.error)) {
errMsg = readRes.errorMsg;
Expand Down Expand Up @@ -124,6 +140,23 @@ void asset_load_level(
asset_repo_source_close(src);
}

const AssetLevelObject* asset_level_find(const AssetLevel* lvl, const u32 persistentId) {
return search_binary_t(
lvl->objects.values,
lvl->objects.values + lvl->objects.count,
AssetLevelObject,
level_compare_object_id,
&(AssetLevelObject){.id = persistentId});
}

u32 asset_level_find_index(const AssetLevel* lvl, const u32 persistentId) {
const AssetLevelObject* obj = asset_level_find(lvl, persistentId);
if (!obj) {
return sentinel_u32;
}
return (u32)(obj - lvl->objects.values);
}

bool asset_level_save(AssetManagerComp* manager, const String id, const AssetLevel* level) {
String idWithExtScratch = id;
const String ext = path_extension(id);
Expand All @@ -142,6 +175,7 @@ bool asset_level_save(AssetManagerComp* manager, const String id, const AssetLev
const DataWriteJsonOpts jOpts = data_write_json_opts(.numberMaxDecDigits = 4, .compact = true);
const Mem levelData = mem_create(level, sizeof(AssetLevel));
data_write_json(g_dataReg, &dataBuffer, g_assetLevelDefMeta, levelData, &jOpts);
dynstring_append_char(&dataBuffer, '\n'); // End the file with a new-line.

const bool res = asset_save(manager, idWithExtScratch, dynstring_view(&dataBuffer));

Expand Down
1 change: 1 addition & 0 deletions libs/asset/src/property.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ void asset_data_init_property(void) {
data_reg_choice_t(g_dataReg, AssetProperty, AssetPropertyType_Quat, data_quat, g_assetGeoQuatType);
data_reg_choice_t(g_dataReg, AssetProperty, AssetPropertyType_Color, data_color, g_assetGeoColor4Type);
data_reg_choice_t(g_dataReg, AssetProperty, AssetPropertyType_Str, data_str, data_prim_t(StringHash));
data_reg_choice_t(g_dataReg, AssetProperty, AssetPropertyType_LevelEntity, data_levelEntity, g_assetLevelRefType);
data_reg_choice_t(g_dataReg, AssetProperty, AssetPropertyType_Asset, data_asset, g_assetRefType);
// clang-format on

Expand Down
Loading

0 comments on commit 13d25e7

Please sign in to comment.