mirror of
https://github.com/antopilo/Nuake.git
synced 2026-01-03 14:09:46 +03:00
Finished basic GLTF asset baking thread safe
This commit is contained in:
@@ -12,15 +12,17 @@
|
||||
|
||||
#include <src/Core/String.h>
|
||||
#include <src/Resource/ModelLoader.h>
|
||||
#include "src/Resource/Bakers/AssetBakerManager.h"
|
||||
#include "src/Resource/ResourceManager.h"
|
||||
|
||||
class MeshPanel : ComponentPanel
|
||||
{
|
||||
private:
|
||||
private:
|
||||
Scope<ModelResourceInspector> _modelInspector;
|
||||
bool _expanded = false;
|
||||
|
||||
|
||||
std::string _importedPathMesh;
|
||||
public:
|
||||
public:
|
||||
MeshPanel()
|
||||
{
|
||||
CreateScope<ModelResourceInspector>();
|
||||
@@ -73,55 +75,31 @@ public:
|
||||
{
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("_Model"))
|
||||
{
|
||||
char* file = (char*)payload->Data;
|
||||
std::string fullPath = std::string(file, 256);
|
||||
// Convert payload to relative path
|
||||
std::string fullPath = std::string(static_cast<char*>(payload->Data), 256);
|
||||
fullPath = Nuake::FileSystem::AbsoluteToRelative(fullPath);
|
||||
|
||||
if (Nuake::String::EndsWith(fullPath, ".mesh"))
|
||||
{
|
||||
component.ModelPath = fullPath;
|
||||
//component.ModelResource = ResourceLoader::LoadModel(fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert to .Model
|
||||
//component.ModelPath = fullPath;
|
||||
//component.LoadModel();
|
||||
//
|
||||
//_importedPathMesh = fullPath;
|
||||
//
|
||||
//auto loader = ModelLoader();
|
||||
//auto modelResource = loader.LoadModel(fullPath);
|
||||
//shouldConvert = true;
|
||||
}
|
||||
auto file = Nuake::FileSystem::GetFile(fullPath);
|
||||
if(file)
|
||||
{
|
||||
// TODO(antopilo) use file type instead of extension
|
||||
if(file->GetExtension() == ".nkmesh")
|
||||
{
|
||||
auto modelResource = ResourceManager::GetResourceFromFile<Model>(file);
|
||||
component.ModelResource = modelResource->ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if we can bake this filetype
|
||||
AssetBakerManager& bakerMgr = AssetBakerManager::Get();
|
||||
bakerMgr.Bake(file);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
if (PopupHelper::DefineConfirmationDialog("##ConvertAsset", "Convert Asset"))
|
||||
{
|
||||
// Convert to disk
|
||||
auto loader = ModelLoader();
|
||||
Ref<Model> modelResource = loader.LoadModel(_importedPathMesh);
|
||||
//json serializedData = modelResource->SerializeData();
|
||||
//
|
||||
//const std::string exportedMeshPath = _importedPathMesh + ".mesh";
|
||||
//FileSystem::BeginWriteFile(exportedMeshPath);
|
||||
//FileSystem::WriteLine(serializedData.dump());
|
||||
//FileSystem::EndWriteFile();
|
||||
//
|
||||
//ResourceManager::RegisterResource(modelResource);
|
||||
//
|
||||
//// Update component
|
||||
//component.ModelPath = exportedMeshPath;
|
||||
//component.ModelResource = modelResource;
|
||||
}
|
||||
|
||||
if (shouldConvert)
|
||||
{
|
||||
PopupHelper::OpenPopup("##ConvertAsset");
|
||||
}
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ComponentTableReset(component.ModelPath, "");
|
||||
}
|
||||
|
||||
@@ -10,13 +10,18 @@
|
||||
#include "src/UI/NuakeUI.h"
|
||||
#include "src/UI/UIInputManager.h"
|
||||
|
||||
|
||||
#include "src/Resource/Bakers/AssetBakerManager.h"
|
||||
#include "src/Resource/Bakers/GLTFBaker.h"
|
||||
|
||||
void EditorApplication::OnInit()
|
||||
{
|
||||
using namespace Nuake;
|
||||
|
||||
Engine::Init();
|
||||
|
||||
// Register bakers, these can convert files into nuake resources
|
||||
AssetBakerManager& assetBakerMgr = AssetBakerManager::Get();
|
||||
assetBakerMgr.RegisterBaker(CreateRef<GLTFBaker>());
|
||||
|
||||
m_Window = Engine::GetCurrentWindow();
|
||||
m_Window->SetSize({ m_Specification.WindowWidth, m_Specification.WindowHeight });
|
||||
m_Window->SetTitle(m_Specification.Name);
|
||||
|
||||
@@ -305,7 +305,7 @@ namespace Nuake
|
||||
{
|
||||
dragType = "_Material";
|
||||
}
|
||||
else if (fileExtension == ".mesh" || fileExtension == ".obj" || fileExtension == ".mdl" || fileExtension == ".gltf" || fileExtension == ".md3" || fileExtension == ".fbx" || fileExtension == ".glb")
|
||||
else if (fileExtension == ".nkmesh" || fileExtension == ".obj" || fileExtension == ".mdl" || fileExtension == ".gltf" || fileExtension == ".md3" || fileExtension == ".fbx" || fileExtension == ".glb")
|
||||
{
|
||||
dragType = "_Model";
|
||||
}
|
||||
|
||||
@@ -20,11 +20,19 @@ namespace Nuake
|
||||
{
|
||||
Mesh::Mesh() {}
|
||||
Mesh::~Mesh() {}
|
||||
|
||||
|
||||
void Mesh::SetData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices)
|
||||
{
|
||||
m_Vertices = vertices;
|
||||
m_Indices = indices;
|
||||
m_IndicesCount = static_cast<uint32_t>(m_Indices.size());
|
||||
m_VerticesCount = static_cast<uint32_t>(m_Vertices.size());
|
||||
}
|
||||
|
||||
void Mesh::AddSurface(std::vector<Vertex> vertices, std::vector<uint32_t> indices)
|
||||
{
|
||||
this->m_Vertices = vertices;
|
||||
this->m_Indices = indices;
|
||||
SetData(vertices, indices);
|
||||
|
||||
|
||||
SetupMesh();
|
||||
CalculateAABB();
|
||||
@@ -34,8 +42,7 @@ namespace Nuake
|
||||
m_Material = MaterialManager::Get()->GetMaterial("default");
|
||||
}
|
||||
|
||||
m_IndicesCount = static_cast<uint32_t>(m_Indices.size());
|
||||
m_VerticesCount = static_cast<uint32_t>(m_Vertices.size());
|
||||
|
||||
|
||||
//m_Indices.clear();
|
||||
}
|
||||
@@ -237,5 +244,5 @@ namespace Nuake
|
||||
|
||||
SetupMesh();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,12 @@ namespace Nuake
|
||||
public:
|
||||
Mesh();
|
||||
~Mesh();
|
||||
|
||||
|
||||
// This sets the data of the mesh without uploading it to the GPU.
|
||||
// Useful for async asset baking
|
||||
void SetData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices);
|
||||
|
||||
// This sets the data and uploads it to the GPU
|
||||
void AddSurface(std::vector<Vertex> vertices, std::vector<uint32_t> indices);
|
||||
std::vector<Vertex>& GetVertices();
|
||||
std::vector<uint32_t>& GetIndices();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include "IAssetBaker.h"
|
||||
#include "src/Core/Core.h"
|
||||
#include "src/FileSystem/File.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
@@ -8,19 +10,31 @@ namespace Nuake
|
||||
{
|
||||
class AssetBakerManager
|
||||
{
|
||||
private:
|
||||
private:
|
||||
std::map<std::string, Ref<IAssetBaker>> Bakers;
|
||||
|
||||
public:
|
||||
|
||||
public:
|
||||
static AssetBakerManager& Get()
|
||||
{
|
||||
static AssetBakerManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
void RegisterBaker(Ref<IAssetBaker> baker)
|
||||
{
|
||||
Bakers[baker->GetExtension()] = baker;
|
||||
}
|
||||
|
||||
Ref<File> Bake(const Ref<File>& file)
|
||||
{
|
||||
const std::string extension = file->GetExtension();
|
||||
if(Bakers.find(extension) == Bakers.end())
|
||||
{
|
||||
assert(false && "Baker not found for asset type");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Bakers[extension]->Bake(file);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,9 +1,274 @@
|
||||
#include "GLTFBaker.h"
|
||||
|
||||
#include "src/Core/Logger.h"
|
||||
#include "src/Core/String.h"
|
||||
|
||||
#include "src/Rendering/Textures/Material.h"
|
||||
#include "src/Rendering/Mesh/Mesh.h"
|
||||
#include "src/Resource/Model.h"
|
||||
#include "src/Resource/Serializer/BinarySerializer.h"
|
||||
|
||||
using namespace Nuake;
|
||||
|
||||
// Converts a GLTF file to a .nkmesh binary file
|
||||
// TODO(antopilo): Add support for multiple file extensions
|
||||
Ref<File> GLTFBaker::Bake(const Ref<File>& file)
|
||||
{
|
||||
|
||||
// 1. Read file
|
||||
// 2. Convert
|
||||
// 3. return output file
|
||||
// NOTE: This function should not interact with the rest
|
||||
// of the engine and be thread-safe in order to be ran from jobs.
|
||||
|
||||
// Load into assimp
|
||||
Assimp::Importer importer;
|
||||
importer.SetPropertyFloat("PP_GSN_MAX_SMOOTHING_ANGLE", 90);
|
||||
|
||||
const std::string absolutePath = file->GetAbsolutePath();
|
||||
auto importFlags =
|
||||
aiProcess_Triangulate |
|
||||
aiProcess_GenSmoothNormals |
|
||||
aiProcess_FixInfacingNormals |
|
||||
aiProcess_CalcTangentSpace;
|
||||
const aiScene* scene = importer.ReadFile(absolutePath, importFlags);
|
||||
|
||||
if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // Assimp failed
|
||||
{
|
||||
std::string assimpErrorMsg = std::string(importer.GetErrorString());
|
||||
std::string logMsg = "" + assimpErrorMsg;
|
||||
Logger::Log(logMsg, "GLTF Baker", CRITICAL);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Processing of the data
|
||||
std::vector<MeshData> meshesData;
|
||||
ProcessNode(scene->mRootNode, scene, meshesData);
|
||||
|
||||
importer.FreeScene();
|
||||
|
||||
// Write those meshes to disk!
|
||||
std::vector<Mesh> meshes;
|
||||
|
||||
Ref<Model> model = CreateRef<Model>();
|
||||
for(auto& meshData : meshesData)
|
||||
{
|
||||
Ref<Mesh> mesh = CreateRef<Mesh>();
|
||||
mesh->SetData(meshData.vertices, meshData.indices);
|
||||
|
||||
const BakerMaterialData materialData = meshData.material;
|
||||
Ref<Material> material = CreateRef<Material>();
|
||||
|
||||
if(!materialData.albedo.empty())
|
||||
{
|
||||
material->SetAlbedo(materialData.albedo);
|
||||
}
|
||||
|
||||
if(!materialData.normal.empty())
|
||||
{
|
||||
material->SetNormal(materialData.normal);
|
||||
}
|
||||
|
||||
if(!materialData.ao.empty())
|
||||
{
|
||||
material->SetAO(materialData.ao);
|
||||
}
|
||||
|
||||
if(!materialData.metallic.empty())
|
||||
{
|
||||
material->SetMetalness(materialData.metallic);
|
||||
}
|
||||
|
||||
if(!materialData.roughness.empty())
|
||||
{
|
||||
material->SetRoughness(materialData.roughness);
|
||||
}
|
||||
|
||||
model->AddMesh(std::move(mesh));
|
||||
}
|
||||
|
||||
// Bake
|
||||
BinarySerializer serializer;
|
||||
|
||||
const std::string outputPath = file->GetAbsolutePath() + ".nkmesh";
|
||||
serializer.SerializeModel(outputPath, model);
|
||||
|
||||
//Ref<Model> model = CreateRef<Model>();
|
||||
return file;
|
||||
}
|
||||
|
||||
void GLTFBaker::ProcessNode(aiNode* node, const aiScene* scene, std::vector<MeshData>& meshes)
|
||||
{
|
||||
for(uint32_t i = 0; i < node->mNumMeshes; i++)
|
||||
{
|
||||
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
||||
meshes.push_back(ProcessMesh(node, mesh, scene));
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < node->mNumChildren; i++)
|
||||
{
|
||||
ProcessNode(node->mChildren[i], scene, meshes);
|
||||
}
|
||||
}
|
||||
|
||||
MeshData GLTFBaker::ProcessMesh(aiNode* node, aiMesh* meshNode, const aiScene* scene)
|
||||
{
|
||||
std::vector<Vertex> vertices = ProcessVertices(meshNode);
|
||||
for(auto& vertex : vertices)
|
||||
{
|
||||
const Matrix4& modelTransform = ConvertAssimpToGLM(node->mTransformation);
|
||||
vertex.position = modelTransform * Vector4(vertex.position, 1.0f);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> indices = ProcessIndices(meshNode);
|
||||
MeshData meshData
|
||||
{
|
||||
std::move(vertices),
|
||||
std::move(indices),
|
||||
ProcessMaterials(scene, meshNode)
|
||||
};
|
||||
|
||||
return std::move(meshData);
|
||||
}
|
||||
|
||||
std::vector<Vertex> GLTFBaker::ProcessVertices(aiMesh* mesh)
|
||||
{
|
||||
auto vertices = std::vector<Vertex>();
|
||||
|
||||
const uint32_t numVertices = mesh->mNumVertices;
|
||||
vertices.reserve(numVertices);
|
||||
|
||||
for(uint32_t i = 0; i < numVertices; i++)
|
||||
{
|
||||
Vertex vertex {};
|
||||
vertex.uv_x = 0.0f;
|
||||
vertex.uv_y = 0.0f;
|
||||
|
||||
// Position
|
||||
auto assimpVec3 = mesh->mVertices[i];
|
||||
Vector3 position
|
||||
{
|
||||
assimpVec3.x,
|
||||
assimpVec3.y,
|
||||
assimpVec3.z
|
||||
};
|
||||
vertex.position = std::move(position);
|
||||
|
||||
// Normal
|
||||
assimpVec3 = mesh->mNormals[i];
|
||||
Vector3 normal
|
||||
{
|
||||
assimpVec3.x,
|
||||
assimpVec3.y,
|
||||
assimpVec3.z
|
||||
};
|
||||
vertex.normal = std::move(normal);
|
||||
|
||||
if(mesh->mTangents)
|
||||
{
|
||||
vertex.tangent =
|
||||
Vector4 {
|
||||
mesh->mTangents[i].x,
|
||||
mesh->mTangents[i].y,
|
||||
mesh->mTangents[i].z,
|
||||
0.0f
|
||||
};
|
||||
}
|
||||
|
||||
if(mesh->mBitangents)
|
||||
{
|
||||
vertex.bitangent =
|
||||
Vector4 {
|
||||
mesh->mBitangents[i].x,
|
||||
mesh->mBitangents[i].y,
|
||||
mesh->mBitangents[i].z,
|
||||
0.0f
|
||||
};
|
||||
}
|
||||
|
||||
if(mesh->mTextureCoords[0])
|
||||
{
|
||||
vertex.uv_x = mesh->mTextureCoords[0][i].x;
|
||||
vertex.uv_y = mesh->mTextureCoords[0][i].y;
|
||||
}
|
||||
|
||||
vertices.push_back(vertex);
|
||||
}
|
||||
|
||||
return std::move(vertices);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> GLTFBaker::ProcessIndices(aiMesh* mesh)
|
||||
{
|
||||
auto indices = std::vector<uint32_t>();
|
||||
for (uint32_t i = 0; i < mesh->mNumFaces; i++)
|
||||
{
|
||||
aiFace face = mesh->mFaces[i];
|
||||
for (uint32_t j = 0; j < face.mNumIndices; j++)
|
||||
{
|
||||
indices.push_back(face.mIndices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(indices);
|
||||
}
|
||||
|
||||
BakerMaterialData GLTFBaker::ProcessMaterials(const aiScene* scene, aiMesh* mesh)
|
||||
{
|
||||
BakerMaterialData materialData { };
|
||||
|
||||
if(mesh->mMaterialIndex < 0)
|
||||
{
|
||||
return materialData;
|
||||
}
|
||||
|
||||
aiMaterial* materialNode = scene->mMaterials[mesh->mMaterialIndex];
|
||||
|
||||
aiString aiMaterialName;
|
||||
materialNode->Get(AI_MATKEY_NAME, aiMaterialName);
|
||||
|
||||
const std::string materialName = std::string(aiMaterialName.C_Str());
|
||||
if(Materials.find(materialName) != Materials.end())
|
||||
{
|
||||
return materialData;
|
||||
}
|
||||
|
||||
aiString str;
|
||||
if(materialNode->GetTextureCount(aiTextureType_DIFFUSE) > 0)
|
||||
{
|
||||
materialNode->GetTexture(aiTextureType_DIFFUSE, 0, &str);
|
||||
materialData.albedo = std::string(str.C_Str());
|
||||
}
|
||||
|
||||
if(materialNode->GetTextureCount(aiTextureType_NORMALS) > 0)
|
||||
{
|
||||
materialNode->GetTexture(aiTextureType_NORMALS, 0, &str);
|
||||
materialData.normal = std::string(str.C_Str());
|
||||
}
|
||||
|
||||
if(materialNode->GetTextureCount(aiTextureType_AMBIENT_OCCLUSION) > 0)
|
||||
{
|
||||
materialNode->GetTexture(aiTextureType_AMBIENT_OCCLUSION, 0, &str);
|
||||
materialData.ao = std::string(str.C_Str());
|
||||
}
|
||||
|
||||
if(materialNode->GetTextureCount(aiTextureType_DIFFUSE_ROUGHNESS) > 0)
|
||||
{
|
||||
materialNode->GetTexture(aiTextureType_DIFFUSE_ROUGHNESS, 0, &str);
|
||||
materialData.roughness = std::string(str.C_Str());
|
||||
}
|
||||
|
||||
return std::move(materialData);
|
||||
}
|
||||
|
||||
std::string GLTFBaker::ProcessTextures(const aiScene* scene, const std::string& path)
|
||||
{
|
||||
if(String::BeginsWith(path, "*"))
|
||||
{
|
||||
// TODO(antopilo): Figure out how to handle embedded textures.
|
||||
|
||||
Logger::Log("Embedded textures not supported", "GLTFBaker", WARNING);
|
||||
return "";
|
||||
}
|
||||
|
||||
return std::move(path);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,60 @@
|
||||
#include "IAssetBaker.h"
|
||||
|
||||
#include "src/Rendering/Vertex.h"
|
||||
|
||||
// Assimp
|
||||
#include "assimp/Importer.hpp"
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Nuake
|
||||
{
|
||||
struct BakerMaterialData
|
||||
{
|
||||
std::string albedo;
|
||||
std::string normal;
|
||||
std::string ao;
|
||||
std::string roughness;
|
||||
std::string metallic;
|
||||
};
|
||||
|
||||
struct MeshData
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
BakerMaterialData material;
|
||||
};
|
||||
|
||||
class GLTFBaker : public IAssetBaker
|
||||
{
|
||||
public:
|
||||
GLTFBaker() : IAssetBaker(".glb") {}
|
||||
|
||||
|
||||
Ref<File> Bake(const Ref<File>& file) override;
|
||||
|
||||
private:
|
||||
std::map<std::string, BakerMaterialData> Materials;
|
||||
|
||||
void ProcessNode(aiNode* node, const aiScene* scene, std::vector<MeshData>& meshes);
|
||||
MeshData ProcessMesh(aiNode* node, aiMesh* meshNode, const aiScene* scene);
|
||||
std::vector<Vertex> ProcessVertices(aiMesh* mesh);
|
||||
std::vector<uint32_t> ProcessIndices(aiMesh* mesh);
|
||||
BakerMaterialData ProcessMaterials(const aiScene* scene, aiMesh* mesh);
|
||||
std::string ProcessTextures(const aiScene* scene, const std::string& path);
|
||||
|
||||
private:
|
||||
static inline Matrix4 ConvertAssimpToGLM(const aiMatrix4x4& from)
|
||||
{
|
||||
Matrix4 to;
|
||||
|
||||
to[0][0] = from.a1; to[0][1] = from.b1; to[0][2] = from.c1; to[0][3] = from.d1;
|
||||
to[1][0] = from.a2; to[1][1] = from.b2; to[1][2] = from.c2; to[1][3] = from.d2;
|
||||
to[2][0] = from.a3; to[2][1] = from.b3; to[2][2] = from.c3; to[2][3] = from.d3;
|
||||
to[3][0] = from.a4; to[3][1] = from.b4; to[3][2] = from.c4; to[3][3] = from.d4;
|
||||
|
||||
return to;
|
||||
}
|
||||
};
|
||||
}
|
||||
30
build.bat
Normal file
30
build.bat
Normal file
@@ -0,0 +1,30 @@
|
||||
@echo off
|
||||
|
||||
REM Set default values for configuration and platform
|
||||
set CONFIG=Debug
|
||||
set PLATFORM=
|
||||
|
||||
REM Get solution file path
|
||||
set SOLUTION=Nuake.sln
|
||||
|
||||
REM Check if Configuration is provided
|
||||
if not "%~2"=="" (
|
||||
set CONFIG=%~2
|
||||
)
|
||||
|
||||
REM Check if Platform is provided
|
||||
if not "%~3"=="" (
|
||||
set PLATFORM=%~3
|
||||
)
|
||||
|
||||
REM Build the solution
|
||||
echo Building solution "%SOLUTION%" with Configuration=%CONFIG% and Platform=%PLATFORM%...
|
||||
"C:\Program Files\Microsoft Visual Studio\2022\Community\Msbuild\Current\Bin\MSBuild.exe" "Nuake.sln" -verbosity:minimal
|
||||
PAUSE
|
||||
REM Check if build succeeded
|
||||
if %ERRORLEVEL%==0 (
|
||||
echo Build succeeded.
|
||||
) else (
|
||||
echo Build failed.
|
||||
exit /b 1
|
||||
)
|
||||
58
project.4coder
Normal file
58
project.4coder
Normal file
@@ -0,0 +1,58 @@
|
||||
version(1);
|
||||
|
||||
project_name = "Nuake";
|
||||
|
||||
patterns = {
|
||||
"*.c",
|
||||
"*.cpp",
|
||||
"*.h",
|
||||
"*.hpp",
|
||||
"*.lua",
|
||||
"*.cs",
|
||||
"*.hlsl",
|
||||
"*.frag",
|
||||
"*.vert",
|
||||
"*.4coder"
|
||||
};
|
||||
|
||||
blacklist_patterns = {
|
||||
".*",
|
||||
};
|
||||
|
||||
load_paths = {
|
||||
{
|
||||
{ {"."}, .recursive = false, .relative = true }, .os = "win"
|
||||
},
|
||||
{
|
||||
{ {"Nuake"}, .recursive = false, .relative = true }, .os = "win"
|
||||
},
|
||||
{
|
||||
{ {"Nuake/src"}, .recursive = true, .relative = true }, .os = "win"
|
||||
}
|
||||
};
|
||||
|
||||
command_list = {
|
||||
{
|
||||
.name = "build",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = true,
|
||||
.cmd = {
|
||||
{ "build.bat Debug Active", .os = "win" },
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "run",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = true,
|
||||
.cmd = {
|
||||
{ "run.bat", .os = "win" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
fkey_command[1] = "build";
|
||||
fkey_command[5] = "run";
|
||||
Reference in New Issue
Block a user