mirror of https://github.com/AxioDL/metaforce.git
Add VISIGen utility
This commit is contained in:
parent
50fe6d34ab
commit
ccbd19b9ea
|
@ -121,6 +121,7 @@ set(CLIENT_SOURCES
|
||||||
add_subdirectory(Runtime)
|
add_subdirectory(Runtime)
|
||||||
add_subdirectory(mpcksum)
|
add_subdirectory(mpcksum)
|
||||||
add_subdirectory(gbalink)
|
add_subdirectory(gbalink)
|
||||||
|
add_subdirectory(visigen)
|
||||||
|
|
||||||
unset(GIT_EXECUTABLE CACHE)
|
unset(GIT_EXECUTABLE CACHE)
|
||||||
find_package(Git)
|
find_package(Git)
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include "zeus/Math.hpp"
|
#include "zeus/Math.hpp"
|
||||||
#include "zeus/CAABox.hpp"
|
#include "zeus/CAABox.hpp"
|
||||||
#include "DataSpec/DNACommon/AROTBuilder.hpp"
|
#include "DataSpec/DNACommon/AROTBuilder.hpp"
|
||||||
|
#include "ScriptObjects/ScriptTypes.hpp"
|
||||||
|
|
||||||
|
extern const hecl::SystemString ExeDir;
|
||||||
|
|
||||||
namespace DataSpec
|
namespace DataSpec
|
||||||
{
|
{
|
||||||
|
@ -162,6 +165,26 @@ bool MREA::Extract(const SpecBase& dataSpec,
|
||||||
ReadBabeDeadToBlender_1_2(os, rs);
|
ReadBabeDeadToBlender_1_2(os, rs);
|
||||||
rs.seek(secStart + head.secSizes[curSec++], athena::Begin);
|
rs.seek(secStart + head.secSizes[curSec++], athena::Begin);
|
||||||
|
|
||||||
|
/* Dump VISI entities */
|
||||||
|
if (head.secSizes[curSec] && rs.readUint32Big() == 'VISI')
|
||||||
|
{
|
||||||
|
athena::io::YAMLDocWriter visiWriter("VISI");
|
||||||
|
if (auto __vec = visiWriter.enterSubVector("entities"))
|
||||||
|
{
|
||||||
|
rs.seek(18, athena::Current);
|
||||||
|
uint32_t entityCount = rs.readUint32Big();
|
||||||
|
rs.seek(8, athena::Current);
|
||||||
|
for (int i=0 ; i<entityCount ; ++i)
|
||||||
|
{
|
||||||
|
uint32_t entityId = rs.readUint32Big();
|
||||||
|
visiWriter.writeUint16(nullptr, entityId & 0xffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hecl::ProjectPath visiMetadataPath(outPath.getParentPath(), _S("!visi.yaml"));
|
||||||
|
athena::io::FileWriter visiMetadata(visiMetadataPath.getAbsolutePath());
|
||||||
|
visiWriter.finish(&visiMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
/* Origins to center of mass */
|
/* Origins to center of mass */
|
||||||
os << "bpy.context.scene.layers[1] = True\n"
|
os << "bpy.context.scene.layers[1] = True\n"
|
||||||
"bpy.ops.object.select_by_type(type='MESH')\n"
|
"bpy.ops.object.select_by_type(type='MESH')\n"
|
||||||
|
@ -251,7 +274,8 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
|
||||||
const hecl::ProjectPath& inPath,
|
const hecl::ProjectPath& inPath,
|
||||||
const std::vector<DNACMDL::Mesh>& meshes,
|
const std::vector<DNACMDL::Mesh>& meshes,
|
||||||
const ColMesh& cMesh,
|
const ColMesh& cMesh,
|
||||||
const std::vector<Light>& lights)
|
const std::vector<Light>& lights,
|
||||||
|
hecl::BlenderToken& btok)
|
||||||
{
|
{
|
||||||
/* Discover area layers */
|
/* Discover area layers */
|
||||||
hecl::ProjectPath areaDirPath = inPath.getParentPath();
|
hecl::ProjectPath areaDirPath = inPath.getParentPath();
|
||||||
|
@ -322,13 +346,13 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
|
||||||
|
|
||||||
/* AROT */
|
/* AROT */
|
||||||
{
|
{
|
||||||
AROTBuilder builder;
|
AROTBuilder arotBuilder;
|
||||||
builder.build(secs, fullAabb, meshAabbs, meshes);
|
arotBuilder.build(secs, fullAabb, meshAabbs, meshes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SCLY */
|
/* SCLY */
|
||||||
{
|
|
||||||
DNAMP1::SCLY sclyData = {};
|
DNAMP1::SCLY sclyData = {};
|
||||||
|
{
|
||||||
sclyData.fourCC = 'SCLY';
|
sclyData.fourCC = 'SCLY';
|
||||||
sclyData.version = 1;
|
sclyData.version = 1;
|
||||||
for (const hecl::ProjectPath& layer : layerScriptPaths)
|
for (const hecl::ProjectPath& layer : layerScriptPaths)
|
||||||
|
@ -373,13 +397,13 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lights */
|
/* Lights */
|
||||||
|
std::vector<atVec3f> lightsVisi;
|
||||||
{
|
{
|
||||||
int actualCount = 0;
|
int actualCount = 0;
|
||||||
for (const Light& l : lights)
|
for (const Light& l : lights)
|
||||||
{
|
|
||||||
if (l.layer == 0 || l.layer == 1)
|
if (l.layer == 0 || l.layer == 1)
|
||||||
++actualCount;
|
++actualCount;
|
||||||
}
|
lightsVisi.reserve(actualCount);
|
||||||
|
|
||||||
secs.emplace_back(12 + 65 * actualCount, 0);
|
secs.emplace_back(12 + 65 * actualCount, 0);
|
||||||
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
|
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
|
||||||
|
@ -401,14 +425,99 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
|
||||||
BabeDeadLight light = {};
|
BabeDeadLight light = {};
|
||||||
WriteBabeDeadLightFromBlender(light, l);
|
WriteBabeDeadLightFromBlender(light, l);
|
||||||
light.write(w);
|
light.write(w);
|
||||||
|
lightsVisi.push_back(light.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* VISI */
|
/* VISI */
|
||||||
|
hecl::ProjectPath visiMetadataPath(areaDirPath, _S("!visi.yaml"));
|
||||||
|
if (visiMetadataPath.isFile())
|
||||||
{
|
{
|
||||||
/* Empty (for now) */
|
bool good = false;
|
||||||
|
athena::io::FileReader visiReader(visiMetadataPath.getAbsolutePath());
|
||||||
|
athena::io::YAMLDocReader r;
|
||||||
|
if (r.parse(&visiReader))
|
||||||
|
{
|
||||||
|
size_t entityCount;
|
||||||
|
std::vector<std::pair<uint16_t, zeus::CAABox>> entities;
|
||||||
|
if (auto __vec = r.enterSubVector("entities", entityCount))
|
||||||
|
{
|
||||||
|
entities.reserve(entityCount);
|
||||||
|
uint16_t entityId = r.readUint16(nullptr);
|
||||||
|
for (const SCLY::ScriptLayer& layer : sclyData.layers)
|
||||||
|
{
|
||||||
|
for (const std::unique_ptr<IScriptObject>& obj : layer.objects)
|
||||||
|
{
|
||||||
|
if ((obj->id & 0xffff) == entityId)
|
||||||
|
{
|
||||||
|
zeus::CAABox entAABB = obj->getVISIAABB(btok);
|
||||||
|
if (entAABB.min.x < entAABB.max.x)
|
||||||
|
entities.emplace_back(entityId, entAABB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hecl::ProjectPath visiIntOut = outPath.getWithExtension(_S(".visiint"));
|
||||||
|
hecl::ProjectPath visiIn = outPath.getWithExtension(_S(".visi"));
|
||||||
|
athena::io::FileWriter w(visiIntOut.getAbsolutePath());
|
||||||
|
w.writeUint32Big(meshes.size());
|
||||||
|
for (const DNACMDL::Mesh& mesh : meshes)
|
||||||
|
{
|
||||||
|
w.writeUint32Big(uint32_t(mesh.topology));
|
||||||
|
|
||||||
|
w.writeUint32Big(mesh.pos.size());
|
||||||
|
for (const auto& v : mesh.pos)
|
||||||
|
{
|
||||||
|
atVec3f xfPos = hecl::BlenderConnection::DataStream::MtxVecMul4RM(mesh.sceneXf, v);
|
||||||
|
w.writeVec3fBig(xfPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
w.writeUint32Big(mesh.surfaces.size());
|
||||||
|
for (const DNACMDL::Mesh::Surface& surf : mesh.surfaces)
|
||||||
|
{
|
||||||
|
w.writeUint32Big(surf.verts.size());
|
||||||
|
for (const DNACMDL::Mesh::Surface::Vert& vert : surf.verts)
|
||||||
|
w.writeUint32Big(vert.iPos);
|
||||||
|
const DNACMDL::Mesh::Material& mat = mesh.materialSets[0][surf.materialIdx];
|
||||||
|
w.writeBool(mat.transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.writeUint32Big(entities.size());
|
||||||
|
for (const auto& ent : entities)
|
||||||
|
{
|
||||||
|
w.writeUint32Big(ent.first);
|
||||||
|
w.writeVec3fBig(ent.second.min);
|
||||||
|
w.writeVec3fBig(ent.second.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
w.writeUint32Big(lightsVisi.size());
|
||||||
|
for (const auto& light : lightsVisi)
|
||||||
|
w.writeVec3fBig(light);
|
||||||
|
|
||||||
|
w.close();
|
||||||
|
|
||||||
|
hecl::SystemString VisiGenPath = ExeDir + _S("/visigen");
|
||||||
|
#if _WIN32
|
||||||
|
VisiGenPath += _S(".exe");
|
||||||
|
#endif
|
||||||
|
const hecl::SystemChar* args[] = {VisiGenPath.c_str(),
|
||||||
|
visiIntOut.getAbsolutePath().c_str(),
|
||||||
|
visiIn.getAbsolutePath().c_str(),
|
||||||
|
nullptr};
|
||||||
|
if (0 == hecl::RunProcess(VisiGenPath.c_str(), args))
|
||||||
|
{
|
||||||
|
athena::io::FileReader r(visiIn.getAbsolutePath());
|
||||||
|
size_t length = r.length();
|
||||||
|
secs.emplace_back(length, 0);
|
||||||
|
r.readBytesToBuf(secs.back().data(), length);
|
||||||
|
good = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!good)
|
||||||
secs.emplace_back(0, 0);
|
secs.emplace_back(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,8 @@ struct MREA
|
||||||
const hecl::ProjectPath& inPath,
|
const hecl::ProjectPath& inPath,
|
||||||
const std::vector<DNACMDL::Mesh>& meshes,
|
const std::vector<DNACMDL::Mesh>& meshes,
|
||||||
const ColMesh& cMesh,
|
const ColMesh& cMesh,
|
||||||
const std::vector<Light>& lights);
|
const std::vector<Light>& lights,
|
||||||
|
hecl::BlenderToken& btok);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ struct Actor : IScriptObject
|
||||||
Value<atVec3f> orientation;
|
Value<atVec3f> orientation;
|
||||||
Value<atVec3f> scale;
|
Value<atVec3f> scale;
|
||||||
Value<atVec3f> collisionExtent;
|
Value<atVec3f> collisionExtent;
|
||||||
Value<atVec3f> centroid;
|
Value<atVec3f> collisionOffset;
|
||||||
Value<float> unknown2;
|
Value<float> unknown2;
|
||||||
Value<float> unknown3;
|
Value<float> unknown3;
|
||||||
HealthInfo healthInfo;
|
HealthInfo healthInfo;
|
||||||
|
@ -64,6 +64,36 @@ struct Actor : IScriptObject
|
||||||
{
|
{
|
||||||
actorParameters.scanIDs(scansOut);
|
actorParameters.scanIDs(scansOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zeus::CAABox getVISIAABB(hecl::BlenderToken& btok) const
|
||||||
|
{
|
||||||
|
hecl::BlenderConnection& conn = btok.getBlenderConnection();
|
||||||
|
zeus::CAABox aabbOut;
|
||||||
|
|
||||||
|
if (model)
|
||||||
|
{
|
||||||
|
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(model);
|
||||||
|
conn.openBlend(path);
|
||||||
|
hecl::BlenderConnection::DataStream ds = conn.beginData();
|
||||||
|
auto aabb = ds.getMeshAABB();
|
||||||
|
aabbOut = zeus::CAABox(aabb.first, aabb.second);
|
||||||
|
}
|
||||||
|
else if (animationParameters.animationCharacterSet)
|
||||||
|
{
|
||||||
|
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(
|
||||||
|
animationParameters.animationCharacterSet);
|
||||||
|
conn.openBlend(path.getWithExtension(_S(".blend"), true));
|
||||||
|
hecl::BlenderConnection::DataStream ds = conn.beginData();
|
||||||
|
auto aabb = ds.getMeshAABB();
|
||||||
|
aabbOut = zeus::CAABox(aabb.first, aabb.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aabbOut.min.x > aabbOut.max.x)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
zeus::CTransform xf = ConvertEditorEulerToTransform4f(scale, orientation, location);
|
||||||
|
return aabbOut.getTransformedAABox(xf);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,13 @@ struct DamageableTrigger : IScriptObject
|
||||||
g_curSpec->flattenDependencies(texture2, pathsOut);
|
g_curSpec->flattenDependencies(texture2, pathsOut);
|
||||||
g_curSpec->flattenDependencies(texture3, pathsOut);
|
g_curSpec->flattenDependencies(texture3, pathsOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zeus::CAABox getVISIAABB(hecl::BlenderToken& btok) const
|
||||||
|
{
|
||||||
|
zeus::CVector3f halfExtent = zeus::CVector3f(volume) / 2.f;
|
||||||
|
zeus::CVector3f loc(location);
|
||||||
|
return zeus::CAABox(loc - halfExtent, loc + halfExtent);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ struct DoorArea : IScriptObject
|
||||||
AnimationParameters animationParameters;
|
AnimationParameters animationParameters;
|
||||||
ActorParameters actorParameters;
|
ActorParameters actorParameters;
|
||||||
Value<atVec3f> unknown1;
|
Value<atVec3f> unknown1;
|
||||||
Value<atVec3f> unknown2;
|
Value<atVec3f> collisionExtent;
|
||||||
Value<atVec3f> unknown3;
|
Value<atVec3f> collisionOffset;
|
||||||
Value<bool> unknown4;
|
Value<bool> unknown4;
|
||||||
Value<bool> unknown5;
|
Value<bool> unknown5;
|
||||||
Value<bool> unknown6;
|
Value<bool> unknown6;
|
||||||
|
@ -49,6 +49,28 @@ struct DoorArea : IScriptObject
|
||||||
{
|
{
|
||||||
actorParameters.scanIDs(scansOut);
|
actorParameters.scanIDs(scansOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zeus::CAABox getVISIAABB(hecl::BlenderToken& btok) const
|
||||||
|
{
|
||||||
|
hecl::BlenderConnection& conn = btok.getBlenderConnection();
|
||||||
|
zeus::CAABox aabbOut;
|
||||||
|
|
||||||
|
if (animationParameters.animationCharacterSet)
|
||||||
|
{
|
||||||
|
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(
|
||||||
|
animationParameters.animationCharacterSet);
|
||||||
|
conn.openBlend(path.getWithExtension(_S(".blend"), true));
|
||||||
|
hecl::BlenderConnection::DataStream ds = conn.beginData();
|
||||||
|
auto aabb = ds.getMeshAABB();
|
||||||
|
aabbOut = zeus::CAABox(aabb.first, aabb.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aabbOut.min.x > aabbOut.max.x)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
zeus::CTransform xf = ConvertEditorEulerToTransform4f(scale, orientation, location);
|
||||||
|
return aabbOut.getTransformedAABox(xf);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,5 +267,16 @@ const std::vector<const struct ScriptObjectSpec*> SCRIPT_OBJECT_DB =
|
||||||
&priv::WorldTeleporterx62Ent,
|
&priv::WorldTeleporterx62Ent,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
zeus::CTransform ConvertEditorEulerToTransform4f(const zeus::CVector3f& scale,
|
||||||
|
const zeus::CVector3f& orientation,
|
||||||
|
const zeus::CVector3f& position)
|
||||||
|
{
|
||||||
|
return zeus::CTransform::RotateZ(zeus::degToRad(orientation.z)) *
|
||||||
|
zeus::CTransform::RotateY(zeus::degToRad(orientation.y)) *
|
||||||
|
zeus::CTransform::RotateX(zeus::degToRad(orientation.x)) *
|
||||||
|
zeus::CTransform::Scale(scale) +
|
||||||
|
position;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,18 @@
|
||||||
#include "../../DNACommon/DNACommon.hpp"
|
#include "../../DNACommon/DNACommon.hpp"
|
||||||
#include "../DNAMP1.hpp"
|
#include "../DNAMP1.hpp"
|
||||||
#include "../SAVW.hpp"
|
#include "../SAVW.hpp"
|
||||||
|
#include "zeus/CAABox.hpp"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
namespace DataSpec
|
namespace DataSpec
|
||||||
{
|
{
|
||||||
namespace DNAMP1
|
namespace DNAMP1
|
||||||
{
|
{
|
||||||
|
|
||||||
|
zeus::CTransform ConvertEditorEulerToTransform4f(const zeus::CVector3f& scale,
|
||||||
|
const zeus::CVector3f& orientation,
|
||||||
|
const zeus::CVector3f& position);
|
||||||
|
|
||||||
struct IScriptObject : BigYAML
|
struct IScriptObject : BigYAML
|
||||||
{
|
{
|
||||||
DECL_YAML
|
DECL_YAML
|
||||||
|
@ -32,6 +38,7 @@ struct IScriptObject : BigYAML
|
||||||
virtual void nameIDs(PAKRouter<PAKBridge>& pakRouter) const {}
|
virtual void nameIDs(PAKRouter<PAKBridge>& pakRouter) const {}
|
||||||
virtual void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {}
|
virtual void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {}
|
||||||
virtual void gatherScans(std::vector<Scan>& scansOut) const {}
|
virtual void gatherScans(std::vector<Scan>& scansOut) const {}
|
||||||
|
virtual zeus::CAABox getVISIAABB(hecl::BlenderToken& btok) const { return {}; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,36 @@ struct Platform : IScriptObject
|
||||||
{
|
{
|
||||||
actorParameters.scanIDs(scansOut);
|
actorParameters.scanIDs(scansOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zeus::CAABox getVISIAABB(hecl::BlenderToken& btok) const
|
||||||
|
{
|
||||||
|
hecl::BlenderConnection& conn = btok.getBlenderConnection();
|
||||||
|
zeus::CAABox aabbOut;
|
||||||
|
|
||||||
|
if (model)
|
||||||
|
{
|
||||||
|
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(model);
|
||||||
|
conn.openBlend(path);
|
||||||
|
hecl::BlenderConnection::DataStream ds = conn.beginData();
|
||||||
|
auto aabb = ds.getMeshAABB();
|
||||||
|
aabbOut = zeus::CAABox(aabb.first, aabb.second);
|
||||||
|
}
|
||||||
|
else if (animationParameters.animationCharacterSet)
|
||||||
|
{
|
||||||
|
hecl::ProjectPath path = UniqueIDBridge::TranslatePakIdToPath(
|
||||||
|
animationParameters.animationCharacterSet);
|
||||||
|
conn.openBlend(path.getWithExtension(_S(".blend"), true));
|
||||||
|
hecl::BlenderConnection::DataStream ds = conn.beginData();
|
||||||
|
auto aabb = ds.getMeshAABB();
|
||||||
|
aabbOut = zeus::CAABox(aabb.first, aabb.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aabbOut.min.x > aabbOut.max.x)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
zeus::CTransform xf = ConvertEditorEulerToTransform4f(scale, orientation, location);
|
||||||
|
return aabbOut.getTransformedAABox(xf);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,13 @@ struct Trigger : IScriptObject
|
||||||
Value<bool> active;
|
Value<bool> active;
|
||||||
Value<bool> unknown2;
|
Value<bool> unknown2;
|
||||||
Value<bool> unknown3;
|
Value<bool> unknown3;
|
||||||
|
|
||||||
|
zeus::CAABox getVISIAABB(hecl::BlenderToken& btok) const
|
||||||
|
{
|
||||||
|
zeus::CVector3f halfExtent = zeus::CVector3f(volume) / 2.f;
|
||||||
|
zeus::CVector3f loc(location);
|
||||||
|
return zeus::CAABox(loc - halfExtent, loc + halfExtent);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,6 +173,13 @@ struct Water : IScriptObject
|
||||||
g_curSpec->flattenDependencies(particle4, pathsOut);
|
g_curSpec->flattenDependencies(particle4, pathsOut);
|
||||||
g_curSpec->flattenDependencies(particle5, pathsOut);
|
g_curSpec->flattenDependencies(particle5, pathsOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zeus::CAABox getVISIAABB(hecl::BlenderToken& btok) const
|
||||||
|
{
|
||||||
|
zeus::CVector3f halfExtent = zeus::CVector3f(volume) / 2.f;
|
||||||
|
zeus::CVector3f loc(location);
|
||||||
|
return zeus::CAABox(loc - halfExtent, loc + halfExtent);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -381,6 +381,7 @@ struct SpecMP1 : SpecBase
|
||||||
std::unique_lock<std::mutex> lk(msgLock);
|
std::unique_lock<std::mutex> lk(msgLock);
|
||||||
progress(sysName.c_str(), _S(""), compIdx, 0.0);
|
progress(sysName.c_str(), _S(""), compIdx, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
hecl::SystemString pakName = sysName.sys_str();
|
hecl::SystemString pakName = sysName.sys_str();
|
||||||
process.addLambdaTransaction([&, pakName](hecl::BlenderToken& btok) {
|
process.addLambdaTransaction([&, pakName](hecl::BlenderToken& btok) {
|
||||||
m_pakRouter.extractResources(pak, force, btok, [&](const hecl::SystemChar* substr, float factor) {
|
m_pakRouter.extractResources(pak, force, btok, [&](const hecl::SystemChar* substr, float factor) {
|
||||||
|
@ -737,8 +738,10 @@ struct SpecMP1 : SpecBase
|
||||||
|
|
||||||
std::vector<Light> lights = ds.compileLights();
|
std::vector<Light> lights = ds.compileLights();
|
||||||
|
|
||||||
|
ds.close();
|
||||||
|
|
||||||
if (m_pc)
|
if (m_pc)
|
||||||
DNAMP1::MREA::PCCook(out, in, meshCompiles, *colMesh, lights);
|
DNAMP1::MREA::PCCook(out, in, meshCompiles, *colMesh, lights, btok);
|
||||||
else
|
else
|
||||||
DNAMP1::MREA::Cook(out, in, meshCompiles, *colMesh, lights);
|
DNAMP1::MREA::Cook(out, in, meshCompiles, *colMesh, lights);
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,9 @@ struct Application : boo::IApplicationCallback
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static hecl::SystemChar CwdBuf[1024];
|
||||||
|
hecl::SystemString ExeDir;
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
int wmain(int argc, const boo::SystemChar** argv)
|
int wmain(int argc, const boo::SystemChar** argv)
|
||||||
#else
|
#else
|
||||||
|
@ -132,6 +135,17 @@ int main(int argc, const boo::SystemChar** argv)
|
||||||
logvisor::RegisterStandardExceptions();
|
logvisor::RegisterStandardExceptions();
|
||||||
logvisor::RegisterConsoleLogger();
|
logvisor::RegisterConsoleLogger();
|
||||||
atSetExceptionHandler(AthenaExc);
|
atSetExceptionHandler(AthenaExc);
|
||||||
|
|
||||||
|
if (hecl::SystemChar* cwd = hecl::Getcwd(CwdBuf, 1024))
|
||||||
|
{
|
||||||
|
if (argv[0][0] != _S('/') && argv[0][0] != _S('\\'))
|
||||||
|
ExeDir = hecl::SystemString(cwd) + _S('/');
|
||||||
|
hecl::SystemString Argv0(argv[0]);
|
||||||
|
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_S("/\\"));
|
||||||
|
if (lastIdx != hecl::SystemString::npos)
|
||||||
|
ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
|
||||||
|
}
|
||||||
|
|
||||||
urde::Application appCb;
|
urde::Application appCb;
|
||||||
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto,
|
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto,
|
||||||
appCb, _S("urde"), _S("URDE"), argc, argv, false);
|
appCb, _S("urde"), _S("URDE"), argc, argv, false);
|
||||||
|
|
2
hecl
2
hecl
|
@ -1 +1 @@
|
||||||
Subproject commit f407c84e78721e6e682bfc7790bfb0c68a886453
|
Subproject commit 1a9260afd0a5bcf9e77203bd4b5cde4334eb1b3f
|
2
specter
2
specter
|
@ -1 +1 @@
|
||||||
Subproject commit 03d074af13e902cebdf05d6f5ec5e4c2cbf569ee
|
Subproject commit 67a7efea539964b2f2a816135ee5719e5ae104a1
|
|
@ -0,0 +1,21 @@
|
||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(visigen)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
else()
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}\
|
||||||
|
-std=c++14 -Wno-multichar -fno-exceptions -Wno-narrowing -Wno-nullability-completeness -Werror=return-type")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set(PLAT_SRCS MainMac.mm)
|
||||||
|
set_source_files_properties(MainMac.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(visigen ${PLAT_SRCS}
|
||||||
|
VISIRenderer.cpp VISIRenderer.hpp
|
||||||
|
VISIBuilder.cpp VISIBuilder.hpp)
|
||||||
|
target_link_libraries(visigen logvisor athena-core zeus glew xxhash ${BOO_SYS_LIBS})
|
||||||
|
|
||||||
|
add_custom_command(TARGET visigen POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:visigen> $<TARGET_FILE_DIR:urde>)
|
|
@ -0,0 +1,122 @@
|
||||||
|
#include "VISIRenderer.hpp"
|
||||||
|
#include <AppKit/AppKit.h>
|
||||||
|
#include "athena/Global.hpp"
|
||||||
|
#include "logvisor/logvisor.hpp"
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#if !__has_feature(objc_arc)
|
||||||
|
#error ARC Required
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static std::thread s_task;
|
||||||
|
|
||||||
|
static const NSOpenGLPixelFormatAttribute PF_RGBA8_Z24_ATTRS[] =
|
||||||
|
{
|
||||||
|
NSOpenGLPFAAccelerated,
|
||||||
|
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
||||||
|
//NSOpenGLPFADoubleBuffer,
|
||||||
|
NSOpenGLPFAColorSize, 24,
|
||||||
|
NSOpenGLPFAAlphaSize, 8,
|
||||||
|
NSOpenGLPFADepthSize, 24,
|
||||||
|
0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface OpenGLView : NSOpenGLView
|
||||||
|
{
|
||||||
|
VISIRenderer* m_renderer;
|
||||||
|
}
|
||||||
|
- (id)initWithFrame:(NSRect)frame renderer:(VISIRenderer*)renderer;
|
||||||
|
@end
|
||||||
|
|
||||||
|
static NSWindow* s_Window;
|
||||||
|
static void UpdatePercent(float percent)
|
||||||
|
{
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
s_Window.title = [NSString stringWithFormat:@"VISIGen [%g%%]", percent * 100.f];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation OpenGLView
|
||||||
|
- (id)initWithFrame:(NSRect)frame renderer:(VISIRenderer*)renderer;
|
||||||
|
{
|
||||||
|
NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:PF_RGBA8_Z24_ATTRS];
|
||||||
|
self = [super initWithFrame:frame pixelFormat:pf];
|
||||||
|
m_renderer = renderer;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)prepareOpenGL
|
||||||
|
{
|
||||||
|
s_task = std::thread([self](){
|
||||||
|
[[self openGLContext] makeCurrentContext];
|
||||||
|
m_renderer->Run(UpdatePercent);
|
||||||
|
[NSApp terminate:nil];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||||
|
{
|
||||||
|
VISIRenderer* m_renderer;
|
||||||
|
NSWindow* m_window;
|
||||||
|
NSOpenGLView* m_glView;
|
||||||
|
}
|
||||||
|
- (id)initWithRenderer:(VISIRenderer*)renderer;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AppDelegate
|
||||||
|
- (id)initWithRenderer:(VISIRenderer*)renderer
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
m_renderer = renderer;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)applicationDidFinishLaunching:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
NSRect cRect = NSMakeRect(100, 100, 768, 512);
|
||||||
|
m_window = [[NSWindow alloc] initWithContentRect:cRect
|
||||||
|
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable
|
||||||
|
backing:NSBackingStoreBuffered
|
||||||
|
defer:NO];
|
||||||
|
m_window.releasedWhenClosed = NO;
|
||||||
|
m_window.title = @"VISIGen";
|
||||||
|
s_Window = m_window;
|
||||||
|
m_glView = [[OpenGLView alloc] initWithFrame:cRect renderer:m_renderer];
|
||||||
|
m_window.contentView = m_glView;
|
||||||
|
[m_window makeKeyAndOrderFront:nil];
|
||||||
|
}
|
||||||
|
- (void)applicationWillTerminate:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
m_renderer->Terminate();
|
||||||
|
if (s_task.joinable())
|
||||||
|
s_task.join();
|
||||||
|
exit(m_renderer->ReturnVal());
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
static logvisor::Module AthenaLog("Athena");
|
||||||
|
static void AthenaExc(athena::error::Level level, const char* file,
|
||||||
|
const char*, int line, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
AthenaLog.report(logvisor::Level(level), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char** argv)
|
||||||
|
{
|
||||||
|
logvisor::RegisterStandardExceptions();
|
||||||
|
logvisor::RegisterConsoleLogger();
|
||||||
|
atSetExceptionHandler(AthenaExc);
|
||||||
|
VISIRenderer renderer(argc, argv);
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
[[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||||
|
|
||||||
|
/* Delegate (OS X callbacks) */
|
||||||
|
AppDelegate* appDelegate = [[AppDelegate alloc] initWithRenderer:&renderer];
|
||||||
|
[[NSApplication sharedApplication] setDelegate:appDelegate];
|
||||||
|
[[NSApplication sharedApplication] run];
|
||||||
|
}
|
||||||
|
return renderer.ReturnVal();
|
||||||
|
}
|
|
@ -0,0 +1,369 @@
|
||||||
|
#include "VISIBuilder.hpp"
|
||||||
|
|
||||||
|
#define VISI_MAX_LEVEL 10
|
||||||
|
#define VISI_MIN_LENGTH 8.0
|
||||||
|
|
||||||
|
static logvisor::Module Log("VISIBuilder");
|
||||||
|
const VISIBuilder::Leaf VISIBuilder::NullLeaf = {};
|
||||||
|
|
||||||
|
VISIBuilder::PVSRenderCache::PVSRenderCache(VISIRenderer& renderer)
|
||||||
|
: m_renderer(renderer)
|
||||||
|
{
|
||||||
|
m_cache.reserve(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<VISIRenderer::RGBA8[]> RGBABuf(new VISIRenderer::RGBA8[256 * 256 * 6]);
|
||||||
|
|
||||||
|
const VISIBuilder::Leaf& VISIBuilder::PVSRenderCache::GetLeaf(const zeus::CVector3f& vec)
|
||||||
|
{
|
||||||
|
auto search = m_cache.find(vec);
|
||||||
|
if (search != m_cache.cend())
|
||||||
|
{
|
||||||
|
//Log.report(logvisor::Info, "Cache hit");
|
||||||
|
return *search->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log.report(logvisor::Info, "Rendering");
|
||||||
|
bool needsTransparent = false;
|
||||||
|
m_renderer.RenderPVSOpaque(RGBABuf.get(), vec, needsTransparent);
|
||||||
|
std::unique_ptr<Leaf> leafOut = std::make_unique<Leaf>();
|
||||||
|
for (unsigned i=0 ; i<768*512 ; ++i)
|
||||||
|
{
|
||||||
|
const VISIRenderer::RGBA8& pixel = RGBABuf[i];
|
||||||
|
uint32_t id = (pixel.b << 16) | (pixel.g << 8) | pixel.r;
|
||||||
|
if (id != 0xffffff)
|
||||||
|
leafOut->setBit(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto setBitLambda = [&](int idx) { leafOut->setBit(idx); };
|
||||||
|
auto setLightLambda = [&](int idx, EPVSVisSetState state)
|
||||||
|
{
|
||||||
|
if (state != EPVSVisSetState::EndOfTree)
|
||||||
|
leafOut->setLightEnum(m_lightMetaBit + idx * 2, state);
|
||||||
|
};
|
||||||
|
if (needsTransparent)
|
||||||
|
m_renderer.RenderPVSTransparent(setBitLambda, vec);
|
||||||
|
m_renderer.RenderPVSEntitiesAndLights(setBitLambda, setLightLambda, vec);
|
||||||
|
|
||||||
|
return *m_cache.emplace(std::make_pair(vec, std::move(leafOut))).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISIBuilder::Progress::report(int divisions)
|
||||||
|
{
|
||||||
|
m_prog += 1.f / divisions;
|
||||||
|
printf(" %g\%% \r", m_prog * 100.f);
|
||||||
|
fflush(stdout);
|
||||||
|
if (m_updatePercent)
|
||||||
|
m_updatePercent(m_prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISIBuilder::Node::buildChildren(int level, int divisions, const zeus::CAABox& curAabb,
|
||||||
|
PVSRenderCache& rc, Progress& prog, const bool& terminate)
|
||||||
|
{
|
||||||
|
if (terminate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Recurse in while building node structure
|
||||||
|
if (level < VISI_MAX_LEVEL)
|
||||||
|
{
|
||||||
|
// Heuristic split
|
||||||
|
int splits[3];
|
||||||
|
splits[0] = (curAabb.max.x - curAabb.min.x >= VISI_MIN_LENGTH) ? 2 : 1;
|
||||||
|
splits[1] = (curAabb.max.y - curAabb.min.y >= VISI_MIN_LENGTH) ? 2 : 1;
|
||||||
|
splits[2] = (curAabb.max.z - curAabb.min.z >= VISI_MIN_LENGTH) ? 2 : 1;
|
||||||
|
|
||||||
|
if (splits[0] == 2)
|
||||||
|
flags |= 0x1;
|
||||||
|
if (splits[1] == 2)
|
||||||
|
flags |= 0x2;
|
||||||
|
if (splits[2] == 2)
|
||||||
|
flags |= 0x4;
|
||||||
|
|
||||||
|
int thisdiv = splits[0] * splits[1] * splits[2] * divisions;
|
||||||
|
|
||||||
|
if (flags)
|
||||||
|
{
|
||||||
|
childNodes.resize(8);
|
||||||
|
|
||||||
|
// Inward subdivide
|
||||||
|
zeus::CAABox Z[2];
|
||||||
|
if (flags & 0x4)
|
||||||
|
curAabb.splitZ(Z[1], Z[0]);
|
||||||
|
else
|
||||||
|
Z[0] = curAabb;
|
||||||
|
for (int i=0 ; i<splits[2] ; ++i)
|
||||||
|
{
|
||||||
|
zeus::CAABox Y[2];
|
||||||
|
if (flags & 0x2)
|
||||||
|
Z[i].splitY(Y[1], Y[0]);
|
||||||
|
else
|
||||||
|
Y[0] = Z[i];
|
||||||
|
for (int j=0 ; j<splits[1] ; ++j)
|
||||||
|
{
|
||||||
|
zeus::CAABox X[2];
|
||||||
|
if (flags & 0x1)
|
||||||
|
Y[j].splitX(X[1], X[0]);
|
||||||
|
else
|
||||||
|
X[0] = Y[j];
|
||||||
|
for (int k=0 ; k<splits[0] ; ++k)
|
||||||
|
{
|
||||||
|
childNodes[i*4 + j*2 + k].buildChildren(level + 1, thisdiv, X[k], rc, prog, terminate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outward unsubdivide for like-leaves
|
||||||
|
for (int i=0 ; i<3 ; ++i)
|
||||||
|
{
|
||||||
|
if (flags & 0x4 &&
|
||||||
|
childNodes[0] == childNodes[4] &&
|
||||||
|
(!(flags & 0x1) || childNodes[1] == childNodes[5]) &&
|
||||||
|
(!(flags & 0x2) || childNodes[2] == childNodes[6]) &&
|
||||||
|
(!(flags & 0x3) || childNodes[3] == childNodes[7]))
|
||||||
|
{
|
||||||
|
flags &= ~0x4;
|
||||||
|
//Log.report(logvisor::Info, "Unsub Z");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (flags & 0x2 &&
|
||||||
|
childNodes[0] == childNodes[2] &&
|
||||||
|
(!(flags & 0x1) || childNodes[1] == childNodes[3]) &&
|
||||||
|
(!(flags & 0x4) || childNodes[4] == childNodes[6]) &&
|
||||||
|
(!(flags & 0x5) || childNodes[5] == childNodes[7]))
|
||||||
|
{
|
||||||
|
flags &= ~0x2;
|
||||||
|
//Log.report(logvisor::Info, "Unsub Y");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (flags & 0x1 &&
|
||||||
|
childNodes[0] == childNodes[1] &&
|
||||||
|
(!(flags & 0x2) || childNodes[2] == childNodes[3]) &&
|
||||||
|
(!(flags & 0x4) || childNodes[4] == childNodes[5]) &&
|
||||||
|
(!(flags & 0x6) || childNodes[6] == childNodes[7]))
|
||||||
|
{
|
||||||
|
flags &= ~0x1;
|
||||||
|
//Log.report(logvisor::Info, "Unsub X");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flags)
|
||||||
|
{
|
||||||
|
// This is now a leaf node
|
||||||
|
for (int i=0 ; i<8 ; ++i)
|
||||||
|
leaf |= childNodes[i].leaf;
|
||||||
|
//Log.report(logvisor::Info, "Leaf Promote");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flags)
|
||||||
|
{
|
||||||
|
// This is a child node
|
||||||
|
zeus::CVector3f center = curAabb.center();
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, curAabb.min.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, curAabb.min.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, curAabb.min.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, center.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, center.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, center.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, curAabb.max.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, curAabb.max.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, curAabb.max.y, curAabb.min.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, curAabb.min.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, curAabb.min.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, curAabb.min.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, center.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, center.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, center.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, curAabb.max.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, curAabb.max.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, curAabb.max.y, center.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, curAabb.min.y, curAabb.max.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, curAabb.min.y, curAabb.max.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, curAabb.min.y, curAabb.max.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, center.y, curAabb.max.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, center.y, curAabb.max.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, center.y, curAabb.max.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.min.x, curAabb.max.y, curAabb.max.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(center.x, curAabb.max.y, curAabb.max.z));
|
||||||
|
leaf |= rc.GetLeaf(zeus::CVector3f(curAabb.max.x, curAabb.max.y, curAabb.max.z));
|
||||||
|
|
||||||
|
prog.report(divisions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int NumChildTable[] =
|
||||||
|
{
|
||||||
|
0, 2, 2, 4, 2, 4, 4, 8
|
||||||
|
};
|
||||||
|
|
||||||
|
void VISIBuilder::Node::calculateSizesAndOffs(size_t& cur, size_t leafSz)
|
||||||
|
{
|
||||||
|
cur += 1;
|
||||||
|
|
||||||
|
if (flags)
|
||||||
|
{
|
||||||
|
int splits[3];
|
||||||
|
splits[0] = (flags & 0x1) ? 2 : 1;
|
||||||
|
splits[1] = (flags & 0x2) ? 2 : 1;
|
||||||
|
splits[2] = (flags & 0x4) ? 2 : 1;
|
||||||
|
|
||||||
|
// Inward accumulate
|
||||||
|
const size_t startCur = cur;
|
||||||
|
size_t maxDelta = 0;
|
||||||
|
for (int i=0 ; i<splits[2] ; ++i)
|
||||||
|
for (int j=0 ; j<splits[1] ; ++j)
|
||||||
|
for (int k=0 ; k<splits[0] ; ++k)
|
||||||
|
{
|
||||||
|
const size_t nodeSel = i*4 + j*2 + k;
|
||||||
|
const size_t delta = cur - startCur;
|
||||||
|
if (delta > maxDelta)
|
||||||
|
maxDelta = delta;
|
||||||
|
childRelOffs[nodeSel] = delta;
|
||||||
|
childNodes[nodeSel].calculateSizesAndOffs(cur, leafSz);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int numChildren = NumChildTable[flags & 0x7];
|
||||||
|
if (maxDelta > 0xffff)
|
||||||
|
{
|
||||||
|
cur += (numChildren - 1) * 3;
|
||||||
|
flags |= 0x40;
|
||||||
|
}
|
||||||
|
else if (maxDelta > 0xff)
|
||||||
|
{
|
||||||
|
cur += (numChildren - 1) * 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cur += numChildren - 1;
|
||||||
|
flags |= 0x20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cur += leafSz;
|
||||||
|
flags |= 0x18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISIBuilder::Node::writeNodes(athena::io::MemoryWriter& w, size_t leafBytes) const
|
||||||
|
{
|
||||||
|
w.writeUByte(flags);
|
||||||
|
|
||||||
|
if (flags & 0x7)
|
||||||
|
{
|
||||||
|
int splits[3];
|
||||||
|
splits[0] = (flags & 0x1) ? 2 : 1;
|
||||||
|
splits[1] = (flags & 0x2) ? 2 : 1;
|
||||||
|
splits[2] = (flags & 0x4) ? 2 : 1;
|
||||||
|
|
||||||
|
// Write offsets
|
||||||
|
for (int i=0 ; i<splits[2] ; ++i)
|
||||||
|
for (int j=0 ; j<splits[1] ; ++j)
|
||||||
|
for (int k=0 ; k<splits[0] ; ++k)
|
||||||
|
{
|
||||||
|
const size_t nodeSel = i*4 + j*2 + k;
|
||||||
|
if (nodeSel == 0)
|
||||||
|
continue;
|
||||||
|
const size_t offset = childRelOffs[nodeSel];
|
||||||
|
if (flags & 0x40)
|
||||||
|
{
|
||||||
|
w.writeUByte((offset >> 16) & 0xff);
|
||||||
|
w.writeUByte((offset >> 8) & 0xff);
|
||||||
|
w.writeUByte(offset & 0xff);
|
||||||
|
}
|
||||||
|
else if (flags & 0x20)
|
||||||
|
{
|
||||||
|
w.writeUByte(offset & 0xff);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
w.writeUint16Big(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inward iterate
|
||||||
|
for (int i=0 ; i<splits[2] ; ++i)
|
||||||
|
for (int j=0 ; j<splits[1] ; ++j)
|
||||||
|
for (int k=0 ; k<splits[0] ; ++k)
|
||||||
|
{
|
||||||
|
const size_t nodeSel = i*4 + j*2 + k;
|
||||||
|
childNodes[nodeSel].writeNodes(w, leafBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
leaf.write(w, leafBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> VISIBuilder::build(const zeus::CAABox& fullAabb,
|
||||||
|
size_t modelCount,
|
||||||
|
const std::vector<VISIRenderer::Entity>& entities,
|
||||||
|
const std::vector<VISIRenderer::Light>& lights,
|
||||||
|
FPercent updatePercent)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Info, "Started!");
|
||||||
|
|
||||||
|
size_t featureCount = modelCount + entities.size();
|
||||||
|
renderCache.m_lightMetaBit = featureCount + lights.size();
|
||||||
|
|
||||||
|
Progress prog(updatePercent);
|
||||||
|
bool& terminate = renderCache.m_renderer.m_terminate;
|
||||||
|
rootNode.buildChildren(0, 1, fullAabb, renderCache, prog, terminate);
|
||||||
|
if (terminate)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Lights cache their CPVSVisSet result enum as 2 bits
|
||||||
|
size_t leafBitsCount = featureCount + lights.size() * 3;
|
||||||
|
size_t leafBytesCount = ROUND_UP_8(leafBitsCount) / 8;
|
||||||
|
|
||||||
|
// Calculate octree size and store relative offsets
|
||||||
|
size_t octreeSz = 0;
|
||||||
|
rootNode.calculateSizesAndOffs(octreeSz, leafBytesCount);
|
||||||
|
octreeSz += 1; // Terminator node
|
||||||
|
|
||||||
|
size_t visiSz = 34 + entities.size() * 4 + lights.size() * leafBytesCount + 36 + octreeSz;
|
||||||
|
size_t roundedVisiSz = ROUND_UP_32(visiSz);
|
||||||
|
|
||||||
|
std::vector<uint8_t> dataOut(roundedVisiSz, 0);
|
||||||
|
athena::io::MemoryWriter w(dataOut.data(), roundedVisiSz);
|
||||||
|
w.writeUint32Big('VISI');
|
||||||
|
w.writeUint32Big(2);
|
||||||
|
w.writeBool(true);
|
||||||
|
w.writeBool(true);
|
||||||
|
w.writeUint32Big(featureCount);
|
||||||
|
w.writeUint32Big(lights.size());
|
||||||
|
w.writeUint32Big(0);
|
||||||
|
w.writeUint32Big(entities.size());
|
||||||
|
w.writeUint32Big(leafBytesCount);
|
||||||
|
w.writeUint32Big(lights.size());
|
||||||
|
|
||||||
|
for (const VISIRenderer::Entity& e : entities)
|
||||||
|
{
|
||||||
|
w.writeUint32Big(e.entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const VISIRenderer::Light& l : lights)
|
||||||
|
{
|
||||||
|
const VISIBuilder::Leaf& leaf = renderCache.GetLeaf(l.point);
|
||||||
|
leaf.write(w, leafBytesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
w.writeVec3fBig(fullAabb.min);
|
||||||
|
w.writeVec3fBig(fullAabb.max);
|
||||||
|
w.writeUint32Big(leafBitsCount);
|
||||||
|
w.writeUint32Big(lights.size());
|
||||||
|
w.writeUint32Big(octreeSz);
|
||||||
|
rootNode.writeNodes(w, leafBytesCount);
|
||||||
|
w.writeUByte(0x10);
|
||||||
|
|
||||||
|
w.seekAlign32();
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
Log.report(logvisor::Info, "Finished!");
|
||||||
|
return dataOut;
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
#ifndef _DNACOMMON_VISIBUILDER_HPP_
|
||||||
|
#define _DNACOMMON_VISIBUILDER_HPP_
|
||||||
|
|
||||||
|
#include "VISIRenderer.hpp"
|
||||||
|
#include "zeus/CAABox.hpp"
|
||||||
|
#include "hecl/extern/xxhash/xxhash.h"
|
||||||
|
#include "athena/MemoryWriter.hpp"
|
||||||
|
#include "hecl/hecl.hpp"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <> struct hash<zeus::CVector3f>
|
||||||
|
{
|
||||||
|
size_t operator()(const zeus::CVector3f& val) const noexcept
|
||||||
|
{
|
||||||
|
return XXH64(val.v, 12, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VISIBuilder
|
||||||
|
{
|
||||||
|
struct Leaf
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> bits;
|
||||||
|
void setBit(size_t bit)
|
||||||
|
{
|
||||||
|
size_t byte = bit / 8;
|
||||||
|
if (byte >= bits.size())
|
||||||
|
bits.resize(byte + 1);
|
||||||
|
bits[byte] |= 1 << (bit & 0x7);
|
||||||
|
}
|
||||||
|
void setLightEnum(size_t bit, EPVSVisSetState state)
|
||||||
|
{
|
||||||
|
size_t byte0 = bit / 8;
|
||||||
|
size_t byte1 = (bit + 1) / 8;
|
||||||
|
if (byte1 >= bits.size())
|
||||||
|
bits.resize(byte1 + 1);
|
||||||
|
|
||||||
|
if (byte0 == byte1)
|
||||||
|
{
|
||||||
|
bits[byte0] |= int(state) << (bit & 0x7);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bits[byte0] |= (int(state) << 7) & 0x1;
|
||||||
|
bits[byte1] |= (int(state) >> 1) & 0x1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool operator==(const Leaf& other) const
|
||||||
|
{
|
||||||
|
if (bits.size() != other.bits.size())
|
||||||
|
return false;
|
||||||
|
if (memcmp(bits.data(), other.bits.data(), bits.size()))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Leaf& operator|=(const Leaf& other)
|
||||||
|
{
|
||||||
|
if (bits.size() < other.bits.size())
|
||||||
|
bits.resize(other.bits.size());
|
||||||
|
for (int i=0 ; i<other.bits.size() ; ++i)
|
||||||
|
bits[i] |= other.bits[i];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
operator bool() const { return bits.size() != 0; }
|
||||||
|
|
||||||
|
void write(athena::io::MemoryWriter& w, size_t leafBytes) const
|
||||||
|
{
|
||||||
|
for (size_t i=0 ; i<leafBytes ; ++i)
|
||||||
|
{
|
||||||
|
if (i < bits.size())
|
||||||
|
w.writeUByte(bits[i]);
|
||||||
|
else
|
||||||
|
w.writeUByte(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static const Leaf NullLeaf;
|
||||||
|
|
||||||
|
class PVSRenderCache
|
||||||
|
{
|
||||||
|
friend struct VISIBuilder;
|
||||||
|
VISIRenderer& m_renderer;
|
||||||
|
std::unordered_map<zeus::CVector3f, std::unique_ptr<Leaf>> m_cache;
|
||||||
|
size_t m_lightMetaBit;
|
||||||
|
public:
|
||||||
|
PVSRenderCache(VISIRenderer& renderer);
|
||||||
|
const Leaf& GetLeaf(const zeus::CVector3f& vec);
|
||||||
|
} renderCache;
|
||||||
|
|
||||||
|
class Progress
|
||||||
|
{
|
||||||
|
float m_prog = 0.f;
|
||||||
|
FPercent m_updatePercent;
|
||||||
|
public:
|
||||||
|
void report(int divisions);
|
||||||
|
Progress(FPercent updatePercent) : m_updatePercent(updatePercent) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
std::vector<Node> childNodes;
|
||||||
|
size_t childRelOffs[8] = {};
|
||||||
|
Leaf leaf;
|
||||||
|
uint8_t flags = 0;
|
||||||
|
|
||||||
|
void buildChildren(int level, int divisions, const zeus::CAABox& curAabb,
|
||||||
|
PVSRenderCache& rc, Progress& prog, const bool& terminate);
|
||||||
|
void calculateSizesAndOffs(size_t& cur, size_t leafSz);
|
||||||
|
void writeNodes(athena::io::MemoryWriter& w, size_t leafBytes) const;
|
||||||
|
|
||||||
|
bool operator==(const Node& other) const
|
||||||
|
{
|
||||||
|
if (!leaf || !other.leaf)
|
||||||
|
return false;
|
||||||
|
return leaf == other.leaf;
|
||||||
|
}
|
||||||
|
} rootNode;
|
||||||
|
|
||||||
|
std::vector<uint8_t> build(const zeus::CAABox& fullAabb,
|
||||||
|
size_t modelCount,
|
||||||
|
const std::vector<VISIRenderer::Entity>& entities,
|
||||||
|
const std::vector<VISIRenderer::Light>& lights,
|
||||||
|
FPercent updatePercent);
|
||||||
|
|
||||||
|
VISIBuilder(VISIRenderer& renderer) : renderCache(renderer) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _DNACOMMON_VISIBUILDER_HPP_
|
|
@ -0,0 +1,562 @@
|
||||||
|
#include "VISIRenderer.hpp"
|
||||||
|
#include "athena/FileReader.hpp"
|
||||||
|
#include "zeus/CAABox.hpp"
|
||||||
|
#include "VISIBuilder.hpp"
|
||||||
|
#include "zeus/CFrustum.hpp"
|
||||||
|
|
||||||
|
static logvisor::Module Log("visigen");
|
||||||
|
|
||||||
|
static const char* VS =
|
||||||
|
"#version 330\n"
|
||||||
|
"layout(location=0) in vec4 posIn;\n"
|
||||||
|
"layout(location=1) in vec4 colorIn;\n"
|
||||||
|
"\n"
|
||||||
|
"uniform UniformBlock\n"
|
||||||
|
"{\n"
|
||||||
|
" mat4 xf;\n"
|
||||||
|
"};\n"
|
||||||
|
"\n"
|
||||||
|
"struct VertToFrag\n"
|
||||||
|
"{\n"
|
||||||
|
" vec4 color;\n"
|
||||||
|
"};\n"
|
||||||
|
"\n"
|
||||||
|
"out VertToFrag vtf;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" vtf.color = colorIn;\n"
|
||||||
|
" gl_Position = xf * vec4(posIn.xyz, 1.0);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char* FS =
|
||||||
|
"#version 330\n"
|
||||||
|
"struct VertToFrag\n"
|
||||||
|
"{\n"
|
||||||
|
" vec4 color;\n"
|
||||||
|
"};\n"
|
||||||
|
"\n"
|
||||||
|
"in VertToFrag vtf;\n"
|
||||||
|
"layout(location=0) out vec4 colorOut;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" colorOut = vtf.color;\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const uint32_t AABBIdxs[19] =
|
||||||
|
{
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 7, 3, 5, 5, 0, 0, 2, 6, 4
|
||||||
|
};
|
||||||
|
|
||||||
|
bool VISIRenderer::SetupShaders()
|
||||||
|
{
|
||||||
|
m_vtxShader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
m_fragShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
m_program = glCreateProgram();
|
||||||
|
|
||||||
|
glShaderSource(m_vtxShader, 1, &VS, nullptr);
|
||||||
|
glCompileShader(m_vtxShader);
|
||||||
|
GLint status;
|
||||||
|
glGetShaderiv(m_vtxShader, GL_COMPILE_STATUS, &status);
|
||||||
|
if (status != GL_TRUE)
|
||||||
|
{
|
||||||
|
GLint logLen;
|
||||||
|
glGetShaderiv(m_vtxShader, GL_INFO_LOG_LENGTH, &logLen);
|
||||||
|
char* log = (char*)malloc(logLen);
|
||||||
|
glGetShaderInfoLog(m_vtxShader, logLen, nullptr, log);
|
||||||
|
Log.report(logvisor::Error, "unable to compile vert source\n%s\n%s\n", log, VS);
|
||||||
|
free(log);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glShaderSource(m_fragShader, 1, &FS, nullptr);
|
||||||
|
glCompileShader(m_fragShader);
|
||||||
|
glGetShaderiv(m_fragShader, GL_COMPILE_STATUS, &status);
|
||||||
|
if (status != GL_TRUE)
|
||||||
|
{
|
||||||
|
GLint logLen;
|
||||||
|
glGetShaderiv(m_fragShader, GL_INFO_LOG_LENGTH, &logLen);
|
||||||
|
char* log = (char*)malloc(logLen);
|
||||||
|
glGetShaderInfoLog(m_fragShader, logLen, nullptr, log);
|
||||||
|
Log.report(logvisor::Error, "unable to compile frag source\n%s\n%s\n", log, FS);
|
||||||
|
free(log);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glAttachShader(m_program, m_vtxShader);
|
||||||
|
glAttachShader(m_program, m_fragShader);
|
||||||
|
|
||||||
|
glLinkProgram(m_program);
|
||||||
|
glGetProgramiv(m_program, GL_LINK_STATUS, &status);
|
||||||
|
if (status != GL_TRUE)
|
||||||
|
{
|
||||||
|
GLint logLen;
|
||||||
|
glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &logLen);
|
||||||
|
char* log = (char*)malloc(logLen);
|
||||||
|
glGetProgramInfoLog(m_program, logLen, nullptr, log);
|
||||||
|
Log.report(logvisor::Error, "unable to link shader program\n%s\n", log);
|
||||||
|
free(log);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glUseProgram(m_program);
|
||||||
|
m_uniLoc = glGetUniformBlockIndex(m_program, "UniformBlock");
|
||||||
|
|
||||||
|
glGenBuffers(1, &m_uniformBufferGL);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL);
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer), nullptr, GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
glGenBuffers(1, &m_aabbIBO);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_aabbIBO);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 19 * 4, &AABBIdxs, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glGenQueries(1, &m_query);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VISIRenderer::Model::Vert> VISIRenderer::AABBToVerts(const zeus::CAABox& aabb,
|
||||||
|
const zeus::CColor& color)
|
||||||
|
{
|
||||||
|
std::vector<Model::Vert> verts;
|
||||||
|
verts.resize(8);
|
||||||
|
|
||||||
|
for (int i=0 ; i<8 ; ++i)
|
||||||
|
verts[i].color = color;
|
||||||
|
|
||||||
|
verts[0].pos = aabb.min;
|
||||||
|
verts[1].pos = {aabb.max.x, aabb.min.y, aabb.min.z};
|
||||||
|
verts[2].pos = {aabb.min.x, aabb.min.y, aabb.max.z};
|
||||||
|
verts[3].pos = {aabb.max.x, aabb.min.y, aabb.max.z};
|
||||||
|
verts[4].pos = {aabb.min.x, aabb.max.y, aabb.max.z};
|
||||||
|
verts[5].pos = aabb.max;
|
||||||
|
verts[6].pos = {aabb.min.x, aabb.max.y, aabb.min.z};
|
||||||
|
verts[7].pos = {aabb.max.x, aabb.max.y, aabb.min.z};
|
||||||
|
|
||||||
|
return verts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static zeus::CColor ColorForIndex(int i)
|
||||||
|
{
|
||||||
|
return zeus::CColor((i & 0xff) / 255.f,
|
||||||
|
((i >> 8) & 0xff) / 255.f,
|
||||||
|
((i >> 16) & 0xff) / 255.f,
|
||||||
|
1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VISIRenderer::SetupVertexBuffersAndFormats()
|
||||||
|
{
|
||||||
|
for (Model& model : m_models)
|
||||||
|
{
|
||||||
|
glGenVertexArrays(1, &model.vao);
|
||||||
|
glGenBuffers(1, &model.vbo);
|
||||||
|
glGenBuffers(1, &model.ibo);
|
||||||
|
|
||||||
|
glBindVertexArray(model.vao);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, model.vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, model.verts.size() * sizeof(Model::Vert), model.verts.data(), GL_STATIC_DRAW);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0);
|
||||||
|
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.ibo);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, model.idxs.size() * 4, model.idxs.data(), GL_STATIC_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = m_models.size();
|
||||||
|
for (Entity& ent : m_entities)
|
||||||
|
{
|
||||||
|
glGenVertexArrays(1, &ent.vao);
|
||||||
|
glGenBuffers(1, &ent.vbo);
|
||||||
|
|
||||||
|
glBindVertexArray(ent.vao);
|
||||||
|
|
||||||
|
auto verts = AABBToVerts(ent.aabb, ColorForIndex(idx++));
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, ent.vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(Model::Vert), verts.data(), GL_STATIC_DRAW);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0);
|
||||||
|
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_aabbIBO);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Light& light : m_lights)
|
||||||
|
{
|
||||||
|
glGenVertexArrays(1, &light.vao);
|
||||||
|
glGenBuffers(1, &light.vbo);
|
||||||
|
|
||||||
|
glBindVertexArray(light.vao);
|
||||||
|
|
||||||
|
Model::Vert vert;
|
||||||
|
vert.pos = light.point;
|
||||||
|
vert.color = ColorForIndex(idx++);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, light.vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(Model::Vert), &vert, GL_STATIC_DRAW);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0);
|
||||||
|
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static zeus::CMatrix4f g_Proj;
|
||||||
|
|
||||||
|
static void CalculateProjMatrix()
|
||||||
|
{
|
||||||
|
float znear = 0.2f;
|
||||||
|
float zfar = 1000.f;
|
||||||
|
float tfov = std::tan(zeus::degToRad(90.f * 0.5f));
|
||||||
|
float top = znear * tfov;
|
||||||
|
float bottom = -top;
|
||||||
|
float right = znear * tfov;
|
||||||
|
float left = -right;
|
||||||
|
|
||||||
|
float rml = right - left;
|
||||||
|
float rpl = right + left;
|
||||||
|
float tmb = top - bottom;
|
||||||
|
float tpb = top + bottom;
|
||||||
|
float fpn = zfar + znear;
|
||||||
|
float fmn = zfar - znear;
|
||||||
|
|
||||||
|
g_Proj = zeus::CMatrix4f(2.f * znear / rml, 0.f, rpl / rml, 0.f,
|
||||||
|
0.f, 2.f * znear / tmb, tpb / tmb, 0.f,
|
||||||
|
0.f, 0.f, -fpn / fmn, -2.f * zfar * znear / fmn,
|
||||||
|
0.f, 0.f, -1.f, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const zeus::CMatrix4f LookMATs[] =
|
||||||
|
{
|
||||||
|
{ // Forward
|
||||||
|
1.f, 0.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 1.f, 0.f,
|
||||||
|
0.f, -1.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 0.f, 1.f
|
||||||
|
},
|
||||||
|
{ // Backward
|
||||||
|
-1.f, 0.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 1.f, 0.f,
|
||||||
|
0.f, 1.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 0.f, 1.f
|
||||||
|
},
|
||||||
|
{ // Up
|
||||||
|
1.f, 0.f, 0.f, 0.f,
|
||||||
|
0.f, -1.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, -1.f, 0.f,
|
||||||
|
0.f, 0.f, 0.f, 1.f
|
||||||
|
},
|
||||||
|
{ // Down
|
||||||
|
1.f, 0.f, 0.f, 0.f,
|
||||||
|
0.f, 1.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 1.f, 0.f,
|
||||||
|
0.f, 0.f, 0.f, 1.f
|
||||||
|
},
|
||||||
|
{ // Left
|
||||||
|
0.f, 1.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 1.f, 0.f,
|
||||||
|
1.f, 0.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 0.f, 1.f
|
||||||
|
},
|
||||||
|
{ // Right
|
||||||
|
0.f, -1.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 1.f, 0.f,
|
||||||
|
-1.f, 0.f, 0.f, 0.f,
|
||||||
|
0.f, 0.f, 0.f, 1.f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
void VISIRenderer::RenderPVSOpaque(RGBA8* bufOut, const zeus::CVector3f& pos, bool& needTransparent)
|
||||||
|
{
|
||||||
|
glViewport(0, 0, 768, 512);
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glClearColor(1.f, 1.f, 1.f, 1.f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
for (int j=0 ; j<6 ; ++j)
|
||||||
|
{
|
||||||
|
GLint x = (j % 3) * 256;
|
||||||
|
GLint y = (j / 3) * 256;
|
||||||
|
glViewport(x, y, 256, 256);
|
||||||
|
|
||||||
|
zeus::CMatrix4f mv = LookMATs[j] * zeus::CTransform::Translate(-pos).toMatrix4f();
|
||||||
|
m_uniformBuffer.m_xf = g_Proj * mv;
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL);
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer), &m_uniformBuffer, GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
glUniformBlockBinding(m_program, m_uniLoc, 0);
|
||||||
|
glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, 0, sizeof(UniformBuffer));
|
||||||
|
|
||||||
|
zeus::CFrustum frustum;
|
||||||
|
frustum.updatePlanes(mv, g_Proj);
|
||||||
|
|
||||||
|
for (const Model& model : m_models)
|
||||||
|
{
|
||||||
|
if (!frustum.aabbFrustumTest(model.aabb))
|
||||||
|
continue;
|
||||||
|
glBindVertexArray(model.vao);
|
||||||
|
for (const Model::Surface& surf : model.surfaces)
|
||||||
|
{
|
||||||
|
// Non-transparents first
|
||||||
|
if (!surf.transparent)
|
||||||
|
glDrawElements(model.topology, surf.count, GL_UNSIGNED_INT,
|
||||||
|
reinterpret_cast<void*>(surf.first * 4));
|
||||||
|
else
|
||||||
|
needTransparent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//m_swapFunc();
|
||||||
|
glFinish();
|
||||||
|
glReadPixels(0, 0, 768, 512, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)bufOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISIRenderer::RenderPVSTransparent(const std::function<void(int)>& passFunc, const zeus::CVector3f& pos)
|
||||||
|
{
|
||||||
|
glDepthMask(GL_FALSE);
|
||||||
|
|
||||||
|
for (int j=0 ; j<6 ; ++j)
|
||||||
|
{
|
||||||
|
GLint x = (j % 3) * 256;
|
||||||
|
GLint y = (j / 3) * 256;
|
||||||
|
glViewport(x, y, 256, 256);
|
||||||
|
|
||||||
|
zeus::CMatrix4f mv = LookMATs[j] * zeus::CTransform::Translate(-pos).toMatrix4f();
|
||||||
|
m_uniformBuffer.m_xf = g_Proj * mv;
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL);
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer), &m_uniformBuffer, GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
glUniformBlockBinding(m_program, m_uniLoc, 0);
|
||||||
|
glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, 0, sizeof(UniformBuffer));
|
||||||
|
|
||||||
|
zeus::CFrustum frustum;
|
||||||
|
frustum.updatePlanes(mv, g_Proj);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (const Model& model : m_models)
|
||||||
|
{
|
||||||
|
if (!frustum.aabbFrustumTest(model.aabb))
|
||||||
|
{
|
||||||
|
++idx;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
glBindVertexArray(model.vao);
|
||||||
|
for (const Model::Surface& surf : model.surfaces)
|
||||||
|
{
|
||||||
|
// transparents
|
||||||
|
if (surf.transparent)
|
||||||
|
{
|
||||||
|
glBeginQuery(GL_ANY_SAMPLES_PASSED, m_query);
|
||||||
|
glDrawElements(model.topology, surf.count, GL_UNSIGNED_INT,
|
||||||
|
reinterpret_cast<void*>(surf.first * 4));
|
||||||
|
glEndQuery(GL_ANY_SAMPLES_PASSED);
|
||||||
|
GLint res;
|
||||||
|
glGetQueryObjectiv(m_query, GL_QUERY_RESULT, &res);
|
||||||
|
if (res)
|
||||||
|
passFunc(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISIRenderer::RenderPVSEntitiesAndLights(const std::function<void(int)>& passFunc,
|
||||||
|
const std::function<void(int, EPVSVisSetState)>& lightPassFunc,
|
||||||
|
const zeus::CVector3f& pos)
|
||||||
|
{
|
||||||
|
glDepthMask(GL_FALSE);
|
||||||
|
|
||||||
|
for (int j=0 ; j<6 ; ++j)
|
||||||
|
{
|
||||||
|
GLint x = (j % 3) * 256;
|
||||||
|
GLint y = (j / 3) * 256;
|
||||||
|
glViewport(x, y, 256, 256);
|
||||||
|
|
||||||
|
zeus::CMatrix4f mv = LookMATs[j] * zeus::CTransform::Translate(-pos).toMatrix4f();
|
||||||
|
m_uniformBuffer.m_xf = g_Proj * mv;
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL);
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer), &m_uniformBuffer, GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
glUniformBlockBinding(m_program, m_uniLoc, 0);
|
||||||
|
glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, 0, sizeof(UniformBuffer));
|
||||||
|
|
||||||
|
zeus::CFrustum frustum;
|
||||||
|
frustum.updatePlanes(mv, g_Proj);
|
||||||
|
|
||||||
|
int idx = m_models.size();
|
||||||
|
for (const Entity& ent : m_entities)
|
||||||
|
{
|
||||||
|
if (!frustum.aabbFrustumTest(ent.aabb))
|
||||||
|
{
|
||||||
|
++idx;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
glBindVertexArray(ent.vao);
|
||||||
|
glBeginQuery(GL_ANY_SAMPLES_PASSED, m_query);
|
||||||
|
glDrawElements(GL_TRIANGLE_STRIP, 19, GL_UNSIGNED_INT, 0);
|
||||||
|
glEndQuery(GL_ANY_SAMPLES_PASSED);
|
||||||
|
GLint res;
|
||||||
|
glGetQueryObjectiv(m_query, GL_QUERY_RESULT, &res);
|
||||||
|
if (res)
|
||||||
|
passFunc(idx);
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lightIdx = 0;
|
||||||
|
for (const Light& light : m_lights)
|
||||||
|
{
|
||||||
|
if (!frustum.pointFrustumTest(light.point))
|
||||||
|
{
|
||||||
|
++idx;
|
||||||
|
++lightIdx;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
glBindVertexArray(light.vao);
|
||||||
|
glBeginQuery(GL_ANY_SAMPLES_PASSED, m_query);
|
||||||
|
glDrawArrays(GL_POINTS, 0, 1);
|
||||||
|
glEndQuery(GL_ANY_SAMPLES_PASSED);
|
||||||
|
GLint res;
|
||||||
|
glGetQueryObjectiv(m_query, GL_QUERY_RESULT, &res);
|
||||||
|
EPVSVisSetState state = m_totalAABB.pointInside(light.point) ?
|
||||||
|
EPVSVisSetState::EndOfTree : EPVSVisSetState::OutOfBounds;
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
passFunc(idx);
|
||||||
|
if (state == EPVSVisSetState::EndOfTree)
|
||||||
|
state = EPVSVisSetState::NodeFound;
|
||||||
|
}
|
||||||
|
lightPassFunc(lightIdx, state);
|
||||||
|
++idx;
|
||||||
|
++lightIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISIRenderer::Run(FPercent updatePercent)
|
||||||
|
{
|
||||||
|
m_updatePercent = updatePercent;
|
||||||
|
CalculateProjMatrix();
|
||||||
|
|
||||||
|
if (glewInit() != GLEW_OK)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "unable to init glew");
|
||||||
|
m_return = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GLEW_ARB_occlusion_query2)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "GL_ARB_occlusion_query2 extension not present");
|
||||||
|
m_return = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetupShaders())
|
||||||
|
{
|
||||||
|
m_return = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_argc < 3)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "Missing input/output file args");
|
||||||
|
m_return = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
athena::io::FileReader r(m_argv[1]);
|
||||||
|
if (r.hasError())
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t modelCount = r.readUint32Big();
|
||||||
|
m_models.resize(modelCount);
|
||||||
|
for (uint32_t i=0 ; i<modelCount ; ++i)
|
||||||
|
{
|
||||||
|
zeus::CColor color = ColorForIndex(i);
|
||||||
|
Model& model = m_models[i];
|
||||||
|
uint32_t topology = r.readUint32Big();
|
||||||
|
model.topology = topology ? GL_TRIANGLE_STRIP : GL_TRIANGLES;
|
||||||
|
|
||||||
|
uint32_t vertCount = r.readUint32Big();
|
||||||
|
model.verts.reserve(vertCount);
|
||||||
|
for (uint32_t j=0 ; j<vertCount ; ++j)
|
||||||
|
{
|
||||||
|
model.verts.emplace_back();
|
||||||
|
Model::Vert& vert = model.verts.back();
|
||||||
|
vert.pos = r.readVec3fBig();
|
||||||
|
vert.color = color;
|
||||||
|
m_totalAABB.accumulateBounds(vert.pos);
|
||||||
|
model.aabb.accumulateBounds(vert.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t surfCount = r.readUint32Big();
|
||||||
|
model.surfaces.resize(surfCount);
|
||||||
|
uint32_t curIdx = 0;
|
||||||
|
for (uint32_t j=0 ; j<surfCount ; ++j)
|
||||||
|
{
|
||||||
|
Model::Surface& surf = model.surfaces[j];
|
||||||
|
surf.first = curIdx;
|
||||||
|
surf.count = r.readUint32Big();
|
||||||
|
curIdx += surf.count;
|
||||||
|
|
||||||
|
for (uint32_t k=0 ; k<surf.count ; ++k)
|
||||||
|
{
|
||||||
|
uint32_t idx = r.readUint32Big();
|
||||||
|
model.idxs.push_back(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
surf.transparent = r.readBool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t entityCount = r.readUint32Big();
|
||||||
|
m_entities.resize(entityCount);
|
||||||
|
for (uint32_t i=0 ; i<entityCount ; ++i)
|
||||||
|
{
|
||||||
|
Entity& ent = m_entities[i];
|
||||||
|
ent.entityId = r.readUint32Big();
|
||||||
|
ent.aabb.min = r.readVec3fBig();
|
||||||
|
ent.aabb.max = r.readVec3fBig();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t lightCount = r.readUint32Big();
|
||||||
|
m_lights.resize(lightCount);
|
||||||
|
for (uint32_t i=0 ; i<lightCount ; ++i)
|
||||||
|
{
|
||||||
|
Light& light = m_lights[i];
|
||||||
|
light.point = r.readVec3fBig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetupVertexBuffersAndFormats())
|
||||||
|
{
|
||||||
|
m_return = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VISIBuilder builder(*this);
|
||||||
|
std::vector<uint8_t> dataOut = builder.build(m_totalAABB, m_models.size(),
|
||||||
|
m_entities, m_lights, m_updatePercent);
|
||||||
|
if (dataOut.empty())
|
||||||
|
{
|
||||||
|
m_return = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
athena::io::FileWriter w(m_argv[2]);
|
||||||
|
w.writeUBytes(dataOut.data(), dataOut.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISIRenderer::Terminate()
|
||||||
|
{
|
||||||
|
m_terminate = true;
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
#ifndef VISIRENDERER_HPP
|
||||||
|
#define VISIRENDERER_HPP
|
||||||
|
|
||||||
|
#include "boo/graphicsdev/glew.h"
|
||||||
|
#include "hecl/hecl.hpp"
|
||||||
|
#include "zeus/CColor.hpp"
|
||||||
|
#include "zeus/CMatrix4f.hpp"
|
||||||
|
#include "zeus/CAABox.hpp"
|
||||||
|
|
||||||
|
typedef void(*FPercent)(float);
|
||||||
|
|
||||||
|
enum class EPVSVisSetState
|
||||||
|
{
|
||||||
|
EndOfTree,
|
||||||
|
NodeFound,
|
||||||
|
OutOfBounds
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIRenderer
|
||||||
|
{
|
||||||
|
friend struct VISIBuilder;
|
||||||
|
|
||||||
|
int m_argc;
|
||||||
|
const hecl::SystemChar** m_argv;
|
||||||
|
int m_return = 0;
|
||||||
|
|
||||||
|
zeus::CAABox m_totalAABB;
|
||||||
|
|
||||||
|
struct UniformBuffer
|
||||||
|
{
|
||||||
|
zeus::CMatrix4f m_xf;
|
||||||
|
} m_uniformBuffer;
|
||||||
|
|
||||||
|
struct Model
|
||||||
|
{
|
||||||
|
GLenum topology;
|
||||||
|
zeus::CAABox aabb;
|
||||||
|
|
||||||
|
struct Vert
|
||||||
|
{
|
||||||
|
zeus::CVector3f pos;
|
||||||
|
zeus::CColor color;
|
||||||
|
};
|
||||||
|
std::vector<Vert> verts;
|
||||||
|
|
||||||
|
std::vector<uint32_t> idxs;
|
||||||
|
GLuint vbo, ibo, vao;
|
||||||
|
|
||||||
|
struct Surface
|
||||||
|
{
|
||||||
|
uint32_t first;
|
||||||
|
uint32_t count;
|
||||||
|
bool transparent;
|
||||||
|
};
|
||||||
|
std::vector<Surface> surfaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Entity
|
||||||
|
{
|
||||||
|
uint32_t entityId;
|
||||||
|
zeus::CAABox aabb;
|
||||||
|
GLuint vbo, vao;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Light
|
||||||
|
{
|
||||||
|
zeus::CVector3f point;
|
||||||
|
GLuint vbo, vao;
|
||||||
|
};
|
||||||
|
|
||||||
|
GLuint m_vtxShader, m_fragShader, m_program, m_uniLoc;
|
||||||
|
GLuint m_uniformBufferGL;
|
||||||
|
GLuint m_aabbIBO;
|
||||||
|
bool SetupShaders();
|
||||||
|
|
||||||
|
std::vector<Model> m_models;
|
||||||
|
std::vector<Entity> m_entities;
|
||||||
|
std::vector<Light> m_lights;
|
||||||
|
bool SetupVertexBuffersAndFormats();
|
||||||
|
|
||||||
|
GLuint m_query;
|
||||||
|
|
||||||
|
FPercent m_updatePercent;
|
||||||
|
|
||||||
|
static std::vector<Model::Vert> AABBToVerts(const zeus::CAABox& aabb,
|
||||||
|
const zeus::CColor& color);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool m_terminate = false;
|
||||||
|
struct RGBA8
|
||||||
|
{
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
uint8_t a;
|
||||||
|
};
|
||||||
|
|
||||||
|
VISIRenderer(int argc, const hecl::SystemChar** argv) : m_argc(argc), m_argv(argv) {}
|
||||||
|
void Run(FPercent updatePercent);
|
||||||
|
void Terminate();
|
||||||
|
void RenderPVSOpaque(RGBA8* bufOut, const zeus::CVector3f& pos, bool& needTransparent);
|
||||||
|
void RenderPVSTransparent(const std::function<void(int)>& passFunc, const zeus::CVector3f& pos);
|
||||||
|
void RenderPVSEntitiesAndLights(const std::function<void(int)>& passFunc,
|
||||||
|
const std::function<void(int, EPVSVisSetState)>& lightPassFunc,
|
||||||
|
const zeus::CVector3f& pos);
|
||||||
|
int ReturnVal() const { return m_return; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VISIRENDERER_HPP
|
Loading…
Reference in New Issue