Add VISIGen utility

This commit is contained in:
Jack Andersen 2017-02-23 22:28:44 -10:00
parent 50fe6d34ab
commit ccbd19b9ea
21 changed files with 1578 additions and 15 deletions

View File

@ -121,6 +121,7 @@ set(CLIENT_SOURCES
add_subdirectory(Runtime)
add_subdirectory(mpcksum)
add_subdirectory(gbalink)
add_subdirectory(visigen)
unset(GIT_EXECUTABLE CACHE)
find_package(Git)

View File

@ -5,6 +5,9 @@
#include "zeus/Math.hpp"
#include "zeus/CAABox.hpp"
#include "DataSpec/DNACommon/AROTBuilder.hpp"
#include "ScriptObjects/ScriptTypes.hpp"
extern const hecl::SystemString ExeDir;
namespace DataSpec
{
@ -162,6 +165,26 @@ bool MREA::Extract(const SpecBase& dataSpec,
ReadBabeDeadToBlender_1_2(os, rs);
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 */
os << "bpy.context.scene.layers[1] = True\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 std::vector<DNACMDL::Mesh>& meshes,
const ColMesh& cMesh,
const std::vector<Light>& lights)
const std::vector<Light>& lights,
hecl::BlenderToken& btok)
{
/* Discover area layers */
hecl::ProjectPath areaDirPath = inPath.getParentPath();
@ -322,13 +346,13 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
/* AROT */
{
AROTBuilder builder;
builder.build(secs, fullAabb, meshAabbs, meshes);
AROTBuilder arotBuilder;
arotBuilder.build(secs, fullAabb, meshAabbs, meshes);
}
/* SCLY */
DNAMP1::SCLY sclyData = {};
{
DNAMP1::SCLY sclyData = {};
sclyData.fourCC = 'SCLY';
sclyData.version = 1;
for (const hecl::ProjectPath& layer : layerScriptPaths)
@ -373,13 +397,13 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
}
/* Lights */
std::vector<atVec3f> lightsVisi;
{
int actualCount = 0;
for (const Light& l : lights)
{
if (l.layer == 0 || l.layer == 1)
++actualCount;
}
lightsVisi.reserve(actualCount);
secs.emplace_back(12 + 65 * actualCount, 0);
athena::io::MemoryWriter w(secs.back().data(), secs.back().size());
@ -401,15 +425,100 @@ bool MREA::PCCook(const hecl::ProjectPath& outPath,
BabeDeadLight light = {};
WriteBabeDeadLightFromBlender(light, l);
light.write(w);
lightsVisi.push_back(light.position);
}
}
}
}
/* VISI */
hecl::ProjectPath visiMetadataPath(areaDirPath, _S("!visi.yaml"));
if (visiMetadataPath.isFile())
{
/* Empty (for now) */
secs.emplace_back(0, 0);
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);
}
/* PATH */

View File

@ -135,7 +135,8 @@ struct MREA
const hecl::ProjectPath& inPath,
const std::vector<DNACMDL::Mesh>& meshes,
const ColMesh& cMesh,
const std::vector<Light>& lights);
const std::vector<Light>& lights,
hecl::BlenderToken& btok);
};
}

View File

@ -16,7 +16,7 @@ struct Actor : IScriptObject
Value<atVec3f> orientation;
Value<atVec3f> scale;
Value<atVec3f> collisionExtent;
Value<atVec3f> centroid;
Value<atVec3f> collisionOffset;
Value<float> unknown2;
Value<float> unknown3;
HealthInfo healthInfo;
@ -64,6 +64,36 @@ struct Actor : IScriptObject
{
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);
}
};
}
}

View File

@ -50,6 +50,13 @@ struct DamageableTrigger : IScriptObject
g_curSpec->flattenDependencies(texture2, 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);
}
};
}
}

View File

@ -19,8 +19,8 @@ struct DoorArea : IScriptObject
AnimationParameters animationParameters;
ActorParameters actorParameters;
Value<atVec3f> unknown1;
Value<atVec3f> unknown2;
Value<atVec3f> unknown3;
Value<atVec3f> collisionExtent;
Value<atVec3f> collisionOffset;
Value<bool> unknown4;
Value<bool> unknown5;
Value<bool> unknown6;
@ -49,6 +49,28 @@ struct DoorArea : IScriptObject
{
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);
}
};
}
}

View File

@ -267,5 +267,16 @@ const std::vector<const struct ScriptObjectSpec*> SCRIPT_OBJECT_DB =
&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;
}
}
}

View File

@ -3,12 +3,18 @@
#include "../../DNACommon/DNACommon.hpp"
#include "../DNAMP1.hpp"
#include "../SAVW.hpp"
#include "zeus/CAABox.hpp"
#include <stdio.h>
namespace DataSpec
{
namespace DNAMP1
{
zeus::CTransform ConvertEditorEulerToTransform4f(const zeus::CVector3f& scale,
const zeus::CVector3f& orientation,
const zeus::CVector3f& position);
struct IScriptObject : BigYAML
{
DECL_YAML
@ -32,6 +38,7 @@ struct IScriptObject : BigYAML
virtual void nameIDs(PAKRouter<PAKBridge>& pakRouter) const {}
virtual void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {}
virtual void gatherScans(std::vector<Scan>& scansOut) const {}
virtual zeus::CAABox getVISIAABB(hecl::BlenderToken& btok) const { return {}; }
};
}
}

View File

@ -66,6 +66,36 @@ struct Platform : IScriptObject
{
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);
}
};
}
}

View File

@ -21,6 +21,13 @@ struct Trigger : IScriptObject
Value<bool> active;
Value<bool> unknown2;
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);
}
};
}
}

View File

@ -173,6 +173,13 @@ struct Water : IScriptObject
g_curSpec->flattenDependencies(particle4, 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);
}
};
}
}

View File

@ -381,6 +381,7 @@ struct SpecMP1 : SpecBase
std::unique_lock<std::mutex> lk(msgLock);
progress(sysName.c_str(), _S(""), compIdx, 0.0);
}
hecl::SystemString pakName = sysName.sys_str();
process.addLambdaTransaction([&, pakName](hecl::BlenderToken& btok) {
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();
ds.close();
if (m_pc)
DNAMP1::MREA::PCCook(out, in, meshCompiles, *colMesh, lights);
DNAMP1::MREA::PCCook(out, in, meshCompiles, *colMesh, lights, btok);
else
DNAMP1::MREA::Cook(out, in, meshCompiles, *colMesh, lights);
}

View File

@ -123,6 +123,9 @@ struct Application : boo::IApplicationCallback
}
static hecl::SystemChar CwdBuf[1024];
hecl::SystemString ExeDir;
#if _WIN32
int wmain(int argc, const boo::SystemChar** argv)
#else
@ -132,6 +135,17 @@ int main(int argc, const boo::SystemChar** argv)
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
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;
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto,
appCb, _S("urde"), _S("URDE"), argc, argv, false);

2
hecl

@ -1 +1 @@
Subproject commit f407c84e78721e6e682bfc7790bfb0c68a886453
Subproject commit 1a9260afd0a5bcf9e77203bd4b5cde4334eb1b3f

@ -1 +1 @@
Subproject commit 03d074af13e902cebdf05d6f5ec5e4c2cbf569ee
Subproject commit 67a7efea539964b2f2a816135ee5719e5ae104a1

21
visigen/CMakeLists.txt Normal file
View File

@ -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>)

122
visigen/MainMac.mm Normal file
View File

@ -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();
}

369
visigen/VISIBuilder.cpp Normal file
View File

@ -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;
}

131
visigen/VISIBuilder.hpp Normal file
View File

@ -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_

562
visigen/VISIRenderer.cpp Normal file
View File

@ -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;
}

109
visigen/VISIRenderer.hpp Normal file
View File

@ -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