Files
healer-man/scripts/runewaker-pipeline/native/runewaker_zone_lighting_exporter.cpp
T
2026-10-06 22:25:34 -04:00

287 lines
11 KiB
C++

#include <windows.h>
#include <cstdint>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include "Rune Engine/Rune/Rune.h"
#include "Rune Engine/Rune/Rune Engine NULL.h"
#include "Rune Engine/Rune/Core/DataStore/RuDataStore_Disk.h"
#include "Rune Engine/Rune/Helper/RuHelper_Math.h"
#include "Rune Engine/Rune/Scene/Terrain/RuWorld_EntitySystem.h"
#include "Rune Engine/Rune/Scene/Terrain/RuWorld_ZoneDescriptorContainer.h"
namespace
{
std::string JsonEscape(const char *value)
{
std::string out;
for(const char *p = value ? value : ""; *p; ++p)
{
if(*p == '\\' || *p == '"')
{
out += '\\';
out += *p;
}
else if(*p == '\n') out += "\\n";
else if(*p == '\r') out += "\\r";
else if(*p == '\t') out += "\\t";
else out += *p;
}
return out;
}
void WriteColor(std::ostream &out, DWORD color)
{
out << "{\"argb\":\"0x" << std::uppercase << std::hex << std::setw(8) << std::setfill('0') << color
<< std::dec << std::setfill(' ') << "\",\"rgba\":["
<< ((color >> 16) & 0xFF) / 255.0 << ','
<< ((color >> 8) & 0xFF) / 255.0 << ','
<< (color & 0xFF) / 255.0 << ','
<< ((color >> 24) & 0xFF) / 255.0 << "]}";
}
void WriteVector(std::ostream &out, const CRuVector3 &vector)
{
out << '[' << vector.x << ',' << vector.y << ',' << vector.z << ']';
}
}
int main(int argc, char **argv)
{
if(argc != 3 && argc != 6)
{
std::cerr << "Usage: runewaker_zone_lighting_exporter <wdb-path> <output-json> [probe-x probe-y probe-z]\n";
return 2;
}
if(RuInitialize_Core() == FALSE || RuInitialize_NULL() == FALSE)
{
std::cerr << "Rune engine initialization failed.\n";
return 3;
}
CRuDataStore_Disk *dataStore = ruNEW CRuDataStore_Disk();
if(dataStore->Open(argv[1], TRUE) == FALSE)
{
std::cerr << "Unable to open WDB: " << argv[1] << "\n";
ruSAFE_RELEASE(dataStore);
RuShutdown();
return 4;
}
CRuWorld_ZoneDescriptorContainer *container = ruNEW CRuWorld_ZoneDescriptorContainer();
if(container->Attach(dataStore) == FALSE)
{
std::cerr << "Unable to attach world-zone descriptor table.\n";
ruSAFE_RELEASE(container);
ruSAFE_RELEASE(dataStore);
RuShutdown();
return 5;
}
CRuWorld_ObjectContainer *objectContainer = ruNEW CRuWorld_ObjectContainer(
CRuAABB(CRuVector3(-100000.0f, -100000.0f, -100000.0f), CRuVector3(100000.0f, 100000.0f, 100000.0f)),
1000.0f);
if(objectContainer->Attach(dataStore) == FALSE)
{
std::cerr << "Unable to attach world object table.\n";
ruSAFE_RELEASE(objectContainer);
ruSAFE_RELEASE(container);
ruSAFE_RELEASE(dataStore);
RuShutdown();
return 7;
}
CRuArrayList<IRuObject *> triggers;
objectContainer->EnumerateAllObjectsByType(CRuWorld_Trigger::Type(), triggers);
CRuWorld_Trigger *entryZoneTrigger = NULL;
INT32 entryZoneTriggerIndex = -1;
INT32 entryZonePriority = 0xFFFFFFFF;
REAL entryZoneDistance = FLT_MAX;
CRuVector3 entryProbe;
const BOOL hasEntryProbe = argc == 6;
if(hasEntryProbe)
{
entryProbe.Set(static_cast<REAL>(atof(argv[3])), static_cast<REAL>(atof(argv[4])), static_cast<REAL>(atof(argv[5])));
CRuCollisionQuery_Segment_Nearest query;
CRuMatrix4x4 identityMatrix;
query.m_worldTransform = &identityMatrix;
query.m_invWorldTransform = &identityMatrix;
query.m_ignoreCollisionFlag = TRUE;
CRuSegment3 segments[2] = {
CRuSegment3(entryProbe, entryProbe + CRuVector3(0.0f, -10000.0f, 0.0f)),
CRuSegment3(entryProbe, entryProbe + CRuVector3(0.0f, 10000.0f, 0.0f))
};
for(INT32 ti = 0; ti < triggers.Count(); ++ti)
{
CRuWorld_Trigger *trigger = static_cast<CRuWorld_Trigger *>(triggers[ti]);
if(trigger->GetTriggerType() != RuWorld::ruTRIGGERTYPE_ZONE) continue;
RuPropertyType propertyValue;
const INT32 priority = trigger->GetProperty("CRuWorld_Trigger:Priority", propertyValue)
? boost::get<INT32>(propertyValue) : 0;
for(INT32 si = 0; si < 2; ++si)
{
query.m_positiveHit = FALSE;
query.m_collisionT = FLT_MAX;
query.m_colSegment = segments[si];
trigger->QueryCollision(&query);
if(!query.m_positiveHit) continue;
const REAL distance = query.m_collisionT;
BOOL prefer = priority > entryZonePriority
|| (priority == entryZonePriority && distance < entryZoneDistance);
if(!prefer && priority == entryZonePriority && distance == entryZoneDistance && entryZoneTrigger)
{
INT32 parentID = trigger->GetZoneID();
CRuWorld_ZoneDescriptor *parent = container->GetZoneDescriptor(parentID);
while(parent)
{
if(parent->GetZoneID() == entryZoneTrigger->GetZoneID())
{
prefer = TRUE;
break;
}
parent = container->GetZoneDescriptor(parent->GetParentZoneID());
}
}
if(prefer)
{
entryZoneTrigger = trigger;
entryZoneTriggerIndex = ti;
entryZonePriority = priority;
entryZoneDistance = distance;
}
}
}
}
std::ofstream out(argv[2], std::ios::out | std::ios::trunc);
if(!out)
{
std::cerr << "Unable to write JSON: " << argv[2] << "\n";
ruSAFE_RELEASE(container);
ruSAFE_RELEASE(dataStore);
RuShutdown();
return 6;
}
out << std::setprecision(9) << "{\n \"sourceWdb\": \"" << JsonEscape(argv[1])
<< "\",\n \"zoneDescriptorCount\": " << container->GetNumZoneDescriptors() << ",\n \"zones\": [\n";
for(INT32 zi = 0; zi < container->GetNumZoneDescriptors(); ++zi)
{
CRuWorld_ZoneDescriptor *zone = container->GetZoneDescriptorByIndex(zi);
out << " {\"id\":" << zone->GetZoneID() << ",\"parentId\":" << zone->GetParentZoneID()
<< ",\"name\":\"" << JsonEscape(zone->GetName()) << "\",\"zoneType\":"
<< static_cast<INT32>(zone->GetZoneType()) << ",\"settings\":[";
for(INT32 si = 0; si < zone->GetNumZoneSettings(); ++si)
{
CRuWorld_ZoneSettings *s = zone->GetZoneSettingsByIndex(si);
if(si) out << ',';
out << "{\"timeGroup\":" << s->m_timeGroup << ",\"weatherGroup\":" << s->m_weatherGroup
<< ",\"primary\":"; WriteColor(out, s->m_color_Primary);
out << ",\"secondary\":"; WriteColor(out, s->m_color_Secondary);
out << ",\"ambient\":"; WriteColor(out, s->m_color_Ambient);
out << ",\"specular\":"; WriteColor(out, s->m_color_Specular);
out << ",\"fogColor\":"; WriteColor(out, s->m_color_Fog);
out << ",\"fog\":{\"constant\":" << s->m_fog_Constant << ",\"start\":" << s->m_fog_Start
<< ",\"end\":" << s->m_fog_End << "},\"standardAnglesRadians\":["
<< s->m_theta_Standard << ',' << s->m_phi_Standard << "],\"specularAnglesRadians\":["
<< s->m_theta_Specular << ',' << s->m_phi_Specular << "],\"nativeDirectionsLeftHandedYUp\":{\"primary\":";
WriteVector(out, RuMathHelper_GenerateLightingVector(s->m_theta_Standard, s->m_phi_Standard));
out << ",\"secondary0\":";
WriteVector(out, RuMathHelper_GenerateLightingVector(s->m_theta_Standard + ruPI * 2.0f / 3.0f, 0.0f));
out << ",\"secondary1\":";
WriteVector(out, RuMathHelper_GenerateLightingVector(s->m_theta_Standard + ruPI * 4.0f / 3.0f, 0.0f));
out << ",\"specular\":";
WriteVector(out, RuMathHelper_GenerateLightingVector(s->m_theta_Specular, s->m_phi_Specular) * -1.0f);
out << "},\"weatherSystem\":\""
<< JsonEscape(s->m_weatherSystem) << "\",\"skyGradientTexture\":\""
<< JsonEscape(s->m_skyGradientTextureName) << "\",\"customSkyBox\":\""
<< JsonEscape(s->m_customSkyBoxName) << "\",\"cloudCover\":" << s->m_cloudCover
<< ",\"cloudSharpness\":" << s->m_cloudSharpness << ",\"cloudColors\":[";
WriteColor(out, s->m_cloudColor0);
out << ',';
WriteColor(out, s->m_cloudColor1);
out << "]}";
}
out << "]}" << (zi + 1 < container->GetNumZoneDescriptors() ? "," : "") << "\n";
}
INT32 zoneTriggerCount = 0;
for(INT32 ti = 0; ti < triggers.Count(); ++ti)
{
if(static_cast<CRuWorld_Trigger *>(triggers[ti])->GetTriggerType() == RuWorld::ruTRIGGERTYPE_ZONE)
{
++zoneTriggerCount;
}
}
out << " ],\n \"zoneTriggerCount\": " << zoneTriggerCount << ",\n \"zoneTriggers\": [\n";
INT32 writtenZoneTriggers = 0;
for(INT32 ti = 0; ti < triggers.Count(); ++ti)
{
CRuWorld_Trigger *trigger = static_cast<CRuWorld_Trigger *>(triggers[ti]);
if(trigger->GetTriggerType() != RuWorld::ruTRIGGERTYPE_ZONE) continue;
CRuOBB bounds = trigger->GetTriggerBounds();
const CRuVector3 &center = bounds.Center();
const CRuVector3 *axes = bounds.Axis();
const float *extents = bounds.Extents();
RuPropertyType propertyValue;
const INT32 priority = trigger->GetProperty("CRuWorld_Trigger:Priority", propertyValue)
? boost::get<INT32>(propertyValue) : 0;
out << " {\"triggerType\":" << static_cast<INT32>(trigger->GetTriggerType())
<< ",\"zoneId\":" << trigger->GetZoneID() << ",\"zoneType\":"
<< static_cast<INT32>(trigger->GetZoneType()) << ",\"priority\":" << priority << ",\"command\":\""
<< JsonEscape(trigger->GetCommandString()) << "\",\"obb\":{\"center\":["
<< center.x << ',' << center.y << ',' << center.z << "],\"axes\":["
<< '[' << axes[0].x << ',' << axes[0].y << ',' << axes[0].z << "],"
<< '[' << axes[1].x << ',' << axes[1].y << ',' << axes[1].z << "],"
<< '[' << axes[2].x << ',' << axes[2].y << ',' << axes[2].z << "]],\"halfExtents\":["
<< extents[0] << ',' << extents[1] << ',' << extents[2] << "]}}"
<< (++writtenZoneTriggers < zoneTriggerCount ? "," : "") << "\n";
}
out << " ],\n \"entryZoneSelection\":";
if(!hasEntryProbe)
{
out << "null\n";
}
else
{
out << "{\"probe\":[" << entryProbe.x << ',' << entryProbe.y << ',' << entryProbe.z << "],\"triggerIndex\":"
<< entryZoneTriggerIndex << ",\"triggerZoneId\":";
if(entryZoneTrigger) out << entryZoneTrigger->GetZoneID(); else out << "null";
out << ",\"triggerPriority\":";
if(entryZoneTrigger) out << entryZonePriority; else out << "null";
out << ",\"boundaryDistance\":";
if(entryZoneTrigger) out << entryZoneDistance; else out << "null";
out << ",\"settingsZoneId\":";
INT32 settingsZoneID = -1;
CRuWorld_ZoneSettings *entrySettings = NULL;
CRuWorld_ZoneDescriptor *selectedZone = entryZoneTrigger
? container->GetZoneDescriptor(entryZoneTrigger->GetZoneID()) : NULL;
while(selectedZone)
{
entrySettings = selectedZone->GetZoneSettings(0, 0);
if(entrySettings)
{
settingsZoneID = selectedZone->GetZoneID();
break;
}
selectedZone = container->GetZoneDescriptor(selectedZone->GetParentZoneID());
}
if(entrySettings) out << settingsZoneID; else out << "null";
out << ",\"timeGroup\":0,\"weatherGroup\":0,\"selectedTimeGroup\":";
if(entrySettings) out << entrySettings->m_timeGroup; else out << "null";
out << ",\"selectedWeatherGroup\":";
if(entrySettings) out << entrySettings->m_weatherGroup; else out << "null";
out << "}\n";
}
out << "}\n";
out.close();
std::cout << "Wrote " << container->GetNumZoneDescriptors() << " source zone descriptors.\n";
for(INT32 ti = 0; ti < triggers.Count(); ++ti) ruSAFE_RELEASE(triggers[ti]);
ruSAFE_RELEASE(objectContainer);
ruSAFE_RELEASE(container);
ruSAFE_RELEASE(dataStore);
RuShutdown();
return 0;
}