287 lines
11 KiB
C++
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 ¢er = 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;
|
|
}
|