Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 107 additions & 59 deletions src/builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,34 @@ void Builder::build_map()
void Builder::build_worldspawn(int idx, LMEntity& ent)
{
// Create node for this entity
auto container_node = memnew(Node3D());
Node3D* container_node;
String path = classname_to_resource_path("worldspawn");

if (path != "") {
// Instantiate the scene with the name "worldspawn" and set its properties,
// as if it were a custom entity
auto resource_loader = ResourceLoader::get_singleton();

Ref<PackedScene> scene = resource_loader->load(path);
if (scene == nullptr) {
UtilityFunctions::printerr("Resource at path '", path, "' could not be loaded as a PackedScene by the resource loader!");
return;
}

Node* instance = scene->instantiate();
if (!instance->is_class("Node3D")) {
UtilityFunctions::printerr("worldspawn.tscn is not a Node3D!");
return;
}

set_custom_entity_properties(ent, instance);

container_node = (Node3D*)instance;
}
else {
container_node = memnew(Node3D());
}

m_loader->add_child(container_node);
container_node->set_owner(m_loader->get_owner());

Expand Down Expand Up @@ -133,10 +160,44 @@ void Builder::build_entity(int idx, LMEntity& ent, const String& classname)
//TODO: More common entities
}

build_entity_custom(idx, ent, m_map->entity_geo[idx], classname);
build_entity_custom(idx, ent, classname);
}

void Builder::build_entity_custom(int idx, LMEntity& ent, LMEntityGeometry& geo, const String& classname)
void Builder::build_entity_custom(int idx, LMEntity& ent, const String& classname)
{
// m_loader->m_entity_path => "res://entities/"
// "info_player_start" => "info/player/start.tscn", "info/player_start.tscn", "info_player_start.tscn"
// "thing" => "thing.tscn"

auto resource_loader = ResourceLoader::get_singleton();

String path = classname_to_resource_path(classname);
if (path == "") {
UtilityFunctions::printerr("Path to entity resource could not be resolved: ", classname);
return;
}

Ref<PackedScene> scene = resource_loader->load(path);
if (scene == nullptr) {
UtilityFunctions::printerr("Resource at path '", path, "' could not be loaded as a PackedScene by the resource loader!");
return;
}

auto instance = scene->instantiate();
m_loader->add_child(instance);
instance->set_owner(m_loader->get_owner());

if (instance->is_class("Node3D")) {
set_entity_node_common((Node3D*)instance, ent);
if (ent.brush_count > 0) {
set_entity_brush_common(idx, (Node3D*)instance, ent);
}
}

set_custom_entity_properties(ent, instance);
}

String Builder::classname_to_resource_path(const String& classname)
{
// m_loader->m_entity_path => "res://entities/"
// "info_player_start" => "info/player/start.tscn", "info/player_start.tscn", "info_player_start.tscn"
Expand All @@ -160,69 +221,56 @@ void Builder::build_entity_custom(int idx, LMEntity& ent, LMEntityGeometry& geo,
path = path + ".tscn";

if (resource_loader->exists(path, "PackedScene")) {
Ref<PackedScene> scene = resource_loader->load(path);
if (scene == nullptr) {
UtilityFunctions::printerr("Resource at path '", path, "' could not be loaded as a PackedScene by the resource loader!");
return;
}
return path;
}
}

auto instance = scene->instantiate();
m_loader->add_child(instance);
instance->set_owner(m_loader->get_owner());
return "";
}

if (instance->is_class("Node3D")) {
set_entity_node_common((Node3D*)instance, ent);
if (ent.brush_count > 0) {
set_entity_brush_common(idx, (Node3D*)instance, ent);
}
void Builder::set_custom_entity_properties(LMEntity& ent, Node* instance)
{
for (int j = 0; j < ent.property_count; j++) {
auto& prop = ent.properties[j];

auto var = instance->get(prop.key);
switch (var.get_type()) {
case Variant::BOOL: instance->set(prop.key, atoi(prop.value) == 1); break;
case Variant::INT: instance->set(prop.key, (int64_t)atoll(prop.value)); break;
case Variant::FLOAT: instance->set(prop.key, atof(prop.value)); break; //TODO: Locale?
case Variant::STRING: instance->set(prop.key, prop.value); break;

case Variant::STRING_NAME: instance->set(prop.key, StringName(prop.value));
case Variant::NODE_PATH: instance->set(prop.key, NodePath(prop.value)); //TODO: More TrenchBroom focused node path conversion?

case Variant::VECTOR2: {
vec2 v = vec2_parse(prop.value);
instance->set(prop.key, Vector2(v.x, v.y));
break;
}
case Variant::VECTOR2I: {
vec2 v = vec2_parse(prop.value);
instance->set(prop.key, Vector2i((int)v.x, (int)v.y));
break;
}
case Variant::VECTOR3: {
vec3 v = vec3_parse(prop.value);
instance->set(prop.key, Vector3(v.x, v.y, v.z));
break;
}
case Variant::VECTOR3I: {
vec3 v = vec3_parse(prop.value);
instance->set(prop.key, Vector3i((int)v.x, (int)v.y, (int)v.z));
break;
}

for (int j = 0; j < ent.property_count; j++) {
auto& prop = ent.properties[j];

auto var = instance->get(prop.key);
switch (var.get_type()) {
case Variant::BOOL: instance->set(prop.key, atoi(prop.value) == 1); break;
case Variant::INT: instance->set(prop.key, (int64_t)atoll(prop.value)); break;
case Variant::FLOAT: instance->set(prop.key, atof(prop.value)); break; //TODO: Locale?
case Variant::STRING: instance->set(prop.key, prop.value); break;

case Variant::STRING_NAME: instance->set(prop.key, StringName(prop.value));
case Variant::NODE_PATH: instance->set(prop.key, NodePath(prop.value)); //TODO: More TrenchBroom focused node path conversion?

case Variant::VECTOR2: {
vec2 v = vec2_parse(prop.value);
instance->set(prop.key, Vector2(v.x, v.y));
break;
}
case Variant::VECTOR2I: {
vec2 v = vec2_parse(prop.value);
instance->set(prop.key, Vector2i((int)v.x, (int)v.y));
break;
}
case Variant::VECTOR3: {
vec3 v = vec3_parse(prop.value);
instance->set(prop.key, Vector3(v.x, v.y, v.z));
break;
}
case Variant::VECTOR3I: {
vec3 v = vec3_parse(prop.value);
instance->set(prop.key, Vector3i((int)v.x, (int)v.y, (int)v.z));
break;
}

case Variant::COLOR: {
vec3 v = vec3_parse(prop.value);
instance->set(prop.key, Color(v.x / 255.0f, v.y / 255.0f, v.z / 255.0f));
break;
}
}
case Variant::COLOR: {
vec3 v = vec3_parse(prop.value);
instance->set(prop.key, Color(v.x / 255.0f, v.y / 255.0f, v.z / 255.0f));
break;
}
return;
}
}

UtilityFunctions::printerr("Path to entity resource could not be resolved: ", classname);
}

void Builder::build_entity_light(int idx, LMEntity& ent)
Expand Down
6 changes: 5 additions & 1 deletion src/builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Builder
void build_brush(int idx, Node3D* node, LMEntity& ent);

void build_entity(int idx, LMEntity& ent, const String& classname);
void build_entity_custom(int idx, LMEntity& ent, LMEntityGeometry& geo, const String& classname);
void build_entity_custom(int idx, LMEntity& ent, const String& classname);
void build_entity_light(int idx, LMEntity& ent);
void build_entity_area(int idx, LMEntity& ent);

Expand All @@ -77,4 +77,8 @@ class Builder
String material_path(const char* name);
Ref<Texture2D> texture_from_name(const char* name);
Ref<Material> material_from_name(const char* name);

private:
String classname_to_resource_path(const String& classname);
void set_custom_entity_properties(LMEntity& ent, Node* instance);
};