Added C# Point entities + automatic export of FGD entities

This commit is contained in:
Antoine Pilote
2024-09-01 19:37:05 -04:00
parent f59a7e931f
commit 63712edc6d
21 changed files with 534 additions and 479 deletions

View File

@@ -118,7 +118,7 @@ namespace Nuake {
using namespace Nuake::StaticResources;
ImGui::LoadIniSettingsFromMemory((const char*)StaticResources::Resources_default_layout_ini);
ScriptingContext::Get().Initialize();
//ScriptingContext::Get().Initialize();
}
void EditorInterface::Init()
@@ -511,6 +511,7 @@ namespace Nuake {
{
playButtonPressed = ImGui::Button(ICON_FA_PLAY, ImVec2(30, 30)) || Input::IsKeyPressed(Key::F5);
tooltip = "Build & Play (F5)";
}
if (playButtonPressed)
@@ -527,6 +528,7 @@ namespace Nuake {
{
this->SceneSnapshot = Engine::GetCurrentScene()->Copy();
std::string statusMessage = ICON_FA_HAMMER + std::string(" Building .Net solution...");
SetStatusMessage(statusMessage);
@@ -553,6 +555,8 @@ namespace Nuake {
}
else
{
Engine::GetProject()->ExportEntitiesToTrenchbroom();
SetStatusMessage("Entering play mode...");
PushCommand(SetGameState(GameState::Playing));

View File

@@ -169,6 +169,7 @@ namespace Nuake
}
FileSystem::SetRootDirectory(FileSystem::GetParentPath(project->FullPath));
ScriptingEngineNet::Get().Initialize();
ScriptingEngineNet::Get().LoadProjectAssembly(project);
return true;

View File

@@ -153,7 +153,7 @@ namespace Nuake {
void AudioManager::LoadWavAudio(const std::string& filePath)
{
const bool STREAMING = true;
const bool STREAMING = false;
if (STREAMING)
{
Ref<SoLoud::WavStream> wavStream = CreateRef<SoLoud::WavStream>();

View File

@@ -494,8 +494,20 @@ namespace Nuake
{
std::string name = entity.GetComponent<NameComponent>().Name;
_JoltBodyInterface->SetPositionAndRotation(bodyId, newPosition, newRotation, JPH::EActivation::DontActivate);
//_JoltBodyInterface->MoveKinematic(bodyId, newPosition, newRotation, 0.0f);
JPH::EMotionType bodyType = _JoltBodyInterface->GetMotionType(bodyId);
switch (bodyType)
{
case JPH::EMotionType::Kinematic:
{
_JoltBodyInterface->MoveKinematic(bodyId, newPosition, newRotation, 0.0f);
break;
}
case JPH::EMotionType::Static:
{
_JoltBodyInterface->SetPositionAndRotation(bodyId, newPosition, newRotation, JPH::EActivation::DontActivate);
break;
}
}
}
}
}
@@ -864,7 +876,7 @@ namespace Nuake
_CollisionCallbacks.push_back(std::move(data));
}
const std::vector<CollisionData>& DynamicWorld::GetCollisionsData()
const std::vector<CollisionData> DynamicWorld::GetCollisionsData()
{
std::scoped_lock<std::mutex> lock(_CollisionCallbackMutex);
return _CollisionCallbacks;

View File

@@ -91,7 +91,7 @@ namespace Nuake
void ClearCollisionData();
void RegisterCollisionCallback(const CollisionData& data);
const std::vector<CollisionData>& GetCollisionsData();
const std::vector<CollisionData> GetCollisionsData();
private:
JPH::Ref<JPH::Shape> GetJoltShape(const Ref<PhysicShape> shape);
void SyncEntitiesTranforms();

View File

@@ -57,7 +57,7 @@ namespace Nuake
return m_World->CastShape(from, to, shape);
}
const std::vector<Physics::CollisionData>& PhysicsManager::GetCollisions()
const std::vector<Physics::CollisionData> PhysicsManager::GetCollisions()
{
return m_World->GetCollisionsData();
}

View File

@@ -53,7 +53,7 @@ namespace Nuake
std::vector<RaycastResult> Raycast(const Vector3& from, const Vector3& to);
std::vector<ShapeCastResult> Shapecast(const Vector3& from, const Vector3& to, const Ref<Physics::PhysicShape>& shape);
const std::vector<Physics::CollisionData>& GetCollisions();
const std::vector<Physics::CollisionData> GetCollisions();
void RegisterBody(Ref<Physics::RigidBody> rb);
void RegisterGhostBody(Ref<GhostObject> rb);

View File

@@ -20,6 +20,7 @@ namespace Nuake
Quat _rotation;
Entity _entity;
bool _ForceKinematic = false;
bool _isTrigger = false;
bool m_LockXAxis = false;
bool m_LockYAxis = false;
@@ -29,7 +30,7 @@ namespace Nuake
RigidBody();
RigidBody(Vector3 position, Entity handle);
RigidBody(float mass, Vector3 position, Quat rotation, Matrix4 transform, Ref<PhysicShape> shape, Entity entity, Vector3 initialVel = Vector3(0, 0, 0));
RigidBody(float mass, Vector3 position, Quat rotation, Matrix4 transform, Ref<PhysicShape> shape, Entity entity, Vector3 initialVel = Vector3(0, 0, 0), bool forceKinematic = false);
void UpdateTransform();
@@ -43,6 +44,7 @@ namespace Nuake
void SetLockXAxis(bool lock) { m_LockXAxis = lock; }
void SetLockYAxis(bool lock) { m_LockYAxis = lock; }
void SetLockZAxis(bool lock) { m_LockZAxis = lock; }
bool GetForceKinematic() const { return _ForceKinematic; }
void SetEntityID(Entity ent);
Vector3 GetPosition() const { return _position; }

View File

@@ -20,12 +20,13 @@ namespace Nuake
}
RigidBody::RigidBody(float mass, Vector3 position, Quat rotation, Matrix4 transform, Ref<PhysicShape> shape, Entity entity, Vector3 initialVel) :
RigidBody::RigidBody(float mass, Vector3 position, Quat rotation, Matrix4 transform, Ref<PhysicShape> shape, Entity entity, Vector3 initialVel, bool forceKinematic) :
_position(position),
_collisionShape(shape),
_mass(mass),
_entity(entity),
_rotation(rotation)
_rotation(rotation),
_ForceKinematic(forceKinematic)
{
}

View File

@@ -75,6 +75,9 @@ namespace Nuake
mCapsuleGizmo = CreateRef<CapsuleGizmo>();
mCapsuleGizmo->CreateMesh();
mDebugLines = std::vector<DebugLine>();
mDebugShapes = std::vector<DebugShape>();
}
void SceneRenderer::Cleanup()
@@ -84,24 +87,30 @@ namespace Nuake
void SceneRenderer::Update(const Timestep time, bool isEditorUpdate)
{
// Delete debug shapes that are dead
std::erase_if(mDebugLines, [](const DebugLine& line)
if (mDebugLines.size() > 0)
{
return line.Life < 0.0f;
});
std::erase_if(mDebugLines, [](const DebugLine& line)
{
return line.Life < 0.0f;
});
std::erase_if(mDebugShapes, [](const DebugShape& shape)
{
return shape.Life < 0.0f;
});
for (auto& line : mDebugLines)
{
line.Life -= time;
for (auto& line : mDebugLines)
{
line.Life -= time;
}
}
for (auto& shape : mDebugShapes)
if (mDebugShapes.size() > 0)
{
shape.Life -= time;
std::erase_if(mDebugShapes, [](const DebugShape& shape)
{
return shape.Life < 0.0f;
});
for (auto& shape : mDebugShapes)
{
shape.Life -= time;
}
}
}
@@ -527,10 +536,7 @@ namespace Nuake
if (model.IsTransparent || !visibility.Visible)
continue;
for (Ref<Mesh>& m : model.Meshes)
{
Renderer::SubmitMesh(m, transform.GetGlobalTransform());
}
}
Renderer::Flush(shader, true);
@@ -620,10 +626,7 @@ namespace Nuake
if (model.IsTransparent || !visibility.Visible)
continue;
for (Ref<Mesh>& m : model.Meshes)
{
Renderer::SubmitMesh(m, transform.GetGlobalTransform());
}
}
Renderer::Flush(shader, true);
@@ -795,10 +798,7 @@ namespace Nuake
if (model.IsTransparent || !visibility.Visible)
continue;
for (auto& b : model.Meshes)
{
Renderer::SubmitMesh(b, transform.GetGlobalTransform(), (uint32_t)e);
}
}
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);

View File

@@ -76,6 +76,7 @@ namespace Nuake {
std::string Name;
std::string Description;
std::string Prefab;
std::string Script;
std::vector<ClassProperty> Properties;
FGDBaseEntity BaseClass;

View File

@@ -1,11 +1,15 @@
#include "Project.h"
#include "../Core/FileSystem.h"
#include "src/Core/FileSystem.h"
#include "Engine.h"
#include "src/Core/Logger.h"
#include "src/Audio/AudioManager.h"
#include "src/Scripting/ScriptingEngineNet.h"
#include <json/json.hpp>
#include <fstream>
#include <streambuf>
#include "../Core/Logger.h"
#include "Engine.h"
#include <src/Audio/AudioManager.h>
namespace Nuake
{
@@ -104,6 +108,73 @@ namespace Nuake
return project;
}
void Project::ExportEntitiesToTrenchbroom()
{
Ref<FGDFile> file = EntityDefinitionsFile;
file->BrushEntities.clear();
for (auto& [name, type] : ScriptingEngineNet::Get().GetBrushEntities())
{
FGDBrushEntity brushEntity = FGDBrushEntity(name);
brushEntity.Script = name;
brushEntity.Description = type.Description;
brushEntity.IsTrigger = type.isTrigger;
for (auto& t : type.exposedVars)
{
ClassProperty classProp;
classProp.name = t.Name;
classProp.type = ClassPropertyType::String;
if (t.Type == ExposedVarTypes::String && t.Value.has_value())
{
classProp.value = std::any_cast<std::string>(t.Value);
}
else if (t.Type == ExposedVarTypes::Int)
{
classProp.value = std::to_string(std::any_cast<int>(t.Value));
}
else
{
classProp.value = "";
}
brushEntity.Properties.push_back(classProp);
}
file->BrushEntities.push_back(brushEntity);
}
file->PointEntities.clear();
for (auto& [name, type] : ScriptingEngineNet::Get().GetPointEntities())
{
FGDPointEntity pointEntity = FGDPointEntity(name);
pointEntity.Script = name;
pointEntity.Description = type.Description;
for (auto& t : type.exposedVars)
{
ClassProperty classProp;
classProp.name = t.Name;
classProp.type = ClassPropertyType::String;
if (t.Type == ExposedVarTypes::String && t.Value.has_value())
{
classProp.value = std::any_cast<std::string>(t.Value);
}
else if (t.Type == ExposedVarTypes::Int)
{
classProp.value = std::to_string(std::any_cast<int>(t.Value));
}
else
{
classProp.value = "";
}
pointEntity.Properties.push_back(classProp);
}
file->PointEntities.push_back(pointEntity);
}
file->Export();
}
json Project::Serialize()
{
BEGIN_SERIALIZE();

View File

@@ -59,6 +59,8 @@ namespace Nuake
static Ref<Project> New();
static Ref<Project> Load(std::string& path);
void ExportEntitiesToTrenchbroom();
json Serialize() override;
bool Deserialize(const json& j) override;
};

View File

@@ -10,7 +10,7 @@ namespace Nuake {
class BSPBrushComponent
{
public:
std::vector<Ref<Mesh>> Meshes;
//std::vector<Ref<Mesh>> Meshes;
std::vector<std::vector<Vector3>> Hulls;
std::vector<Ref<Material>> Materials;
@@ -27,7 +27,7 @@ namespace Nuake {
BSPBrushComponent()
{
Meshes = std::vector<Ref<Mesh>>();
//Meshes = std::vector<Ref<Mesh>>();
Materials = std::vector<Ref<Material>>();
Rigidbody = std::vector<Ref<Physics::RigidBody>>();
Hulls = std::vector<std::vector<Vector3>>();

View File

@@ -131,8 +131,7 @@ namespace Nuake
const Quat& startRotation = transformComponent.GetGlobalRotation();
const auto collisionShape = CreateRef<Physics::ConvexHullShape>(hull);
auto rigidBody = CreateRef<Physics::RigidBody>(0.0f, startPosition, startRotation, startTransform, collisionShape, entity);
auto rigidBody = CreateRef<Physics::RigidBody>(0.0f, startPosition, startRotation, startTransform, collisionShape, entity, Vector3{ 0, 0, 0 }, brushComponent.IsFunc);
brushComponent.Rigidbody.push_back(rigidBody);
rigidBody->SetIsTrigger(brushComponent.IsTrigger);
PhysicsManager::Get().RegisterBody(rigidBody);

View File

@@ -40,392 +40,6 @@ namespace Nuake {
Ref<Material> DefaultMaterial;
void QuakeMapBuilder::CreateTrigger(brush* brush, brush_geometry* brush_inst,
Scene* scene, Entity& parent,
const std::string& target, const std::string& targetname)
{
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
Entity brushEntity = scene->CreateEntity("Trigger");
TransformComponent& transformComponent = brushEntity.GetComponent<TransformComponent>();
TriggerZone& trigger = brushEntity.AddComponent<TriggerZone>();
trigger.target = target;
parent.AddChild(brushEntity);
transformComponent.SetGlobalPosition(Vector3(brush->center.y * (1.0f / 64),
brush->center.z * ScaleFactor * (1.0f / 64),
brush->center.x * ScaleFactor * (1.0f / 64)));
BSPBrushComponent& bsp = brushEntity.AddComponent<BSPBrushComponent>();
bsp.IsSolid = false;
bsp.target = target;
int indexOffset = 0;
for (int f = 0; f < brush->face_count; ++f)
{
face* face = &brush->faces[f];
face_geometry* face_geo_inst = &brush_inst->faces[f];
for (int i = 0; i < face_geo_inst->vertex_count; ++i)
{
face_vertex vertex = face_geo_inst->vertices[i];
Vector3 vertexPos = Vector3(
(vertex.vertex.y - brush->center.y) * ScaleFactor * (1.0f / 64),
(vertex.vertex.z - brush->center.z) * ScaleFactor * (1.0f / 64),
(vertex.vertex.x - brush->center.x) * ScaleFactor * (1.0f / 64)
);
Vector3 vertexNormal = Vector3(vertex.normal.y, vertex.normal.z, vertex.normal.x);
Vector3 vertexTangent = Vector3(vertex.tangent.y, vertex.tangent.z, vertex.tangent.x);
vertices.push_back(Vertex{
vertexPos,
Vector2(0,0),
vertexNormal,
vertexTangent,
glm::vec3(0.0, 1.0, 0.0)
});
}
for (int i = 0; i < (face_geo_inst->vertex_count - 2) * 3; ++i)
{
unsigned int index = face_geo_inst->indices[i];
indices.push_back((unsigned int)indexOffset + index);
}
indexOffset += face_geo_inst->vertex_count;
}
Ref<Mesh> mesh = CreateRef<Mesh>();
mesh->AddSurface(vertices, indices);
bsp.Meshes.push_back(mesh);
}
void QuakeMapBuilder::CreateBrush(brush* brush, brush_geometry* brush_inst,
Scene* scene, Entity& parent,
const std::string& target, const std::string& targetname)
{
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
Entity brushEntity = scene->CreateEntity(targetname);
TransformComponent& transformComponent = brushEntity.GetComponent<TransformComponent>();
BSPBrushComponent& bsp = brushEntity.AddComponent<BSPBrushComponent>();
parent.AddChild(brushEntity);
transformComponent.SetGlobalPosition(Vector3(
brush->center.y * ScaleFactor * (1.0f / 64),
brush->center.z * ScaleFactor * (1.0f / 64),
brush->center.x * ScaleFactor * (1.0f / 64)));
int index_offset = 0;
int lastTextureID = -1;
std::string lastTexturePath = "";
for (int f = 0; f < brush->face_count; ++f)
{
face* face = &brush->faces[f];
texture_data* texture = &textures[face->texture_idx];
bool textureIsEmpty = std::string(texture->name) == "__TB_empty" || std::string(texture->name).empty();
if (textureIsEmpty)
{
texture->height = 1;
texture->width = 1;
continue;
}
else
{
if (FileSystem::FileExists("textures/" + std::string(texture->name) + ".material"))
{
}
else
{
std::string path = FileSystem::Root + "textures/" + std::string(texture->name) + ".png";
auto tex = TextureManager::Get()->GetTexture(path);
texture->height = tex->GetHeight();
texture->width = tex->GetWidth();
}
}
face_geometry* face_geo_inst = &brush_inst->faces[f];
for (int i = 0; i < face_geo_inst->vertex_count; ++i)
{
face_vertex vertex = face_geo_inst->vertices[i];
vertex_uv vertex_uv = get_valve_uv(vertex.vertex, face, texture->width, texture->height);
Vector3 vertexPos = Vector3(
(vertex.vertex.y - brush->center.y) * ScaleFactor * (1.0f / 64),
(vertex.vertex.z - brush->center.z) * ScaleFactor * (1.0f / 64),
(vertex.vertex.x - brush->center.x) * ScaleFactor * (1.0f / 64)
);
Vector2 vertexUV = Vector2(vertex_uv.u, vertex_uv.v);
Vector3 vertexNormal = Vector3(vertex.normal.y, vertex.normal.z, vertex.normal.x);
Vector3 vertexTangent = Vector3(vertex.tangent.y, vertex.tangent.z, vertex.tangent.x);
vertices.push_back(Vertex{
vertexPos,
vertexUV,
vertexNormal,
vertexTangent,
glm::vec3(0.0, 1.0, 0.0)
});
}
for (int i = 0; i < (face_geo_inst->vertex_count - 2) * 3; ++i)
{
unsigned int index = face_geo_inst->indices[i];
indices.push_back(index_offset + (unsigned int)index);
}
if (lastTextureID != face->texture_idx)
{
lastTexturePath = FileSystem::Root + "textures/" + std::string(texture->name) + ".png";
Ref<Mesh> mesh = CreateRef<Mesh>();
mesh->AddSurface(vertices, indices);
if (const std::string materialPath = "textures/" + std::string(texture->name) + ".material";
FileSystem::FileExists(materialPath))
{
Ref<Material> material = ResourceLoader::LoadMaterial(materialPath);
mesh->SetMaterial(material);
}
else if (std::string(texture->name) != "__TB_empty")
{
Ref<Material> material = MaterialManager::Get()->GetMaterial(lastTexturePath);
mesh->SetMaterial(material);
}
bsp.Meshes.push_back(mesh);
index_offset = 0;
vertices.clear();
indices.clear();
lastTextureID = face->texture_idx;
}
else
{
index_offset += (face_geo_inst->vertex_count);
}
}
if (vertices.size() > 0)
{
Ref<Mesh> mesh = CreateRef<Mesh>();
mesh->AddSurface(vertices, indices);
Ref<Material> material = MaterialManager::Get()->GetMaterial(lastTexturePath);
mesh->SetMaterial(material);
bsp.Meshes.push_back(mesh);
}
}
void QuakeMapBuilder::CreateFuncBrush(brush* brush, brush_geometry* brush_inst,
Scene* scene, Entity& parent,
const std::string& target, const std::string& targetname, FGDBrushEntity fgdBrush, std::map<std::string, std::string> props)
{
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
std::string name = fgdBrush.Name;
if (targetname != "")
name = targetname;
Entity brushEntity = scene->CreateEntity(name);
TransformComponent& transformComponent = brushEntity.GetComponent<TransformComponent>();
BSPBrushComponent& bsp = brushEntity.AddComponent<BSPBrushComponent>();
std::map<std::string, Ref<Material>> m_Materials;
bsp.IsSolid = !fgdBrush.IsTrigger;
bsp.IsTransparent = !fgdBrush.Visible;
bsp.IsFunc = true;
bsp.IsTrigger = fgdBrush.IsTrigger;
if (fgdBrush.Script != "")
{
NetScriptComponent& netScript = brushEntity.AddComponent<NetScriptComponent>();
netScript.ScriptPath = fgdBrush.Script;
ScriptingEngineNet::Get().UpdateEntityWithExposedVar(brushEntity);
for (auto& ev : netScript.ExposedVar)
{
if (props.find(ev.Name) != props.end())
{
if (ev.Type == NetScriptExposedVarType::String)
{
ev.Value = props[ev.Name];
ev.DefaultValue = ev.Value;
}
}
}
}
bsp.target = target;
bsp.TargetName = targetname;
parent.AddChild(brushEntity);
transformComponent.SetLocalPosition(Vector3(
brush->center.y * ScaleFactor * (1.0f / 64),
brush->center.z * ScaleFactor * (1.0f / 64),
brush->center.x * ScaleFactor * (1.0f / 64)));
int index_offset = 0;
int lastTextureID = -1;
std::string lastTexturePath = "";
std::vector<Vector3> pointsInBrush;
for (int f = 0; f < brush->face_count; ++f)
{
face* face = &brush->faces[f];
texture_data* texture = &textures[face->texture_idx];
if (std::string(texture->name) == "__TB_empty")
{
texture->height = 1;
texture->width = 1;
}
else
{
std::string path = FileSystem::Root + std::string(texture->name) + ".png";
auto tex = TextureManager::Get()->GetTexture(path);
texture->height = tex->GetHeight();
texture->width = tex->GetWidth();
}
Ref<Material> currentMaterial;
if (std::string(texture->name) != "__TB_empty")
{
std::string path = FileSystem::Root + "Textures/" + std::string(texture->name) + ".png";
if (const std::string materialPath = "Materials/" + std::string(texture->name) + ".material";
FileSystem::FileExists(materialPath))
{
Ref<Material> material = ResourceLoader::LoadMaterial(materialPath);
m_Materials[path] = material;
}
else
{
if (m_Materials.find(path) == m_Materials.end())
{
m_Materials[path] = MaterialManager::Get()->GetMaterial(path);
}
}
currentMaterial = m_Materials[path];
texture->height = currentMaterial->m_Albedo->GetHeight();
texture->width = currentMaterial->m_Albedo->GetWidth();
}
else
{
continue;
currentMaterial = MaterialManager::Get()->GetMaterial("resources/Textures/default/Default.png");
texture->height = texture->width = 1;
}
face_geometry* face_geo_inst = &brush_inst->faces[f];
for (int i = 0; i < face_geo_inst->vertex_count; ++i)
{
face_vertex vertex = face_geo_inst->vertices[i];
vertex_uv vertex_uv = get_valve_uv(vertex.vertex, face, texture->width, texture->height);
Vector3 vertexPos = Vector3(
(vertex.vertex.y - brush->center.y) * ScaleFactor * (1.0f / 64),
(vertex.vertex.z - brush->center.z) * ScaleFactor * (1.0f / 64),
(vertex.vertex.x - brush->center.x) * ScaleFactor * (1.0f / 64)
);
pointsInBrush.push_back(vertexPos);
Vector2 vertexUV = Vector2(vertex_uv.u, vertex_uv.v);
Vector3 vertexNormal = Vector3(vertex.normal.y, vertex.normal.z, vertex.normal.x);
Vector3 vertexTangent = Vector3(vertex.tangent.y, vertex.tangent.z, vertex.tangent.x);
vertices.push_back(Vertex
{
vertexPos,
vertexUV,
vertexNormal,
vertexTangent,
glm::vec3(0.0, 1.0, 0.0)
}
);
}
for (int i = 0; i < (face_geo_inst->vertex_count - 2) * 3; ++i)
{
unsigned int index = face_geo_inst->indices[i];
indices.push_back(index_offset + (unsigned int)index);
}
if (!bsp.IsTrigger)
{
if (lastTextureID != face->texture_idx)
{
lastTexturePath = FileSystem::Root + "textures/" + std::string(texture->name) + ".png";
Ref<Mesh> mesh = CreateRef<Mesh>();
mesh->AddSurface(vertices, indices);
if (std::string(texture->name) != "__TB_empty")
{
mesh->SetMaterial(currentMaterial);
}
bsp.Meshes.push_back(mesh);
index_offset = 0;
vertices.clear();
indices.clear();
lastTextureID = face->texture_idx;
}
else
{
index_offset += (face_geo_inst->vertex_count);
}
}
else
{
index_offset += (face_geo_inst->vertex_count);
}
}
bsp.Hulls.push_back(std::move(pointsInBrush));
if (bsp.IsTrigger)
{
Ref<Mesh> mesh = CreateRef<Mesh>();
mesh->AddSurface(vertices, indices);
bsp.Meshes.push_back(mesh);
vertices.clear();
}
if (vertices.size() > 0)
{
Ref<Mesh> mesh = CreateRef<Mesh>();
mesh->AddSurface(vertices, indices);
Ref<Material> material = MaterialManager::Get()->GetMaterial(lastTexturePath);
mesh->SetMaterial(material);
bsp.Meshes.push_back(mesh);
ModelComponent& modelComponent = brushEntity.AddComponent<ModelComponent>();
Ref<Model> model = CreateRef<Model>();
model->AddMesh(mesh);
modelComponent.ModelResource = model;
}
}
void QuakeMapBuilder::BuildQuakeMap(Entity& ent, bool Collisions)
{
if (!ent.HasComponent<QuakeMapComponent>())
@@ -454,6 +68,7 @@ namespace Nuake {
DefaultMaterial = MaterialManager::Get()->GetMaterial("default");
Entity worldspawnEntity = { (entt::entity)-1, m_Scene };
Entity currentNonWorldEntity;
std::map<std::string, Entity> pointEntities = std::map<std::string, Entity>();
for (uint32_t e = 0; e < (uint32_t)entity_count; ++e)
{
@@ -537,41 +152,248 @@ namespace Nuake {
if (!isWorldSpawn)
{
currentNonWorldEntity = m_Scene->CreateEntity(properties["classname"]);
ent.AddChild(currentNonWorldEntity);
auto& transformComponent = currentNonWorldEntity.GetComponent<TransformComponent>();
transformComponent.SetLocalPosition({entity_inst->center.y / 64.0f, entity_inst->center.z / 64.0f, entity_inst->center.x / 64.0f });
auto& bsp = currentNonWorldEntity.AddComponent<BSPBrushComponent>();
if (isPointEntity)
{
if (pointEntities.find(pointEntity->Name) == pointEntities.end())
std::string name = pointEntity->Name;
if (targetname != "")
{
Entity pointEntityParent = Engine::GetCurrentScene()->CreateEntity(pointEntity->Name);
pointEntities[pointEntity->Name] = pointEntityParent;
ent.AddChild(pointEntityParent);
name = targetname;
}
Entity newPrefab = Engine::GetCurrentScene()->CreateEntity(pointEntity->Name);
bsp.target = target;
bsp.TargetName = targetname;
pointEntities[pointEntity->Name].AddChild(newPrefab);
auto& prefabComponent = newPrefab.AddComponent<PrefabComponent>();
prefabComponent.SetPrefab(Prefab::New(pointEntity->Prefab));
newPrefab.GetComponent<TransformComponent>().SetLocalPosition(brushLocalPosition);
for (auto& e : prefabComponent.PrefabInstance->Entities)
if (properties.find("origin") != properties.end())
{
if (!e.GetComponent<ParentComponent>().HasParent)
auto splits = String::Split(properties["origin"], ' ');
if (splits.size() == 3)
{
newPrefab.AddChild(e);
int x = atoi(splits[0].c_str());
int y = atoi(splits[1].c_str());
int z = atoi(splits[2].c_str());
Vector3 pointPosition = { y / 64.0f, z / 64.0f, x / 64.0f };
transformComponent.SetLocalPosition(pointPosition);
}
}
if (pointEntity->Script != "")
{
NetScriptComponent& netScript = currentNonWorldEntity.AddComponent<NetScriptComponent>();
netScript.ScriptPath = pointEntity->Script;
ScriptingEngineNet::Get().UpdateEntityWithExposedVar(currentNonWorldEntity);
for (auto& ev : netScript.ExposedVar)
{
if (properties.find(ev.Name) != properties.end())
{
if (ev.Type == NetScriptExposedVarType::String)
{
ev.Value = properties[ev.Name];
ev.DefaultValue = ev.Value;
}
}
}
}
else
{
Entity newPrefab = Engine::GetCurrentScene()->CreateEntity(pointEntity->Name);
pointEntities[pointEntity->Name].AddChild(newPrefab);
auto& prefabComponent = newPrefab.AddComponent<PrefabComponent>();
prefabComponent.SetPrefab(Prefab::New(pointEntity->Prefab));
newPrefab.GetComponent<TransformComponent>().SetLocalPosition(brushLocalPosition);
for (auto& e : prefabComponent.PrefabInstance->Entities)
{
if (!e.GetComponent<ParentComponent>().HasParent)
{
newPrefab.AddChild(e);
}
}
}
}
for (int b = 0; b < entity_inst->brush_count; ++b)
else
{
brush* brush_inst = &entity_inst->brushes[b];
brush_geometry* brush_geo_inst = &entity_geo_inst->brushes[b];
std::string name = fgdBrush->Name;
if (targetname != "")
{
name = targetname;
}
if (isEntity)
CreateFuncBrush(brush_inst, brush_geo_inst, m_Scene, worldspawnEntity, target, targetname, *fgdBrush, properties);
//else
// CreateBrush(brush_inst, brush_geo_inst, m_Scene, newEntity, target, targetname);
bsp.IsSolid = !fgdBrush->IsTrigger;
bsp.IsTransparent = !fgdBrush->Visible;
bsp.IsFunc = true;
bsp.IsTrigger = fgdBrush->IsTrigger;
bsp.target = target;
bsp.TargetName = targetname;
if (fgdBrush->Script != "")
{
NetScriptComponent& netScript = currentNonWorldEntity.AddComponent<NetScriptComponent>();
netScript.ScriptPath = fgdBrush->Script;
ScriptingEngineNet::Get().UpdateEntityWithExposedVar(currentNonWorldEntity);
for (auto& ev : netScript.ExposedVar)
{
if (properties.find(ev.Name) != properties.end())
{
if (ev.Type == NetScriptExposedVarType::String)
{
ev.Value = properties[ev.Name];
ev.DefaultValue = ev.Value;
}
}
}
}
std::map<std::string, Ref<Material>> m_Materials;
std::map<Ref<Material>, std::vector<ProcessedMesh>> m_StaticWorld;
for (int b = 0; b < entity_inst->brush_count; ++b)
{
brush* brush_inst = &entity_inst->brushes[b];
brush_geometry* brush_geo_inst = &entity_geo_inst->brushes[b];
std::vector<Vector3> pointsInBrush;
if (isEntity)
{
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
int index_offset = 0;
int lastTextureID = -1;
std::string lastTexturePath = "";
for (int f = 0; f < brush_inst->face_count; ++f)
{
face* face = &brush_inst->faces[f];
texture_data* texture = &textures[face->texture_idx];
Ref<Material> currentMaterial;
if (std::string(texture->name) != "__TB_empty")
{
std::string path = FileSystem::Root + "Textures/" + std::string(texture->name) + ".png";
if (const std::string materialPath = "Materials/" + std::string(texture->name) + ".material";
FileSystem::FileExists(materialPath))
{
Ref<Material> material = ResourceLoader::LoadMaterial(materialPath);
m_Materials[path] = material;
}
else
{
if (m_Materials.find(path) == m_Materials.end())
{
m_Materials[path] = MaterialManager::Get()->GetMaterial(path);
}
}
currentMaterial = m_Materials[path];
texture->height = currentMaterial->m_Albedo->GetHeight();
texture->width = currentMaterial->m_Albedo->GetWidth();
}
else
{
continue;
currentMaterial = MaterialManager::Get()->GetMaterial("resources/Textures/default/Default.png");
texture->height = texture->width = 1;
}
face_geometry* face_geo_inst = &brush_geo_inst->faces[f];
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;
for (int i = 0; i < face_geo_inst->vertex_count; ++i)
{
face_vertex vertex = face_geo_inst->vertices[i];
vertex_uv vertex_uv;
if (face->is_valve_uv)
vertex_uv = get_valve_uv(vertex.vertex, face, texture->width, texture->height);
else
vertex_uv = get_standard_uv(vertex.vertex, face, texture->width, texture->height);
Vector3 vertexPos = Vector3(
(vertex.vertex.y - entity_inst->center.y) * quakeMapC.ScaleFactor,
(vertex.vertex.z - entity_inst->center.z) * quakeMapC.ScaleFactor,
(vertex.vertex.x - entity_inst->center.x) * quakeMapC.ScaleFactor
);
// We need to push the hull points because the batching
// will create 1 model per material, this prevents us from
// having 1 collision shape per brush(convex).
pointsInBrush.push_back(vertexPos * (1.0f / 64.0f));
Vector2 vertexUV = Vector2(vertex_uv.u, 1.0 - vertex_uv.v);
Vector3 vertexNormal = Vector3(vertex.normal.y, vertex.normal.z, vertex.normal.x);
Vector3 vertexTangent = Vector3(vertex.tangent.y, vertex.tangent.z, vertex.tangent.x);
Vector3 vertexBitangent = glm::cross(vertexNormal, vertexTangent) * (float)vertex.tangent.w;
vertices.push_back(Vertex{
vertexPos * (1.0f / 64.0f),
vertexUV,
vertexNormal,
vertexTangent,
vertexBitangent
});
}
for (int i = 0; i < (face_geo_inst->vertex_count - 2) * 3; i += 3)
{
uint32_t i1 = face_geo_inst->indices[i];
uint32_t i2 = face_geo_inst->indices[i + 1];
uint32_t i3 = face_geo_inst->indices[i + 2];
indices.push_back(i3);
indices.push_back(i2);
indices.push_back(i1);
}
m_StaticWorld[currentMaterial].push_back({ vertices, indices });
vertices.clear();
indices.clear();
}
bsp.Hulls.push_back(std::move(pointsInBrush));
}
//else
// CreateBrush(brush_inst, brush_geo_inst, m_Scene, newEntity, target, targetname);
}
// Batching process
Ref<Model> model = CreateRef<Model>();
for (auto& mat : m_StaticWorld)
{
std::vector<Vertex> batchedVertices;
std::vector<uint32_t> batchedIndices;
uint32_t indexOffset = 0;
for (auto& pm : mat.second)
{
for (auto& vert : pm.Vertices)
batchedVertices.push_back(vert);
for (auto& index : pm.Indices)
batchedIndices.push_back(indexOffset + index);
indexOffset += static_cast<uint32_t>(pm.Vertices.size());
}
Ref<Mesh> mesh = CreateRef<Mesh>();
mesh->AddSurface(batchedVertices, batchedIndices);
mesh->SetMaterial(mat.first);
model->AddMesh(mesh);
}
if (!bsp.IsTrigger)
{
ModelComponent& modelComponent = currentNonWorldEntity.AddComponent<ModelComponent>();
modelComponent.ModelResource = model;
}
}
}
else

View File

@@ -234,6 +234,7 @@ namespace Nuake {
for (auto e : brushView)
{
BSPBrushComponent& brushComponent = brushView.get<BSPBrushComponent>(e);
auto targets = brushComponent.Targets;
if (brushComponent.TargetName == target)
{
Entity entity = { (entt::entity)(e), scene.get() };
@@ -345,6 +346,29 @@ namespace Nuake {
}
}
float CameraGetFOV(int entityId)
{
Entity entity = { (entt::entity)(entityId), Engine::GetCurrentScene().get() };
if (entity.IsValid() && entity.HasComponent<CameraComponent>())
{
auto& component = entity.GetComponent<CameraComponent>();
return component.CameraInstance->Fov;
}
}
void CameraSetFOV(int entityId, float fov)
{
float safeFov = glm::clamp(fov, 1.0f, 180.0f);
Entity entity = { (entt::entity)(entityId), Engine::GetCurrentScene().get() };
if (entity.IsValid() && entity.HasComponent<CameraComponent>())
{
auto& component = entity.GetComponent<CameraComponent>();
component.CameraInstance->Fov = safeFov;
}
}
void MoveAndSlide(int entityId, float vx, float vy, float vz)
{
Entity entity = { (entt::entity)(entityId), Engine::GetCurrentScene().get() };
@@ -432,6 +456,30 @@ namespace Nuake {
return {};
}
bool AudioEmitterGetIsPlaying(int entityId)
{
Entity entity = Entity((entt::entity)(entityId), Engine::GetCurrentScene().get());
if (entity.IsValid() && entity.HasComponent<AudioEmitterComponent>())
{
auto& audioEmitter = entity.GetComponent<AudioEmitterComponent>();
return audioEmitter.IsPlaying;
}
return false;
}
void AudioEmitterSetIsPlaying(int entityId, Coral::Bool32 isPlaying)
{
Entity entity = Entity((entt::entity)(entityId), Engine::GetCurrentScene().get());
if (entity.IsValid() && entity.HasComponent<AudioEmitterComponent>())
{
auto& audioEmitter = entity.GetComponent<AudioEmitterComponent>();
audioEmitter.IsPlaying = isPlaying;
}
}
void Nuake::SceneNetAPI::RegisterMethods()
{
// Entity
@@ -443,6 +491,7 @@ namespace Nuake {
RegisterMethod("Entity.EntityIsValidIcall", &EntityIsValid);
RegisterMethod("Entity.EntityGetTargetsIcall", &EntityGetTargets);
RegisterMethod("Entity.EntityGetTargetIcall", &EntityGetTarget);
// Prefab
RegisterMethod("Prefab.PrefabInstanceIcall", &PrefabInstance);
@@ -465,6 +514,8 @@ namespace Nuake {
// Camera
RegisterMethod("CameraComponent.GetDirectionIcall", &CameraGetDirection);
RegisterMethod("CameraComponent.GetCameraFOVIcall", &CameraGetFOV);
RegisterMethod("CameraComponent.SetCameraFOVIcall", &CameraSetFOV);
// Character Controller
RegisterMethod("CharacterControllerComponent.MoveAndSlideIcall", &MoveAndSlide);
@@ -475,6 +526,10 @@ namespace Nuake {
// Navigation Mesh
RegisterMethod("NavMeshVolumeComponent.FindPathIcall", &NavMeshComponentFindPath);
// Audio Emitter
RegisterMethod("AudioEmitterComponent.GetIsPlayingIcall", &AudioEmitterGetIsPlaying);
RegisterMethod("AudioEmitterComponent.SetIsPlayingIcall", &AudioEmitterSetIsPlaying);
}
}

View File

@@ -95,26 +95,38 @@ namespace Nuake
{
auto lineCharNums = String::Split(numbersString[0], ',');
int lineNum = std::stoi(lineCharNums[0]);
int charNum = std::stoi(lineCharNums[1]);
// error message
std::string errMesg = "";
int i = 0;
for (auto s : String::Split(line, ':'))
if (lineCharNums.size() == 1)
{
if (i >= 3)
{
errMesg += s;
}
i++;
CompilationError compilationError;
compilationError.message = "";
compilationError.file = filePath;
compilationError.line = 0;
errors.push_back(compilationError);
}
else
{
int lineNum = std::stoi(lineCharNums[0]);
int charNum = std::stoi(lineCharNums[1]);
CompilationError compilationError;
compilationError.message = errMesg;
compilationError.file = filePath;
compilationError.line = lineNum;
errors.push_back(compilationError);
// error message
std::string errMesg = "";
int i = 0;
for (auto s : String::Split(line, ':'))
{
if (i >= 3)
{
errMesg += s;
}
i++;
}
CompilationError compilationError;
compilationError.message = errMesg;
compilationError.file = filePath;
compilationError.line = lineNum;
errors.push_back(compilationError);
}
}
}
@@ -173,6 +185,7 @@ namespace Nuake
}
Coral::GC::Collect();
Coral::GC::WaitForPendingFinalizers();
GetHostInstance()->UnloadAssemblyLoadContext(m_LoadContext);
@@ -298,23 +311,33 @@ namespace Nuake
m_PrefabType = m_GameAssembly.GetType("Nuake.Net.Prefab");
auto& exposedFieldAttributeType = m_GameAssembly.GetType("Nuake.Net.ExposedAttribute");
auto& brushScriptAttributeType = m_GameAssembly.GetType("Nuake.Net.BrushScript");
auto& pointScriptAttributeType = m_GameAssembly.GetType("Nuake.Net.PointScript");
for (auto& type : m_GameAssembly.GetTypes())
{
// Brush
bool isBrushScript = false;
std::string brushDescription;
bool isTrigger = false;
// Point
bool isPointScript = false;
std::string pointDescription;
for (auto& attribute : type->GetAttributes())
{
if (attribute.GetType() != brushScriptAttributeType)
if (attribute.GetType() == brushScriptAttributeType)
{
continue;
brushDescription = attribute.GetFieldValue<Coral::String>("Description");
isTrigger = attribute.GetFieldValue<Coral::Bool32>("IsTrigger");
isBrushScript = true;
}
brushDescription = attribute.GetFieldValue<Coral::String>("Description");
isTrigger = attribute.GetFieldValue<Coral::Bool32>("IsTrigger");
isBrushScript = true;
if (attribute.GetType() == pointScriptAttributeType)
{
pointDescription = attribute.GetFieldValue<Coral::String>("Description");
isPointScript = true;
}
}
const std::string baseTypeName = std::string(type->GetBaseType().GetFullName());
@@ -395,6 +418,11 @@ namespace Nuake
m_BrushEntityTypes[shortenedTypeName] = gameScriptObject;
}
if (isPointScript)
{
gameScriptObject.Description = pointDescription;
m_PointEntityTypes[shortenedTypeName] = gameScriptObject;
}
m_GameEntityTypes[shortenedTypeName] = gameScriptObject;
}
}

View File

@@ -50,6 +50,7 @@ namespace Nuake {
bool isTrigger = false;
std::string Description = "";
AABB aabb = AABB({0, 0, 0}, { 1, 1, 1 });
};
struct CompilationError
@@ -86,6 +87,7 @@ namespace Nuake {
// This is a map that contains all the instances of entity scripts.
std::unordered_map<std::string, NetGameScriptObject> m_GameEntityTypes;
std::unordered_map<std::string, NetGameScriptObject> m_BrushEntityTypes;
std::unordered_map<std::string, NetGameScriptObject> m_PointEntityTypes;
std::unordered_map<uint32_t, Coral::ManagedObject> m_EntityToManagedObjects;
ScriptingEngineNet();
@@ -126,6 +128,7 @@ namespace Nuake {
Coral::ManagedAssembly GetNuakeAssembly() const { return m_NuakeAssembly; }
std::unordered_map<std::string, NetGameScriptObject> GetBrushEntities() const { return m_BrushEntityTypes; }
std::unordered_map<std::string, NetGameScriptObject> GetPointEntities() const { return m_PointEntityTypes; }
private:
std::string GenerateGUID();
};

View File

@@ -157,13 +157,25 @@ namespace Nuake.Net
public class CameraComponent : IComponent
{
internal static unsafe delegate*<int, NativeArray<float>> GetDirectionIcall;
internal static unsafe delegate*<int, float, void> SetCameraFOVIcall;
internal static unsafe delegate*<int, float> GetCameraFOVIcall;
public CameraComponent(int entityId)
{
EntityID = entityId;
}
public float FOV { get; set; }
public float FOV
{
get
{
unsafe { return GetCameraFOVIcall(EntityID); }
}
set
{
unsafe { SetCameraFOVIcall(EntityID, value); }
}
}
public Vector3 Direction
{
@@ -181,6 +193,9 @@ namespace Nuake.Net
public class AudioEmitterComponent : IComponent
{
internal static unsafe delegate*<int, bool> GetIsPlayingIcall;
internal static unsafe delegate*<int, bool, void> SetIsPlayingIcall;
public AudioEmitterComponent(int entityId)
{
EntityID = entityId;
@@ -190,7 +205,11 @@ namespace Nuake.Net
public bool Loop { get; set; }
public bool Spatialized { get; set; }
public bool Playing { get; set; }
public bool Playing
{
get { unsafe { return GetIsPlayingIcall(EntityID); } }
set { unsafe { SetIsPlayingIcall(EntityID, value); } }
}
}
public class ModelComponent : IComponent

View File

@@ -30,6 +30,23 @@ namespace Nuake.Net
}
}
public struct AABB
{
public Vector3 A;
public Vector3 B;
public AABB(Vector3 a, Vector3 b)
{
A = a;
B = b;
}
public AABB(float ax, float ay, float az, float bx, float by, float bz)
{
A = new Vector3(ax, ay, az);
B = new Vector3(bx, by, bz);
}
}
[AttributeUsage(AttributeTargets.Field)]
public sealed class ExposedAttribute : Attribute
@@ -53,12 +70,30 @@ namespace Nuake.Net
public sealed class PointScript : Attribute
{
public string Description = "";
public bool IsTrigger = false;
public Vector3 AABBA;
public Vector2 AABBB;
public List<float> AABBData = new();
public List<float> ColorData = new();
public Color Color
{
get { return Color; }
set
{
ColorData.Add(value.R);
ColorData.Add(value.G);
ColorData.Add(value.B);
}
}
string Model;
float ModelScale = 1.0f;
string Sprite;
public PointScript(string description = "")
{
Description = description;
IsTrigger = isTrigger;
}
}