mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-11-23 13:12:06 +00:00
WIP:
- Updated file structure to use BSP naming scheme - Re-wrote BSP creator to use c++ more efficiently
This commit is contained in:
@@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "Game/T6/T6.h"
|
|
||||||
using namespace T6;
|
|
||||||
|
|
||||||
struct CustomMapVertex
|
|
||||||
{
|
|
||||||
vec3_t pos;
|
|
||||||
vec4_t color;
|
|
||||||
vec2_t texCoord;
|
|
||||||
vec3_t normal;
|
|
||||||
vec3_t tangent;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum CustomMapMaterialType
|
|
||||||
{
|
|
||||||
MATERIAL_TYPE_COLOUR,
|
|
||||||
MATERIAL_TYPE_TEXTURE,
|
|
||||||
MATERIAL_TYPE_EMPTY
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CustomMapMaterial
|
|
||||||
{
|
|
||||||
CustomMapMaterialType materialType;
|
|
||||||
std::string materialName;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CustomMapSurface
|
|
||||||
{
|
|
||||||
CustomMapMaterial material;
|
|
||||||
int triCount;
|
|
||||||
int indexOfFirstVertex;
|
|
||||||
int indexOfFirstIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CustomMapWorld
|
|
||||||
{
|
|
||||||
std::vector<CustomMapSurface> surfaces;
|
|
||||||
std::vector<CustomMapVertex> vertices;
|
|
||||||
std::vector<uint16_t> indices;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CustomMapBSP
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::string bspName;
|
|
||||||
|
|
||||||
CustomMapWorld gfxWorld;
|
|
||||||
CustomMapWorld colWorld;
|
|
||||||
};
|
|
||||||
185
src/ObjLoading/Game/T6/BSP/BSP.h
Normal file
185
src/ObjLoading/Game/T6/BSP/BSP.h
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "Utils/Logging/Log.h"
|
||||||
|
|
||||||
|
#include "Game/T6/T6.h"
|
||||||
|
using namespace T6;
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
enum BSPMaterialType
|
||||||
|
{
|
||||||
|
MATERIAL_TYPE_COLOUR,
|
||||||
|
MATERIAL_TYPE_TEXTURE,
|
||||||
|
MATERIAL_TYPE_EMPTY
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSPVertex
|
||||||
|
{
|
||||||
|
vec3_t pos;
|
||||||
|
vec4_t color;
|
||||||
|
vec2_t texCoord;
|
||||||
|
vec3_t normal;
|
||||||
|
vec3_t tangent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSPMaterial
|
||||||
|
{
|
||||||
|
BSPMaterialType materialType;
|
||||||
|
std::string materialName;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSPSurface
|
||||||
|
{
|
||||||
|
BSPMaterial material;
|
||||||
|
int triCount;
|
||||||
|
int indexOfFirstVertex;
|
||||||
|
int indexOfFirstIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSPWorld
|
||||||
|
{
|
||||||
|
std::vector<BSPSurface> surfaces;
|
||||||
|
std::vector<BSPVertex> vertices;
|
||||||
|
std::vector<uint16_t> indices;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSPData
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::string bspName;
|
||||||
|
|
||||||
|
BSPWorld gfxWorld;
|
||||||
|
BSPWorld colWorld;
|
||||||
|
};
|
||||||
|
|
||||||
|
// BSPGameConstants:
|
||||||
|
// These values are hardcoded ingame and will break the map if they are changed
|
||||||
|
namespace BSPGameConstants
|
||||||
|
{
|
||||||
|
constexpr int MAX_COLLISION_VERTS = UINT16_MAX;
|
||||||
|
|
||||||
|
constexpr int STATIC_LIGHT_INDEX = 0;
|
||||||
|
constexpr int SUN_LIGHT_INDEX = 1;
|
||||||
|
|
||||||
|
inline const char* DEFENDER_SPAWN_POINT_NAMES[] = {
|
||||||
|
"mp_ctf_spawn_allies",
|
||||||
|
"mp_ctf_spawn_allies_start",
|
||||||
|
"mp_sd_spawn_defender",
|
||||||
|
"mp_dom_spawn_allies_start",
|
||||||
|
"mp_dem_spawn_defender_start",
|
||||||
|
"mp_dem_spawn_defenderOT_start",
|
||||||
|
"mp_dem_spawn_defender",
|
||||||
|
"mp_tdm_spawn_allies_start",
|
||||||
|
"mp_tdm_spawn_team1_start",
|
||||||
|
"mp_tdm_spawn_team2_start",
|
||||||
|
"mp_tdm_spawn_team3_start"
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const char* ATTACKER_SPAWN_POINT_NAMES[] = {
|
||||||
|
"mp_ctf_spawn_axis",
|
||||||
|
"mp_ctf_spawn_axis_start",
|
||||||
|
"mp_sd_spawn_attacker",
|
||||||
|
"mp_dom_spawn_axis_start",
|
||||||
|
"mp_dem_spawn_attacker_start",
|
||||||
|
"mp_dem_spawn_attackerOT_start",
|
||||||
|
"mp_dem_spawn_defender",
|
||||||
|
"mp_tdm_spawn_axis_start",
|
||||||
|
"mp_tdm_spawn_team4_start",
|
||||||
|
"mp_tdm_spawn_team5_start",
|
||||||
|
"mp_tdm_spawn_team6_start"
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const char* FFA_SPAWN_POINT_NAMES[] = {
|
||||||
|
"mp_tdm_spawn",
|
||||||
|
"mp_dm_spawn",
|
||||||
|
"mp_dom_spawn"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// BSPLinkingConstants:
|
||||||
|
// These values are BSP linking constants that are required for the link to be successful
|
||||||
|
namespace BSPLinkingConstants
|
||||||
|
{
|
||||||
|
constexpr const char* MISSING_IMAGE_NAME = "missing_image";
|
||||||
|
constexpr const char* COLOR_ONLY_IMAGE_NAME = "color_only_image";
|
||||||
|
|
||||||
|
constexpr const char* DEFAULT_SPAWN_POINT_STRING = R"({
|
||||||
|
"attackers": [
|
||||||
|
{
|
||||||
|
"origin": "0 0 0",
|
||||||
|
"angles": "0 0 0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defenders": [
|
||||||
|
{
|
||||||
|
"origin": "0 0 0",
|
||||||
|
"angles": "0 0 0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"FFA": [
|
||||||
|
{
|
||||||
|
"origin": "0 0 0",
|
||||||
|
"angles": "0 0 0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})";
|
||||||
|
|
||||||
|
|
||||||
|
constexpr const char* DEFAULT_MAP_ENTS_STRING = R"({
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"classname": "worldspawn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"angles": "0 0 0",
|
||||||
|
"classname": "info_player_start",
|
||||||
|
"origin": "0 0 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"angles": "0 0 0",
|
||||||
|
"classname": "mp_global_intermission",
|
||||||
|
"origin": "0 0 0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})";
|
||||||
|
}
|
||||||
|
|
||||||
|
// BSPEditableConstants:
|
||||||
|
// These values are BSP constants that can be edited and won't break the linker/game if changed
|
||||||
|
namespace BSPEditableConstants
|
||||||
|
{
|
||||||
|
// Default xmodel values
|
||||||
|
// Unused as there is no support for xmodels right now
|
||||||
|
constexpr float DEFAULT_SMODEL_CULL_DIST = 10000.0f;
|
||||||
|
constexpr int DEFAULT_SMODEL_FLAGS = STATIC_MODEL_FLAG_NO_SHADOW;
|
||||||
|
constexpr int DEFAULT_SMODEL_LIGHT = 1;
|
||||||
|
constexpr int DEFAULT_SMODEL_REFLECTION_PROBE = 0;
|
||||||
|
|
||||||
|
// Default surface values
|
||||||
|
constexpr int DEFAULT_SURFACE_LIGHT = BSPGameConstants::SUN_LIGHT_INDEX;
|
||||||
|
constexpr int DEFAULT_SURFACE_LIGHTMAP = 0;
|
||||||
|
constexpr int DEFAULT_SURFACE_REFLECTION_PROBE = 0;
|
||||||
|
constexpr int DEFAULT_SURFACE_FLAGS = (GFX_SURFACE_CASTS_SUN_SHADOW | GFX_SURFACE_CASTS_SHADOW);
|
||||||
|
|
||||||
|
// material flags determine the features of a surface
|
||||||
|
// unsure which flag type changes what right now
|
||||||
|
// -1 results in: no running, water splashes all the time, low friction, slanted angles make you slide very fast
|
||||||
|
// 1 results in: normal surface features, grenades work, seems normal
|
||||||
|
constexpr int MATERIAL_SURFACE_FLAGS = 1;
|
||||||
|
constexpr int MATERIAL_CONTENT_FLAGS = 1;
|
||||||
|
|
||||||
|
// terrain/world flags: does not change the type of terrain or what features they have
|
||||||
|
// from testing, as long at it isn't 0 things will work correctly
|
||||||
|
constexpr int LEAF_TERRAIN_CONTENTS = 1;
|
||||||
|
constexpr int WORLD_TERRAIN_CONTENTS = 1;
|
||||||
|
|
||||||
|
// lightgrid (global) lighting colour
|
||||||
|
// since lightgrids are not well understood, this colour is used for the R, G and B values right now
|
||||||
|
constexpr unsigned char LIGHTGRID_COLOUR = 128;
|
||||||
|
};
|
||||||
|
}
|
||||||
151
src/ObjLoading/Game/T6/BSP/BSPCalculation.cpp
Normal file
151
src/ObjLoading/Game/T6/BSP/BSPCalculation.cpp
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
#include "BSPCalculation.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
constexpr int MAX_NODE_SIZE = 512; // maximum size a BSP node can be before it becomes a leaf
|
||||||
|
|
||||||
|
BSPObject::BSPObject(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax, int objPartitionIndex)
|
||||||
|
{
|
||||||
|
min.x = xMin;
|
||||||
|
min.y = yMin;
|
||||||
|
min.z = zMin;
|
||||||
|
max.x = xMax;
|
||||||
|
max.y = yMax;
|
||||||
|
max.z = zMax;
|
||||||
|
partitionIndex = objPartitionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSPLeaf::addObject(std::shared_ptr<BSPObject> object)
|
||||||
|
{
|
||||||
|
objectList.emplace_back(std::move(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
BSPObject* BSPLeaf::getObject(int index)
|
||||||
|
{
|
||||||
|
return objectList.at(index).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BSPLeaf::getObjectCount()
|
||||||
|
{
|
||||||
|
return objectList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
BSPNode::BSPNode(std::unique_ptr<BSPTree> frontTree, std::unique_ptr<BSPTree> backTree, PlaneAxis nodeAxis, float nodeDistance)
|
||||||
|
{
|
||||||
|
front = std::move(frontTree);
|
||||||
|
back = std::move(backTree);
|
||||||
|
axis = nodeAxis;
|
||||||
|
distance = nodeDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaneSide BSPNode::objectIsInFront(BSPObject* object)
|
||||||
|
{
|
||||||
|
float minCoord, maxCoord;
|
||||||
|
|
||||||
|
// Select the relevant coordinate based on the plane's axis
|
||||||
|
if (axis == AXIS_X)
|
||||||
|
{
|
||||||
|
minCoord = object->min.x;
|
||||||
|
maxCoord = object->max.x;
|
||||||
|
}
|
||||||
|
else if (axis == AXIS_Y)
|
||||||
|
{
|
||||||
|
minCoord = object->min.y;
|
||||||
|
maxCoord = object->max.y;
|
||||||
|
}
|
||||||
|
else // axis == AXIS_Z
|
||||||
|
{
|
||||||
|
minCoord = object->min.z;
|
||||||
|
maxCoord = object->max.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare with the plane's distance
|
||||||
|
if (maxCoord < distance)
|
||||||
|
{
|
||||||
|
return SIDE_BACK; // Object is entirely on the negative side
|
||||||
|
}
|
||||||
|
else if (minCoord > distance)
|
||||||
|
{
|
||||||
|
return SIDE_FRONT; // Object is entirely on the positive side
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return SIDE_INTERSECTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BSPTree::BSPTree(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax, int treeLevel)
|
||||||
|
{
|
||||||
|
min.x = xMin;
|
||||||
|
min.y = yMin;
|
||||||
|
min.z = zMin;
|
||||||
|
max.x = xMax;
|
||||||
|
max.y = yMax;
|
||||||
|
max.z = zMax;
|
||||||
|
level = treeLevel;
|
||||||
|
splitTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For simplicity, only split across the X and Z axis.
|
||||||
|
// It is unlikely that there are many layers to a map, and is instead mostly flat
|
||||||
|
void BSPTree::splitTree()
|
||||||
|
{
|
||||||
|
std::unique_ptr<BSPTree> front;
|
||||||
|
std::unique_ptr<BSPTree> back;
|
||||||
|
float halfLength;
|
||||||
|
|
||||||
|
if (max.x - min.x > MAX_NODE_SIZE)
|
||||||
|
{
|
||||||
|
// split along the x axis
|
||||||
|
halfLength = (min.x + max.x) * 0.5f;
|
||||||
|
front = std::make_unique<BSPTree>(halfLength, min.y, min.z, max.x, max.y, max.z, level + 1);
|
||||||
|
back = std::make_unique<BSPTree>(min.x, min.y, min.z, halfLength, max.y, max.z, level + 1);
|
||||||
|
|
||||||
|
isLeaf = false;
|
||||||
|
node = std::make_unique<BSPNode>(std::move(front), std::move(back), AXIS_X, halfLength);
|
||||||
|
leaf = nullptr;
|
||||||
|
}
|
||||||
|
else if (max.z - min.z > MAX_NODE_SIZE)
|
||||||
|
{
|
||||||
|
// split along the z axis
|
||||||
|
halfLength = (min.z + max.z) * 0.5f;
|
||||||
|
front = std::make_unique<BSPTree>(min.x, min.y, halfLength, max.x, max.y, max.z, level + 1);
|
||||||
|
back = std::make_unique<BSPTree>(min.x, min.y, min.z, max.x, max.y, halfLength, level + 1);
|
||||||
|
|
||||||
|
isLeaf = false;
|
||||||
|
node = std::make_unique<BSPNode>(std::move(front), std::move(back), AXIS_Z, halfLength);
|
||||||
|
leaf = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isLeaf = true;
|
||||||
|
node = nullptr;
|
||||||
|
leaf = std::make_unique<BSPLeaf>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSPTree::addObjectToTree(std::shared_ptr<BSPObject> object)
|
||||||
|
{
|
||||||
|
if (isLeaf)
|
||||||
|
{
|
||||||
|
leaf->addObject(std::move(object));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PlaneSide side = node->objectIsInFront(object.get());
|
||||||
|
if (side == SIDE_FRONT)
|
||||||
|
{
|
||||||
|
node->front->addObjectToTree(std::move(object));
|
||||||
|
}
|
||||||
|
else if (side == SIDE_BACK)
|
||||||
|
{
|
||||||
|
node->back->addObjectToTree(std::move(object));
|
||||||
|
}
|
||||||
|
else // intersects
|
||||||
|
{
|
||||||
|
node->front->addObjectToTree(object);
|
||||||
|
node->back->addObjectToTree(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/ObjLoading/Game/T6/BSP/BSPCalculation.h
Normal file
72
src/ObjLoading/Game/T6/BSP/BSPCalculation.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BSP.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
enum PlaneAxis
|
||||||
|
{
|
||||||
|
AXIS_X,
|
||||||
|
AXIS_Y,
|
||||||
|
AXIS_Z
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PlaneSide
|
||||||
|
{
|
||||||
|
SIDE_FRONT,
|
||||||
|
SIDE_BACK,
|
||||||
|
SIDE_INTERSECTS
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSPObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
vec3_t min;
|
||||||
|
vec3_t max;
|
||||||
|
int partitionIndex; // index of the partition the object is contained in
|
||||||
|
|
||||||
|
BSPObject(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax, int objPartitionIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BSPLeaf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::vector<std::shared_ptr<BSPObject>> objectList;
|
||||||
|
|
||||||
|
void addObject(std::shared_ptr<BSPObject> object);
|
||||||
|
BSPObject* getObject(int index);
|
||||||
|
size_t getObjectCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSPTree;
|
||||||
|
|
||||||
|
class BSPNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::unique_ptr<BSPTree> front;
|
||||||
|
std::unique_ptr<BSPTree> back;
|
||||||
|
|
||||||
|
PlaneAxis axis; // axis that the split plane is on
|
||||||
|
float distance; // distance from the origin (0, 0, 0) to the plane
|
||||||
|
|
||||||
|
BSPNode(std::unique_ptr<BSPTree> frontTree, std::unique_ptr<BSPTree> backTree, PlaneAxis nodeAxis, float nodeDistance);
|
||||||
|
PlaneSide objectIsInFront(BSPObject* object);
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSPTree
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool isLeaf;
|
||||||
|
std::unique_ptr<BSPLeaf> leaf;
|
||||||
|
std::unique_ptr<BSPNode> node;
|
||||||
|
|
||||||
|
int level; // level in the BSP tree
|
||||||
|
vec3_t min;
|
||||||
|
vec3_t max;
|
||||||
|
|
||||||
|
BSPTree(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax, int treeLevel);
|
||||||
|
void splitTree();
|
||||||
|
void addObjectToTree(std::shared_ptr<BSPObject> object);
|
||||||
|
};
|
||||||
|
}
|
||||||
269
src/ObjLoading/Game/T6/BSP/BSPCreator.cpp
Normal file
269
src/ObjLoading/Game/T6/BSP/BSPCreator.cpp
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
#include "BSPCreator.h"
|
||||||
|
#include "fbx/ufbx.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace BSP;
|
||||||
|
|
||||||
|
void addFBXMeshToWorld(ufbx_node* node,
|
||||||
|
std::vector<BSPSurface>& surfaceVec,
|
||||||
|
std::vector<BSPVertex>& vertexVec,
|
||||||
|
std::vector<uint16_t>& indexVec,
|
||||||
|
bool& hasTangentSpace)
|
||||||
|
{
|
||||||
|
ufbx_mesh* mesh = node->mesh;
|
||||||
|
|
||||||
|
assert(node->attrib_type == UFBX_ELEMENT_MESH);
|
||||||
|
|
||||||
|
if (mesh->instances.count != 1)
|
||||||
|
con::warn("mesh {} has {} instances, only the 1st instace will be used.", node->name.data, mesh->instances.count);
|
||||||
|
|
||||||
|
if (mesh->num_triangles == 0)
|
||||||
|
{
|
||||||
|
con::warn("ignoring mesh {}: triangle count is 0.", node->name.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh->num_indices % 3 != 0)
|
||||||
|
{
|
||||||
|
con::warn("ignoring mesh {}: it is not triangulated.", node->name.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t k = 0; k < mesh->num_indices; k++)
|
||||||
|
{
|
||||||
|
if (mesh->vertex_indices[k] > UINT16_MAX)
|
||||||
|
{
|
||||||
|
con::warn("ignoring mesh {}, it has more than {} indices.", node->name.data, UINT16_MAX);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh->vertex_tangent.exists == false)
|
||||||
|
hasTangentSpace = false;
|
||||||
|
|
||||||
|
// Fix the target_unit_meters ufbx opt not working
|
||||||
|
// UFBX stores the transform data in units that are 100x larger than what blender uses, so this converts them back
|
||||||
|
ufbx_transform origTransform = node->local_transform;
|
||||||
|
origTransform.translation.x /= 100.0f;
|
||||||
|
origTransform.translation.y /= 100.0f;
|
||||||
|
origTransform.translation.z /= 100.0f;
|
||||||
|
origTransform.scale.x /= 100.0f;
|
||||||
|
origTransform.scale.y /= 100.0f;
|
||||||
|
origTransform.scale.z /= 100.0f;
|
||||||
|
ufbx_matrix meshMatrix = ufbx_transform_to_matrix(&origTransform);
|
||||||
|
|
||||||
|
for (ufbx_mesh_part& meshPart : mesh->material_parts)
|
||||||
|
{
|
||||||
|
if (meshPart.num_faces == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
surfaceVec.emplace_back();
|
||||||
|
BSPSurface* surface = &surfaceVec[surfaceVec.size() - 1];
|
||||||
|
|
||||||
|
size_t partTriangleNum = meshPart.num_triangles;
|
||||||
|
surface->triCount = static_cast<int>(partTriangleNum);
|
||||||
|
surface->indexOfFirstVertex = vertexVec.size();
|
||||||
|
surface->indexOfFirstIndex = indexVec.size();
|
||||||
|
|
||||||
|
if (mesh->materials.count == 0)
|
||||||
|
{
|
||||||
|
surface->material.materialType = MATERIAL_TYPE_EMPTY;
|
||||||
|
surface->material.materialName = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
surface->material.materialType = MATERIAL_TYPE_TEXTURE;
|
||||||
|
surface->material.materialName = mesh->materials.data[meshPart.index]->name.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BSPVertex> vertices;
|
||||||
|
std::vector<uint32_t> indices;
|
||||||
|
indices.resize(mesh->max_face_triangles * 3);
|
||||||
|
for (uint32_t faceIndex : meshPart.face_indices)
|
||||||
|
{
|
||||||
|
ufbx_face* face = &mesh->faces.data[faceIndex];
|
||||||
|
|
||||||
|
// Triangulate the face into the indices vector
|
||||||
|
uint32_t triangluatedTriCount = ufbx_triangulate_face(indices.data(), indices.size(), mesh, *face);
|
||||||
|
|
||||||
|
// Iterate over each triangle corner contiguously.
|
||||||
|
for (uint32_t idxOfIndex = 0; idxOfIndex < triangluatedTriCount * 3; idxOfIndex++)
|
||||||
|
{
|
||||||
|
vertices.emplace_back();
|
||||||
|
BSPVertex* vertex = &vertices[vertices.size() - 1];
|
||||||
|
uint32_t index = indices[idxOfIndex];
|
||||||
|
|
||||||
|
ufbx_vec3 transformedPos = ufbx_transform_position(&meshMatrix, ufbx_get_vertex_vec3(&mesh->vertex_position, index));
|
||||||
|
vertex->pos.x = static_cast<float>(transformedPos.x);
|
||||||
|
vertex->pos.y = static_cast<float>(transformedPos.y);
|
||||||
|
vertex->pos.z = static_cast<float>(transformedPos.z);
|
||||||
|
|
||||||
|
if (surface->material.materialType == MATERIAL_TYPE_TEXTURE || surface->material.materialType == MATERIAL_TYPE_EMPTY)
|
||||||
|
{
|
||||||
|
vertex->color.x = 1.0f;
|
||||||
|
vertex->color.y = 1.0f;
|
||||||
|
vertex->color.z = 1.0f;
|
||||||
|
vertex->color.w = 1.0f;
|
||||||
|
}
|
||||||
|
else // surface->material.materialType == MATERIAL_TYPE_COLOUR
|
||||||
|
{
|
||||||
|
float factor = static_cast<float>(mesh->materials.data[meshPart.index]->fbx.diffuse_factor.value_real);
|
||||||
|
ufbx_vec4 diffuse = mesh->materials.data[meshPart.index]->fbx.diffuse_color.value_vec4;
|
||||||
|
vertex->color.x = static_cast<float>(diffuse.x * factor);
|
||||||
|
vertex->color.y = static_cast<float>(diffuse.y * factor);
|
||||||
|
vertex->color.z = static_cast<float>(diffuse.z * factor);
|
||||||
|
vertex->color.w = static_cast<float>(diffuse.w * factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.0f - uv.y reason: https://gamedev.stackexchange.com/questions/92886/fbx-uv-coordinates-is-strange
|
||||||
|
ufbx_vec2 uv = ufbx_get_vertex_vec2(&mesh->vertex_uv, index);
|
||||||
|
vertex->texCoord.x = static_cast<float>(uv.x);
|
||||||
|
vertex->texCoord.y = static_cast<float>(1.0f - uv.y);
|
||||||
|
|
||||||
|
ufbx_vec3 normal = ufbx_get_vertex_vec3(&mesh->vertex_normal, index);
|
||||||
|
vertex->normal.x = static_cast<float>(normal.x);
|
||||||
|
vertex->normal.y = static_cast<float>(normal.y);
|
||||||
|
vertex->normal.z = static_cast<float>(normal.z);
|
||||||
|
|
||||||
|
if (mesh->vertex_tangent.exists)
|
||||||
|
{
|
||||||
|
ufbx_vec3 tangent = ufbx_get_vertex_vec3(&mesh->vertex_tangent, index);
|
||||||
|
vertex->tangent.x = static_cast<float>(tangent.x);
|
||||||
|
vertex->tangent.y = static_cast<float>(tangent.y);
|
||||||
|
vertex->tangent.z = static_cast<float>(tangent.z);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vertex->tangent.x = 0.0f;
|
||||||
|
vertex->tangent.y = 0.0f;
|
||||||
|
vertex->tangent.z = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the index buffer.
|
||||||
|
// ufbx_generate_indices will deduplicate vertices, modifying the arrays passed in streams,
|
||||||
|
// indices are written to outIndices and the number of unique vertices is returned.
|
||||||
|
ufbx_vertex_stream streams[1] = {
|
||||||
|
{vertices.data(), vertices.size(), sizeof(BSPVertex)},
|
||||||
|
};
|
||||||
|
std::vector<uint32_t> outIndices;
|
||||||
|
outIndices.resize(partTriangleNum * 3);
|
||||||
|
size_t numGeneratedVertices = ufbx_generate_indices(streams, 1, outIndices.data(), outIndices.size(), nullptr, nullptr);
|
||||||
|
assert(numGeneratedVertices != 0);
|
||||||
|
|
||||||
|
// trim non-unique vertexes and add to the world vertex vector
|
||||||
|
vertices.resize(numGeneratedVertices);
|
||||||
|
vertexVec.insert(vertexVec.end(), vertices.begin(), vertices.end());
|
||||||
|
|
||||||
|
// T6 uses unsigned shorts as their index type so we have to loop and convert them from an unsigned int
|
||||||
|
for (size_t idx = 0; idx < outIndices.size(); idx++)
|
||||||
|
{
|
||||||
|
indexVec.emplace_back(static_cast<uint16_t>(outIndices[idx]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadWorldData(ufbx_scene* scene, BSPData* bsp, bool isGfxData)
|
||||||
|
{
|
||||||
|
bool hasTangentSpace = true;
|
||||||
|
for (ufbx_node* node : scene->nodes)
|
||||||
|
{
|
||||||
|
if (node->attrib_type == UFBX_ELEMENT_MESH)
|
||||||
|
{
|
||||||
|
if (isGfxData)
|
||||||
|
addFBXMeshToWorld(node, bsp->gfxWorld.surfaces, bsp->gfxWorld.vertices, bsp->gfxWorld.indices, hasTangentSpace);
|
||||||
|
else
|
||||||
|
addFBXMeshToWorld(node, bsp->colWorld.surfaces, bsp->colWorld.vertices, bsp->colWorld.indices, hasTangentSpace);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
con::debug("ignoring node type {}: {}", static_cast<int>(node->attrib_type), node->name.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTangentSpace == false)
|
||||||
|
con::warn("warning: one or more meshes have no tangent space. Be sure to select the tangent space box when exporting the FBX.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
std::unique_ptr<BSPData> createBSPData(std::string& mapName, ISearchPath& searchPath)
|
||||||
|
{
|
||||||
|
std::string gfxFbxPath = "BSP/map_gfx.fbx";
|
||||||
|
auto gfxFile = searchPath.Open(gfxFbxPath);
|
||||||
|
if (!gfxFile.IsOpen())
|
||||||
|
{
|
||||||
|
con::error("Failed to open map gfx fbx file: {}", gfxFbxPath);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<char> gfxMapData(new char[static_cast<size_t>(gfxFile.m_length)]);
|
||||||
|
gfxFile.m_stream->read(gfxMapData.get(), gfxFile.m_length);
|
||||||
|
if (gfxFile.m_stream->gcount() != gfxFile.m_length)
|
||||||
|
{
|
||||||
|
con::error("Read error of gfx fbx file: {}", gfxFbxPath);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ufbx_error errorGfx;
|
||||||
|
ufbx_load_opts optsGfx {};
|
||||||
|
optsGfx.target_axes = ufbx_axes_right_handed_y_up;
|
||||||
|
optsGfx.generate_missing_normals = true;
|
||||||
|
optsGfx.allow_missing_vertex_position = false;
|
||||||
|
ufbx_scene* gfxScene = ufbx_load_memory(gfxMapData.get(), static_cast<size_t>(gfxFile.m_length), &optsGfx, &errorGfx);
|
||||||
|
if (!gfxScene)
|
||||||
|
{
|
||||||
|
con::error("Failed to load map gfx fbx file: {}", errorGfx.description.data);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ufbx_scene* colScene;
|
||||||
|
std::string colFbxPath = "BSP/map_col.fbx";
|
||||||
|
auto colFile = searchPath.Open(colFbxPath);
|
||||||
|
if (!colFile.IsOpen())
|
||||||
|
{
|
||||||
|
con::warn("Failed to open map collison fbx file: {}. map gfx will be used for collision instead.", colFbxPath);
|
||||||
|
colScene = gfxScene;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::unique_ptr<char> colMapData(new char[static_cast<size_t>(colFile.m_length)]);
|
||||||
|
colFile.m_stream->seekg(0);
|
||||||
|
colFile.m_stream->read(colMapData.get(), colFile.m_length);
|
||||||
|
if (colFile.m_stream->gcount() != colFile.m_length)
|
||||||
|
{
|
||||||
|
con::error("Read error of collision fbx file: {}", colFbxPath);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ufbx_error errorCol;
|
||||||
|
ufbx_load_opts optsCol {};
|
||||||
|
optsCol.target_axes = ufbx_axes_right_handed_y_up;
|
||||||
|
optsCol.generate_missing_normals = true;
|
||||||
|
optsCol.allow_missing_vertex_position = false;
|
||||||
|
colScene = ufbx_load_memory(colMapData.get(), static_cast<size_t>(colFile.m_length), &optsCol, &errorCol);
|
||||||
|
if (!colScene)
|
||||||
|
{
|
||||||
|
con::error("Failed to load map collision fbx file: {}", errorCol.description.data);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<BSPData> bsp;
|
||||||
|
|
||||||
|
bsp->name = mapName;
|
||||||
|
bsp->bspName = "maps/mp/" + mapName + ".d3dbsp";
|
||||||
|
|
||||||
|
loadWorldData(gfxScene, bsp.get(), true);
|
||||||
|
loadWorldData(colScene, bsp.get(), false);
|
||||||
|
|
||||||
|
ufbx_free_scene(gfxScene);
|
||||||
|
if (gfxScene != colScene)
|
||||||
|
ufbx_free_scene(colScene);
|
||||||
|
|
||||||
|
return bsp;
|
||||||
|
}
|
||||||
|
} // namespace BSP
|
||||||
9
src/ObjLoading/Game/T6/BSP/BSPCreator.h
Normal file
9
src/ObjLoading/Game/T6/BSP/BSPCreator.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BSP.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
std::unique_ptr<BSPData> createBSPData(std::string& mapName, ISearchPath& searchPath);
|
||||||
|
};
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include "BSP.h"
|
||||||
|
|
||||||
#include "Game/T6/T6.h"
|
|
||||||
using namespace T6;
|
|
||||||
|
|
||||||
class BSPUtil
|
class BSPUtil
|
||||||
{
|
{
|
||||||
1987
src/ObjLoading/Game/T6/BSP/CustomMapLinker.h
Normal file
1987
src/ObjLoading/Game/T6/BSP/CustomMapLinker.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,15 @@
|
|||||||
#include "Game/T6/Maps/CustomMaps.h"
|
#include "LoaderBSP_T6.h"
|
||||||
|
|
||||||
#include "LoaderCustomMapT6.h"
|
|
||||||
#include "BSPCreator.h"
|
#include "BSPCreator.h"
|
||||||
#include "CustomMapLinker.h"
|
#include "CustomMapLinker.h"
|
||||||
|
|
||||||
#include "Game/T6/T6.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
using namespace T6;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
class CustomMapLoader final : public AssetCreator<AssetGfxWorld>
|
using namespace BSP;
|
||||||
|
|
||||||
|
class BSPLoader final : public AssetCreator<AssetGfxWorld>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CustomMapLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
BSPLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
||||||
: m_memory(memory),
|
: m_memory(memory),
|
||||||
m_search_path(searchPath),
|
m_search_path(searchPath),
|
||||||
m_zone(zone)
|
m_zone(zone)
|
||||||
@@ -25,21 +19,21 @@ namespace
|
|||||||
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
|
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
|
||||||
{
|
{
|
||||||
// custom maps must have a map_gfx file
|
// custom maps must have a map_gfx file
|
||||||
auto mapGfxFile = m_search_path.Open("custom_map/map_gfx.fbx");
|
auto mapGfxFile = m_search_path.Open("BSP/map_gfx.fbx");
|
||||||
if (!mapGfxFile.IsOpen())
|
if (!mapGfxFile.IsOpen())
|
||||||
return AssetCreationResult::NoAction();
|
return AssetCreationResult::NoAction();
|
||||||
|
|
||||||
CustomMapBSP* mapBSP = BSPCreator::createCustomMapBSP(m_zone.m_name, m_search_path);
|
BSPData* BSP = BSP::createBSPData(m_zone.m_name, m_search_path);
|
||||||
if (mapBSP == NULL)
|
if (BSP == nullptr)
|
||||||
return AssetCreationResult::Failure();
|
return AssetCreationResult::Failure();
|
||||||
|
|
||||||
CustomMapLinker* linker = new CustomMapLinker(m_memory, m_search_path, m_zone, context);
|
CustomMapLinker linker(m_memory, m_search_path, m_zone, context);
|
||||||
bool result = linker->linkCustomMap(mapBSP);
|
bool result = linker.linkCustomMap(BSP);
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
auto gfxWorldAsset = context.LoadDependency<AssetGfxWorld>(mapBSP->bspName);
|
auto gfxWorldAsset = context.LoadDependency<AssetGfxWorld>(BSP->bspName);
|
||||||
_ASSERT(gfxWorldAsset != NULL);
|
_ASSERT(gfxWorldAsset != nullptr);
|
||||||
return AssetCreationResult::Success(gfxWorldAsset);
|
return AssetCreationResult::Success(gfxWorldAsset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -53,10 +47,10 @@ namespace
|
|||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace custom_map
|
namespace BSP
|
||||||
{
|
{
|
||||||
std::unique_ptr<AssetCreator<AssetGfxWorld>> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
std::unique_ptr<AssetCreator<AssetGfxWorld>> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
||||||
{
|
{
|
||||||
return std::make_unique<CustomMapLoader>(memory, searchPath, zone);
|
return std::make_unique<BSPLoader>(memory, searchPath, zone);
|
||||||
}
|
}
|
||||||
} // namespace custom_map
|
} // namespace BSP
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace custom_map
|
namespace BSP
|
||||||
{
|
{
|
||||||
std::unique_ptr<AssetCreator<T6::AssetGfxWorld>> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
|
std::unique_ptr<AssetCreator<T6::AssetGfxWorld>> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
|
||||||
} // namespace custom_map
|
} // namespace BSP
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
#include "BSPCalculation.h"
|
|
||||||
|
|
||||||
|
|
||||||
BSPObject::BSPObject(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax, int objPartitionIndex)
|
|
||||||
{
|
|
||||||
min.x = xMin;
|
|
||||||
min.y = yMin;
|
|
||||||
min.z = zMin;
|
|
||||||
max.x = xMax;
|
|
||||||
max.y = yMax;
|
|
||||||
max.z = zMax;
|
|
||||||
partitionIndex = objPartitionIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BSPLeaf::addObject(std::shared_ptr<BSPObject> object)
|
|
||||||
{
|
|
||||||
objectList.emplace_back(std::move(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
BSPObject* BSPLeaf::getObject(int index)
|
|
||||||
{
|
|
||||||
return objectList.at(index).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
int BSPLeaf::getObjectCount()
|
|
||||||
{
|
|
||||||
return objectList.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
BSPNode::BSPNode(std::unique_ptr<BSPTree> frontTree, std::unique_ptr<BSPTree> backTree, PlaneAxis nodeAxis, float nodeDistance)
|
|
||||||
{
|
|
||||||
front = std::move(frontTree);
|
|
||||||
back = std::move(backTree);
|
|
||||||
axis = nodeAxis;
|
|
||||||
distance = nodeDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlaneSide BSPNode::objectIsInfront(BSPObject* object)
|
|
||||||
{
|
|
||||||
float minCoord, maxCoord;
|
|
||||||
|
|
||||||
// Select the relevant coordinate based on the plane's axis
|
|
||||||
if (axis == AXIS_X)
|
|
||||||
{
|
|
||||||
minCoord = object->min.x;
|
|
||||||
maxCoord = object->max.x;
|
|
||||||
}
|
|
||||||
else if (axis == AXIS_Y)
|
|
||||||
{
|
|
||||||
minCoord = object->min.y;
|
|
||||||
maxCoord = object->max.y;
|
|
||||||
}
|
|
||||||
else // axis == AXIS_Z
|
|
||||||
{
|
|
||||||
minCoord = object->min.z;
|
|
||||||
maxCoord = object->max.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare with the plane's distance
|
|
||||||
if (maxCoord < distance)
|
|
||||||
{
|
|
||||||
return SIDE_BACK; // Object is entirely on the negative side
|
|
||||||
}
|
|
||||||
else if (minCoord > distance)
|
|
||||||
{
|
|
||||||
return SIDE_FRONT; // Object is entirely on the positive side
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return SIDE_INTERSECTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BSPTree::BSPTree(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax, int treeLevel)
|
|
||||||
{
|
|
||||||
min.x = xMin;
|
|
||||||
min.y = yMin;
|
|
||||||
min.z = zMin;
|
|
||||||
max.x = xMax;
|
|
||||||
max.y = yMax;
|
|
||||||
max.z = zMax;
|
|
||||||
level = treeLevel;
|
|
||||||
splitTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
// For simplicity, only split across the X and Z axis.
|
|
||||||
// It is unlikely that there are many layers to a map, and is instead mostly flat
|
|
||||||
void BSPTree::splitTree()
|
|
||||||
{
|
|
||||||
std::unique_ptr<BSPTree> front;
|
|
||||||
std::unique_ptr<BSPTree> back;
|
|
||||||
float halfLength;
|
|
||||||
|
|
||||||
if (max.x - min.x > MAX_NODE_SIZE)
|
|
||||||
{
|
|
||||||
// split along the x axis
|
|
||||||
halfLength = (min.x + max.x) * 0.5f;
|
|
||||||
front = std::make_unique<BSPTree>(halfLength, min.y, min.z, max.x, max.y, max.z, level + 1);
|
|
||||||
back = std::make_unique<BSPTree>(min.x, min.y, min.z, halfLength, max.y, max.z, level + 1);
|
|
||||||
|
|
||||||
isLeaf = false;
|
|
||||||
node = std::make_unique<BSPNode>(std::move(front), std::move(back), AXIS_X, halfLength);
|
|
||||||
leaf = nullptr;
|
|
||||||
}
|
|
||||||
else if (max.z - min.z > MAX_NODE_SIZE)
|
|
||||||
{
|
|
||||||
// split along the z axis
|
|
||||||
halfLength = (min.z + max.z) * 0.5f;
|
|
||||||
front = std::make_unique<BSPTree>(min.x, min.y, halfLength, max.x, max.y, max.z, level + 1);
|
|
||||||
back = std::make_unique<BSPTree>(min.x, min.y, min.z, max.x, max.y, halfLength, level + 1);
|
|
||||||
|
|
||||||
isLeaf = false;
|
|
||||||
node = std::make_unique<BSPNode>(std::move(front), std::move(back), AXIS_Z, halfLength);
|
|
||||||
leaf = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
isLeaf = true;
|
|
||||||
node = nullptr;
|
|
||||||
leaf = std::make_unique<BSPLeaf>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BSPTree::addObjectToTree(std::shared_ptr<BSPObject> object)
|
|
||||||
{
|
|
||||||
if (isLeaf)
|
|
||||||
{
|
|
||||||
leaf->addObject(std::move(object));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PlaneSide side = node->objectIsInfront(object.get());
|
|
||||||
if (side == SIDE_FRONT)
|
|
||||||
{
|
|
||||||
node->front->addObjectToTree(std::move(object));
|
|
||||||
}
|
|
||||||
else if (side == SIDE_BACK)
|
|
||||||
{
|
|
||||||
node->back->addObjectToTree(std::move(object));
|
|
||||||
}
|
|
||||||
else // intersects
|
|
||||||
{
|
|
||||||
node->front->addObjectToTree(object);
|
|
||||||
node->back->addObjectToTree(object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Game/T6/T6.h"
|
|
||||||
using namespace T6;
|
|
||||||
|
|
||||||
constexpr int MAX_NODE_SIZE = 512; // maximum size a BSP node can be before it becomes a leaf
|
|
||||||
|
|
||||||
enum PlaneAxis
|
|
||||||
{
|
|
||||||
AXIS_X,
|
|
||||||
AXIS_Y,
|
|
||||||
AXIS_Z
|
|
||||||
};
|
|
||||||
|
|
||||||
enum PlaneSide
|
|
||||||
{
|
|
||||||
SIDE_FRONT,
|
|
||||||
SIDE_BACK,
|
|
||||||
SIDE_INTERSECTS
|
|
||||||
};
|
|
||||||
|
|
||||||
class BSPObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
vec3_t min;
|
|
||||||
vec3_t max;
|
|
||||||
int partitionIndex; // index of the partition the object is contained in
|
|
||||||
|
|
||||||
BSPObject(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax, int objPartitionIndex);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class BSPLeaf
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::vector<std::shared_ptr<BSPObject>> objectList;
|
|
||||||
|
|
||||||
void addObject(std::shared_ptr<BSPObject> object);
|
|
||||||
BSPObject* getObject(int index);
|
|
||||||
int getObjectCount();
|
|
||||||
};
|
|
||||||
|
|
||||||
class BSPTree;
|
|
||||||
|
|
||||||
class BSPNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::unique_ptr<BSPTree> front;
|
|
||||||
std::unique_ptr<BSPTree> back;
|
|
||||||
|
|
||||||
PlaneAxis axis; // axis that the split plane is on
|
|
||||||
float distance; // distance from the origin (0, 0, 0) to the plane
|
|
||||||
|
|
||||||
BSPNode(std::unique_ptr<BSPTree> frontTree, std::unique_ptr<BSPTree> backTree, PlaneAxis nodeAxis, float nodeDistance);
|
|
||||||
PlaneSide objectIsInfront(BSPObject* object);
|
|
||||||
};
|
|
||||||
|
|
||||||
class BSPTree
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool isLeaf;
|
|
||||||
std::unique_ptr<BSPLeaf> leaf;
|
|
||||||
std::unique_ptr<BSPNode> node;
|
|
||||||
|
|
||||||
int level; // level in the BSP tree
|
|
||||||
vec3_t min;
|
|
||||||
vec3_t max;
|
|
||||||
|
|
||||||
BSPTree(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax, int treeLevel);
|
|
||||||
void splitTree();
|
|
||||||
void addObjectToTree(std::shared_ptr<BSPObject> object);
|
|
||||||
};
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// BSPGameConstants:
|
|
||||||
// These values are hardcoded ingame and will break the map if they are changed
|
|
||||||
namespace BSPGameConstants
|
|
||||||
{
|
|
||||||
constexpr int MAX_COLLISION_VERTS = UINT16_MAX;
|
|
||||||
|
|
||||||
constexpr int STATIC_LIGHT_INDEX = 0;
|
|
||||||
constexpr int SUN_LIGHT_INDEX = 1;
|
|
||||||
|
|
||||||
inline const char* DEFENDER_SPAWN_POINT_NAMES[] = {
|
|
||||||
"mp_ctf_spawn_allies",
|
|
||||||
"mp_ctf_spawn_allies_start",
|
|
||||||
"mp_sd_spawn_defender",
|
|
||||||
"mp_dom_spawn_allies_start",
|
|
||||||
"mp_dem_spawn_defender_start",
|
|
||||||
"mp_dem_spawn_defenderOT_start",
|
|
||||||
"mp_dem_spawn_defender",
|
|
||||||
"mp_tdm_spawn_allies_start",
|
|
||||||
"mp_tdm_spawn_team1_start",
|
|
||||||
"mp_tdm_spawn_team2_start",
|
|
||||||
"mp_tdm_spawn_team3_start"
|
|
||||||
};
|
|
||||||
|
|
||||||
inline const char* ATTACKER_SPAWN_POINT_NAMES[] = {
|
|
||||||
"mp_ctf_spawn_axis",
|
|
||||||
"mp_ctf_spawn_axis_start",
|
|
||||||
"mp_sd_spawn_attacker",
|
|
||||||
"mp_dom_spawn_axis_start",
|
|
||||||
"mp_dem_spawn_attacker_start",
|
|
||||||
"mp_dem_spawn_attackerOT_start",
|
|
||||||
"mp_dem_spawn_defender",
|
|
||||||
"mp_tdm_spawn_axis_start",
|
|
||||||
"mp_tdm_spawn_team4_start",
|
|
||||||
"mp_tdm_spawn_team5_start",
|
|
||||||
"mp_tdm_spawn_team6_start"
|
|
||||||
};
|
|
||||||
|
|
||||||
inline const char* FFA_SPAWN_POINT_NAMES[] = {
|
|
||||||
"mp_tdm_spawn",
|
|
||||||
"mp_dm_spawn",
|
|
||||||
"mp_dom_spawn"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// BSPLinkingConstants:
|
|
||||||
// These values are BSP linking constants that are required for the link to be successful
|
|
||||||
namespace BSPLinkingConstants
|
|
||||||
{
|
|
||||||
constexpr const char* MISSING_IMAGE_NAME = "missing_image";
|
|
||||||
constexpr const char* COLOR_ONLY_IMAGE_NAME = "color_only_image";
|
|
||||||
|
|
||||||
constexpr const char* DEFAULT_SPAWN_POINT_STRING = R"({
|
|
||||||
"attackers": [
|
|
||||||
{
|
|
||||||
"origin": "0 0 0",
|
|
||||||
"angles": "0 0 0"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"defenders": [
|
|
||||||
{
|
|
||||||
"origin": "0 0 0",
|
|
||||||
"angles": "0 0 0"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"FFA": [
|
|
||||||
{
|
|
||||||
"origin": "0 0 0",
|
|
||||||
"angles": "0 0 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})";
|
|
||||||
|
|
||||||
|
|
||||||
constexpr const char* DEFAULT_MAP_ENTS_STRING = R"({
|
|
||||||
"entities": [
|
|
||||||
{
|
|
||||||
"classname": "worldspawn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"angles": "0 0 0",
|
|
||||||
"classname": "info_player_start",
|
|
||||||
"origin": "0 0 0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"angles": "0 0 0",
|
|
||||||
"classname": "mp_global_intermission",
|
|
||||||
"origin": "0 0 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})";
|
|
||||||
}
|
|
||||||
|
|
||||||
// BSPEditableConstants:
|
|
||||||
// These values are BSP constants that can be edited and won't break the linker/game if changed
|
|
||||||
namespace BSPEditableConstants
|
|
||||||
{
|
|
||||||
// Default xmodel values
|
|
||||||
// Unused as there is no support for xmodels right now
|
|
||||||
constexpr float DEFAULT_SMODEL_CULL_DIST = 10000.0f;
|
|
||||||
constexpr int DEFAULT_SMODEL_FLAGS = STATIC_MODEL_FLAG_NO_SHADOW;
|
|
||||||
constexpr int DEFAULT_SMODEL_LIGHT = 1;
|
|
||||||
constexpr int DEFAULT_SMODEL_REFLECTION_PROBE = 0;
|
|
||||||
|
|
||||||
// Default surface values
|
|
||||||
constexpr int DEFAULT_SURFACE_LIGHT = BSPGameConstants::SUN_LIGHT_INDEX;
|
|
||||||
constexpr int DEFAULT_SURFACE_LIGHTMAP = 0;
|
|
||||||
constexpr int DEFAULT_SURFACE_REFLECTION_PROBE = 0;
|
|
||||||
constexpr int DEFAULT_SURFACE_FLAGS = (GFX_SURFACE_CASTS_SUN_SHADOW | GFX_SURFACE_CASTS_SHADOW);
|
|
||||||
|
|
||||||
// material flags determine the features of a surface
|
|
||||||
// unsure which flag type changes what right now
|
|
||||||
// -1 results in: no running, water splashes all the time, low friction, slanted angles make you slide very fast
|
|
||||||
// 1 results in: normal surface features, grenades work, seems normal
|
|
||||||
constexpr int MATERIAL_SURFACE_FLAGS = 1;
|
|
||||||
constexpr int MATERIAL_CONTENT_FLAGS = 1;
|
|
||||||
|
|
||||||
// terrain/world flags: does not change the type of terrain or what features they have
|
|
||||||
// from testing, as long at it isn't 0 things will work correctly
|
|
||||||
constexpr int LEAF_TERRAIN_CONTENTS = 1;
|
|
||||||
constexpr int WORLD_TERRAIN_CONTENTS = 1;
|
|
||||||
|
|
||||||
// lightgrid (global) lighting colour
|
|
||||||
// since lightgrids are not well understood, this colour is used for the R, G and B values right now
|
|
||||||
constexpr unsigned char LIGHTGRID_COLOUR = 128;
|
|
||||||
};
|
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
#include "BSPCreator.h"
|
|
||||||
|
|
||||||
#include "fbx/ufbx.h"
|
|
||||||
#include "Game/T6/T6.h"
|
|
||||||
using namespace T6;
|
|
||||||
|
|
||||||
bool addFBXMeshToWorld(ufbx_node* node,
|
|
||||||
std::vector<CustomMapSurface>& surfaceVec,
|
|
||||||
std::vector<CustomMapVertex>& vertexVec,
|
|
||||||
std::vector<uint16_t>& indexVec,
|
|
||||||
bool& hasTangentSpace)
|
|
||||||
{
|
|
||||||
if (node->attrib_type != UFBX_ELEMENT_MESH)
|
|
||||||
{
|
|
||||||
printf("ignoring non-mesh node \"%s\"\n", node->name.data);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ufbx_mesh* mesh = node->mesh;
|
|
||||||
|
|
||||||
if (mesh->instances.count != 1)
|
|
||||||
printf("mesh %s has %i instances, only the 1st instace will be used.\n", node->name.data, mesh->instances.count);
|
|
||||||
|
|
||||||
if (mesh->num_triangles == 0)
|
|
||||||
{
|
|
||||||
printf("ignoring mesh %s: triangle count is 0.\n", node->name.data);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mesh->num_indices % 3 != 0)
|
|
||||||
{
|
|
||||||
printf("ignoring mesh %s: it is not triangulated.\n", node->name.data);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t k = 0; k < mesh->num_indices; k++)
|
|
||||||
{
|
|
||||||
if (mesh->vertex_indices[k] > UINT16_MAX)
|
|
||||||
{
|
|
||||||
printf("ignoring mesh %s, it has more than %i indices.\n", node->name.data, UINT16_MAX);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mesh->vertex_tangent.exists == false)
|
|
||||||
hasTangentSpace = false;
|
|
||||||
|
|
||||||
// Fix the target_unit_meters opt not working
|
|
||||||
// UFBX stores the transform data in units that are 100x larger than what blender uses, so this converts them back
|
|
||||||
ufbx_transform origTransform = node->local_transform;
|
|
||||||
origTransform.translation.x /= 100.0f;
|
|
||||||
origTransform.translation.y /= 100.0f;
|
|
||||||
origTransform.translation.z /= 100.0f;
|
|
||||||
origTransform.scale.x /= 100.0f;
|
|
||||||
origTransform.scale.y /= 100.0f;
|
|
||||||
origTransform.scale.z /= 100.0f;
|
|
||||||
ufbx_matrix meshMatrix = ufbx_transform_to_matrix(&origTransform);
|
|
||||||
|
|
||||||
// FBX loading code modified from https://ufbx.github.io/elements/meshes/
|
|
||||||
// Seems like I have to use this exact way of loading, otherwise some values become incorrect
|
|
||||||
for (size_t i = 0; i < mesh->material_parts.count; i++)
|
|
||||||
{
|
|
||||||
ufbx_mesh_part* meshPart = &mesh->material_parts.data[i];
|
|
||||||
|
|
||||||
if (meshPart->num_faces == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
CustomMapSurface surface;
|
|
||||||
surface.triCount = meshPart->num_triangles;
|
|
||||||
surface.indexOfFirstVertex = vertexVec.size();
|
|
||||||
surface.indexOfFirstIndex = indexVec.size();
|
|
||||||
|
|
||||||
CustomMapMaterialType meshMaterialType;
|
|
||||||
if (mesh->materials.count == 0)
|
|
||||||
{
|
|
||||||
meshMaterialType = MATERIAL_TYPE_EMPTY;
|
|
||||||
}
|
|
||||||
//else if (mesh->materials.data[i]->textures.count != 0)
|
|
||||||
//{
|
|
||||||
// meshMaterialType = CM_MATERIAL_TEXTURE;
|
|
||||||
// surface.material.materialName = _strdup(mesh->materials.data[i]->name.data);
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// meshMaterialType = CM_MATERIAL_COLOUR;
|
|
||||||
// surface.material.materialName = "";
|
|
||||||
//}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
meshMaterialType = MATERIAL_TYPE_TEXTURE;
|
|
||||||
surface.material.materialName = _strdup(mesh->materials.data[i]->name.data);
|
|
||||||
}
|
|
||||||
surface.material.materialType = meshMaterialType;
|
|
||||||
|
|
||||||
size_t num_triangles = meshPart->num_triangles;
|
|
||||||
CustomMapVertex* vertices = (CustomMapVertex*)calloc(num_triangles * 3, sizeof(CustomMapVertex));
|
|
||||||
size_t num_vertices = 0;
|
|
||||||
|
|
||||||
// Reserve space for the maximum triangle indices.
|
|
||||||
size_t num_tri_indices = mesh->max_face_triangles * 3;
|
|
||||||
uint32_t* tri_indices = (uint32_t*)calloc(num_tri_indices, sizeof(uint32_t));
|
|
||||||
|
|
||||||
_ASSERT(meshPart->num_triangles == meshPart->num_faces);
|
|
||||||
for (size_t face_ix = 0; face_ix < meshPart->num_faces; face_ix++)
|
|
||||||
{
|
|
||||||
ufbx_face face = mesh->faces.data[meshPart->face_indices.data[face_ix]];
|
|
||||||
|
|
||||||
// Triangulate the face into `tri_indices[]`.
|
|
||||||
uint32_t num_tris = ufbx_triangulate_face(tri_indices, num_tri_indices, mesh, face);
|
|
||||||
|
|
||||||
// Iterate over each triangle corner contiguously.
|
|
||||||
for (size_t q = 0; q < num_tris * 3; q++)
|
|
||||||
{
|
|
||||||
uint32_t index = tri_indices[q];
|
|
||||||
|
|
||||||
CustomMapVertex* vertex = &vertices[num_vertices++];
|
|
||||||
|
|
||||||
//ufbx_vec3 pos = ufbx_get_vertex_vec3(&mesh->vertex_position, index);
|
|
||||||
//vertex->pos.x = static_cast<float>(pos.x);
|
|
||||||
//vertex->pos.y = static_cast<float>(pos.y);
|
|
||||||
//vertex->pos.z = static_cast<float>(pos.z);
|
|
||||||
// Fix the target_unit_meters opt not working
|
|
||||||
ufbx_vec3 transformedPos = ufbx_transform_position(&meshMatrix, ufbx_get_vertex_vec3(&mesh->vertex_position, index));
|
|
||||||
vertex->pos.x = static_cast<float>(transformedPos.x);
|
|
||||||
vertex->pos.y = static_cast<float>(transformedPos.y);
|
|
||||||
vertex->pos.z = static_cast<float>(transformedPos.z);
|
|
||||||
|
|
||||||
|
|
||||||
switch (meshMaterialType)
|
|
||||||
{
|
|
||||||
case MATERIAL_TYPE_TEXTURE:
|
|
||||||
case MATERIAL_TYPE_EMPTY:
|
|
||||||
vertex->color.x = 1.0f;
|
|
||||||
vertex->color.y = 1.0f;
|
|
||||||
vertex->color.z = 1.0f;
|
|
||||||
vertex->color.w = 1.0f;
|
|
||||||
break;
|
|
||||||
case MATERIAL_TYPE_COLOUR:
|
|
||||||
{
|
|
||||||
float factor = static_cast<float>(mesh->materials.data[i]->fbx.diffuse_factor.value_real);
|
|
||||||
ufbx_vec4 diffuse = mesh->materials.data[i]->fbx.diffuse_color.value_vec4;
|
|
||||||
vertex->color.x = static_cast<float>(diffuse.x * factor);
|
|
||||||
vertex->color.y = static_cast<float>(diffuse.y * factor);
|
|
||||||
vertex->color.z = static_cast<float>(diffuse.z * factor);
|
|
||||||
vertex->color.w = static_cast<float>(diffuse.w * factor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
_ASSERT(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 1.0f - uv.y reason:
|
|
||||||
// https://gamedev.stackexchange.com/questions/92886/fbx-uv-coordinates-is-strange
|
|
||||||
ufbx_vec2 uv = ufbx_get_vertex_vec2(&mesh->vertex_uv, index);
|
|
||||||
vertex->texCoord.x = (float)(uv.x);
|
|
||||||
vertex->texCoord.y = (float)(1.0f - uv.y);
|
|
||||||
|
|
||||||
ufbx_vec3 normal = ufbx_get_vertex_vec3(&mesh->vertex_normal, index);
|
|
||||||
vertex->normal.x = static_cast<float>(normal.x);
|
|
||||||
vertex->normal.y = static_cast<float>(normal.y);
|
|
||||||
vertex->normal.z = static_cast<float>(normal.z);
|
|
||||||
|
|
||||||
if (mesh->vertex_tangent.exists)
|
|
||||||
{
|
|
||||||
ufbx_vec3 tangent = ufbx_get_vertex_vec3(&mesh->vertex_tangent, index);
|
|
||||||
vertex->tangent.x = static_cast<float>(tangent.x);
|
|
||||||
vertex->tangent.y = static_cast<float>(tangent.y);
|
|
||||||
vertex->tangent.z = static_cast<float>(tangent.z);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vertex->tangent.x = 0.0f;
|
|
||||||
vertex->tangent.y = 0.0f;
|
|
||||||
vertex->tangent.z = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ASSERT(num_vertices == num_triangles * 3);
|
|
||||||
|
|
||||||
// Generate the index buffer.
|
|
||||||
ufbx_vertex_stream streams[1] = {
|
|
||||||
{vertices, num_vertices, sizeof(CustomMapVertex)},
|
|
||||||
};
|
|
||||||
size_t num_indices = num_triangles * 3;
|
|
||||||
uint32_t* indices = (uint32_t*)calloc(num_indices, sizeof(uint32_t));
|
|
||||||
|
|
||||||
// This call will deduplicate vertices, modifying the arrays passed in `streams[]`,
|
|
||||||
// indices are written in `indices[]` and the number of unique vertices is returned.
|
|
||||||
num_vertices = ufbx_generate_indices(streams, 1, indices, num_indices, NULL, NULL);
|
|
||||||
_ASSERT(num_vertices != 0);
|
|
||||||
|
|
||||||
vertexVec.insert(vertexVec.end(), &vertices[0], &vertices[num_vertices]);
|
|
||||||
|
|
||||||
size_t currIndexVecSize = indexVec.size();
|
|
||||||
indexVec.resize(indexVec.size() + num_indices);
|
|
||||||
for (size_t m = 0; m < num_indices; m++)
|
|
||||||
{
|
|
||||||
indexVec[currIndexVecSize + m] = (uint16_t)indices[m];
|
|
||||||
}
|
|
||||||
|
|
||||||
surfaceVec.push_back(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadWorldData(ufbx_scene* scene, CustomMapBSP* bsp, bool isGfxData)
|
|
||||||
{
|
|
||||||
bool hasTangentSpace = true;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < scene->nodes.count; i++)
|
|
||||||
{
|
|
||||||
ufbx_node* node = scene->nodes.data[i];
|
|
||||||
|
|
||||||
if (node->attrib_type == UFBX_ELEMENT_MESH)
|
|
||||||
{
|
|
||||||
if (isGfxData)
|
|
||||||
addFBXMeshToWorld(node, bsp->gfxWorld.surfaces, bsp->gfxWorld.vertices, bsp->gfxWorld.indices, hasTangentSpace);
|
|
||||||
else
|
|
||||||
addFBXMeshToWorld(node, bsp->colWorld.surfaces, bsp->colWorld.vertices, bsp->colWorld.indices, hasTangentSpace);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//printf("ignoring node type %i: %s\n", node->attrib_type, node->name.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasTangentSpace == false)
|
|
||||||
printf("warning: one or more meshes have no tangent space. Be sure to select the tangent space box when exporting the FBX from blender.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomMapBSP* BSPCreator::createCustomMapBSP(std::string& mapName, ISearchPath& searchPath)
|
|
||||||
{
|
|
||||||
ufbx_scene* gfxScene;
|
|
||||||
ufbx_scene* colScene;
|
|
||||||
|
|
||||||
std::string gfxFbxPath = "custom_map/map_gfx.fbx";
|
|
||||||
auto gfxFile = searchPath.Open(gfxFbxPath);
|
|
||||||
if (!gfxFile.IsOpen())
|
|
||||||
{
|
|
||||||
printf("Failed to open map gfx fbx file: %s\n", gfxFbxPath.c_str());
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* gfxMapData = new char[static_cast<unsigned int>(gfxFile.m_length)];
|
|
||||||
gfxFile.m_stream->seekg(0);
|
|
||||||
gfxFile.m_stream->read(gfxMapData, gfxFile.m_length);
|
|
||||||
|
|
||||||
ufbx_error error;
|
|
||||||
ufbx_load_opts opts; // IDK why but opts don't seem to be working correctly, target_unit_meters isn't being used
|
|
||||||
memset(&opts, 0, sizeof(ufbx_load_opts));
|
|
||||||
opts.target_axes = ufbx_axes_right_handed_y_up;
|
|
||||||
opts.generate_missing_normals = true;
|
|
||||||
opts.allow_missing_vertex_position = false;
|
|
||||||
opts.target_unit_meters = 100.0f;
|
|
||||||
//gfxScene = ufbx_load_memory(gfxMapData, static_cast<size_t>(gfxFile.m_length), &opts, &error);
|
|
||||||
gfxScene = ufbx_load_memory(gfxMapData, static_cast<size_t>(gfxFile.m_length), NULL, &error);
|
|
||||||
if (!gfxScene)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to load map gfx fbx file: %s\n", error.description.data);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string colFbxPath = "custom_map/map_col.fbx";
|
|
||||||
auto colFile = searchPath.Open(colFbxPath);
|
|
||||||
if (!colFile.IsOpen())
|
|
||||||
{
|
|
||||||
printf("Failed to open map collison fbx file: %s. map gfx will be used for collision instead.\n", colFbxPath.c_str());
|
|
||||||
colScene = gfxScene;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char* colMapData = new char[static_cast<unsigned int>(colFile.m_length)];
|
|
||||||
colFile.m_stream->seekg(0);
|
|
||||||
colFile.m_stream->read(colMapData, colFile.m_length);
|
|
||||||
|
|
||||||
colScene = ufbx_load_memory(colMapData, static_cast<size_t>(colFile.m_length), &opts, &error);
|
|
||||||
if (!colScene)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to load map collision fbx file: %s\n", error.description.data);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomMapBSP* projInfo = new CustomMapBSP;
|
|
||||||
|
|
||||||
projInfo->name = mapName;
|
|
||||||
projInfo->bspName = "maps/mp/" + mapName + ".d3dbsp";
|
|
||||||
|
|
||||||
loadWorldData(gfxScene, projInfo, true);
|
|
||||||
loadWorldData(colScene, projInfo, false);
|
|
||||||
|
|
||||||
ufbx_free_scene(gfxScene);
|
|
||||||
if (gfxScene != colScene)
|
|
||||||
ufbx_free_scene(colScene);
|
|
||||||
|
|
||||||
return projInfo;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Game/T6/Maps/CustomMaps.h"
|
|
||||||
#include "SearchPath/ISearchPath.h"
|
|
||||||
|
|
||||||
class BSPCreator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static CustomMapBSP* createCustomMapBSP(std::string& mapName, ISearchPath& searchPath);
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// These options can be edited
|
|
||||||
|
|
||||||
// material flags determine the features of the surface
|
|
||||||
// unsure which flag type changes what right now
|
|
||||||
// -1 results in: no running, water splashes all the time, low friction, slanted angles make you slide very fast
|
|
||||||
// 1 results in: normal surface features, grenades work, seems normal
|
|
||||||
#define MATERIAL_SURFACE_FLAGS 1
|
|
||||||
#define MATERIAL_CONTENT_FLAGS 1
|
|
||||||
|
|
||||||
// terrain/world flags: does not change the type of terrain or what features they have
|
|
||||||
// from testing, as long at it isn't 0 things will work correctly
|
|
||||||
#define LEAF_TERRAIN_CONTENTS 1
|
|
||||||
#define WORLD_TERRAIN_CONTENTS 1
|
|
||||||
|
|
||||||
#define LIGHTGRID_COLOUR 128 // global lighting colour
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
#include "Weapon/WeaponRawLoaderT6.h"
|
#include "Weapon/WeaponRawLoaderT6.h"
|
||||||
#include "ZBarrier/GdtLoaderZBarrierT6.h"
|
#include "ZBarrier/GdtLoaderZBarrierT6.h"
|
||||||
#include "ZBarrier/RawLoaderZBarrierT6.h"
|
#include "ZBarrier/RawLoaderZBarrierT6.h"
|
||||||
#include "CustomMap/LoaderCustomMapT6.h"
|
#include "BSP/LoaderBSP_T6.h"
|
||||||
#include "TechniqueSet/LoaderTechniqueSetT6.h"
|
#include "TechniqueSet/LoaderTechniqueSetT6.h"
|
||||||
|
|
||||||
#include <format>
|
#include <format>
|
||||||
@@ -442,7 +442,7 @@ namespace T6
|
|||||||
collection.AddAssetCreator(z_barrier::CreateRawLoaderT6(memory, searchPath, zone));
|
collection.AddAssetCreator(z_barrier::CreateRawLoaderT6(memory, searchPath, zone));
|
||||||
collection.AddAssetCreator(z_barrier::CreateGdtLoaderT6(memory, searchPath, gdt, zone));
|
collection.AddAssetCreator(z_barrier::CreateGdtLoaderT6(memory, searchPath, gdt, zone));
|
||||||
|
|
||||||
collection.AddAssetCreator(custom_map::CreateLoaderT6(memory, searchPath, zone));
|
collection.AddAssetCreator(BSP::CreateLoaderT6(memory, searchPath, zone));
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user