newdark: fixed lights not loading, almost fixed orientation loading wrong, (to-do: fix prop materials sometimes being wrong)

This commit is contained in:
ecker 2026-07-17 11:55:20 -05:00
parent c5e49e59eb
commit 9520e33d75
6 changed files with 132 additions and 85 deletions

View File

@ -116,7 +116,7 @@
"deferred": true,
"gui": true,
"vsync": true, // vsync on vulkan side rather than engine-side
"hdr": false,
"hdr": true,
"vxgi": false, // to-do: fix issues
"culling": false,
"bloom": true,

View File

@ -53,6 +53,27 @@ namespace impl {
out[3] = (uint8_t)(std::clamp(exponent + 128, 0, 255));
}
inline pod::Vector3f hsvToRgb(float h, float s, float v) {
if (s <= 0.0f) return {v, v, v};
h = std::fmod(h, 1.0f) * 6.0f;
int i = (int)std::floor(h);
float f = h - (float)i;
float p = v * (1.0f - s);
float q = v * (1.0f - s * f);
float t = v * (1.0f - s * (1.0f - f));
switch (i) {
case 0: return {v, t, p};
case 1: return {q, v, p};
case 2: return {p, v, t};
case 3: return {p, q, v};
case 4: return {t, p, v};
default: return {v, p, q};
}
}
inline pod::Vector3f convertPos_NewDark( const pod::Vector3f& v, float scale = impl::darkToMeters ) {
return pod::Vector3f{ v.x, v.z, v.y } * scale;
}

View File

@ -336,19 +336,19 @@ template<typename T> T uf::matrix::rotate( const T& matrix, const pod::Vector3t<
T res = matrix;
if (vector.x != 0) {
T Rx = uf::matrix::identity<T>();
T Rx = uf::matrix::identity<typename T::type_t>();
Rx(1,1) = cos(vector.x); Rx(1,2) = -sin(vector.x);
Rx(2,1) = sin(vector.x); Rx(2,2) = cos(vector.x);
res = uf::matrix::multiply(res, Rx);
}
if (vector.y != 0) {
T Ry = uf::matrix::identity<T>();
T Ry = uf::matrix::identity<typename T::type_t>();
Ry(0,0) = cos(vector.y); Ry(0,2) = sin(vector.y);
Ry(2,0) = -sin(vector.y); Ry(2,2) = cos(vector.y);
res = uf::matrix::multiply(res, Ry);
}
if (vector.z != 0) {
T Rz = uf::matrix::identity<T>();
T Rz = uf::matrix::identity<typename T::type_t>();
Rz(0,0) = cos(vector.z); Rz(0,1) = -sin(vector.z);
Rz(1,0) = sin(vector.z); Rz(1,1) = cos(vector.z);
res = uf::matrix::multiply(res, Rz);

View File

@ -106,6 +106,7 @@ namespace impl {
bool hasRot = false;
for ( auto i = 0; i < 9; ++i) if ( std::abs(subObj.trans.rot[i]) > 0.0001f ) { hasRot = true; break; }
// to-do: verify if this is proper
if ( hasRot ) {
rot(0,0) = subObj.trans.rot[0]; rot(1,0) = subObj.trans.rot[1]; rot(2,0) = subObj.trans.rot[2];
rot(0,1) = subObj.trans.rot[3]; rot(1,1) = subObj.trans.rot[4]; rot(2,1) = subObj.trans.rot[5];

View File

@ -1,6 +1,8 @@
#include <uf/ext/ttlg/common.h>
uf::stl::string impl::sanitizeString( const char* raw, size_t maxLength ) {
return uf::stl::string( raw );
/*
if (!raw || maxLength == 0) return "";
size_t len = 0;
@ -19,4 +21,5 @@ uf::stl::string impl::sanitizeString( const char* raw, size_t maxLength ) {
}
}
return clean;
*/
}

View File

@ -129,31 +129,32 @@ namespace impl {
uint16_t flavor;
};
struct sPositionProp {
// ordered in the way they're read per OpenDarkEngine
struct PropertyPosition {
pod::Vector3f position;
uint32_t cell;
uint16_t headingRaw;
uint16_t pitchRaw;
uint16_t rollRaw;
int32_t cell;
int16_t heading;
int16_t pitch;
int16_t bank;
};
struct sLightProp {
pod::Vector3f color;
float intensity;
float range;
uint8_t type;
uint8_t active;
uint8_t castShadows;
struct PropertyLight {
float brightness;
float hue;
float saturation;
float z_offset;
float radius;
};
struct sAmbientSoundProp {
struct PropertyAmbient {
char schemaName[16];
uint32_t flags;
float volume;
float radius;
};
struct sAnimLightProp {
// ?
struct PropertyAnimLight {
uint32_t mode;
float millis;
pod::Vector3f color;
@ -176,8 +177,8 @@ namespace impl {
uf::stl::unordered_map<int32_t, int32_t> parentMap;
uf::stl::unordered_map<int32_t, uf::stl::string> modelNames;
uf::stl::unordered_map<int32_t, uf::stl::string> customNames;
uf::stl::unordered_map<int32_t, sLightProp> lightProps;
uf::stl::unordered_map<int32_t, sAmbientSoundProp> ambientSounds;
uf::stl::unordered_map<int32_t, PropertyLight> lightProps;
uf::stl::unordered_map<int32_t, PropertyAmbient> ambientSounds;
uf::stl::unordered_map<int32_t, uf::stl::vector<uf::stl::string>> scripts;
uf::stl::vector<LinkData> links;
@ -219,7 +220,7 @@ namespace impl {
uint16_t flavorId = 1;
while ( offset + 32 <= endOffset ) {
uf::stl::string relName = impl::sanitizeString((const char*)(buffer.data() + offset), 32);
uf::stl::string relName = uf::stl::string((const char*)(buffer.data() + offset));
if ( !relName.empty() ) ctx.linkFlavorNames[flavorId] = relName;
flavorId++;
offset += 32;
@ -273,43 +274,42 @@ namespace impl {
PropertyEntry entry;
if ( !impl::readStruct( buffer, offset, entry ) ) break;
uint32_t dataStart = offset;
uint32_t nextEntryOffset = offset + entry.size;
if ( propName == "Light" && entry.size >= sizeof(sLightProp) ) {
sLightProp light;
if ( impl::readStruct( buffer, dataStart, light ) ) ctx.lightProps[entry.objectId] = light;
/*
if ( std::isnan(light.color.x) || std::isinf(light.color.x) ) light.color = {1.f, 1.f, 1.f};
if ( std::isnan(light.intensity) || std::isinf(light.intensity) ) light.intensity = 100.f;
*/
}
else if ( propName == "Ambient" && entry.size >= sizeof(sAmbientSoundProp) ) {
sAmbientSoundProp sound;
if ( impl::readStruct( buffer, dataStart, sound ) ) {
auto schemaName = sanitizeString( sound.schemaName, 16 );
if ( propName == "Light" && entry.size >= sizeof(PropertyLight) ) {
PropertyLight light;
if ( impl::readStruct( buffer, offset, light ) ) {
ctx.lightProps[entry.objectId] = light;
}
} else if ( propName == "Ambient" && entry.size >= sizeof(PropertyAmbient) ) {
PropertyAmbient sound;
if ( impl::readStruct( buffer, offset, sound ) ) {
auto schemaName = uf::stl::string( sound.schemaName );
memset(sound.schemaName, 0, 16);
memcpy(sound.schemaName, schemaName.c_str(), std::min<size_t>(15, schemaName.size()));
ctx.ambientSounds[entry.objectId] = sound;
}
}
else if ( propName == "Scripts" ) {
const char* scriptCursor = (const char*)(buffer.data() + dataStart);
} else if ( propName == "Scripts" ) {
const char* scriptCursor = (const char*)(buffer.data() + offset);
size_t remainingBytes = entry.size;
for ( auto s = 0; s < 4; ++s ) {
if ( remainingBytes <= 0 || *scriptCursor == '\0' ) break;
auto script = sanitizeString( scriptCursor, remainingBytes );
auto script = uf::stl::string( scriptCursor );
if ( script.empty() ) break;
ctx.scripts[entry.objectId].emplace_back(script);
size_t step = strnlen(scriptCursor, remainingBytes) + 1;
if ( step > remainingBytes ) break;
scriptCursor += step;
remainingBytes -= step;
}
}
offset = dataStart + entry.size;
offset = nextEntryOffset;
}
}
@ -500,6 +500,8 @@ namespace impl {
uf::stl::vector<uf::stl::vector<uint8_t>> polyIndices;
uf::stl::vector<impl::WRPlane> planes;
uf::stl::vector<impl::WRLightInfo> lmInfos;
uf::stl::vector<int16_t> animLightList;
uf::stl::vector<uint16_t> lightIndices;
};
uf::stl::vector<ParsedCell> cells( header.numCells );
@ -533,9 +535,9 @@ namespace impl {
impl::readArray( buffer, offset, cell.header.numPlanes, cell.planes );
// read animated lights
offset += cell.header.numAnimLights * sizeof(int16_t);
impl::readArray(buffer, offset, cell.header.numAnimLights, cell.animLightList);
// to-do: read lightmaps information
// read lightmaps information
impl::readArray(buffer, offset, cell.header.numTextured, cell.lmInfos);
// read lightmap
@ -553,38 +555,57 @@ namespace impl {
uint32_t lmSizeBytes = w * h * lightPixelSize;
if ( lmSizeBytes > 0 && ( offset + lmSizeBytes * lmCount ) <= buffer.size()) {
pod::Image image;
image.size = { w, h };
image.channels = 4;
image.bpp = 32;
image.pixels.resize( w * h * 4 );
// read lightmap
uf::stl::vector<uint16_t> samples;
impl::readArray( buffer, offset, w * h * lmCount, samples );
// read pixels
for ( auto y = 0; y < h; ++y ) {
for ( auto x = 0; x < w; ++x ) {
uint16_t sample = samples[y * w + x];
auto color = pod::Vector3f{
(float)((sample >> 10) & 0x1F),
(float)((sample >> 5) & 0x1F),
(float)((sample ) & 0x1F),
} / 31.0f;
impl::encodeRGBE( color, &image.pixels[(y * w + x) * 4] );
for ( int layer = 0; layer < lmCount; ++layer ) {
pod::Image image;
image.size = { w, h };
image.channels = 4;
image.bpp = 32;
image.pixels.resize( w * h * 4 );
for ( auto y = 0; y < h; ++y ) {
for ( auto x = 0; x < w; ++x ) {
uint16_t sample = samples[(layer * w * h) + (y * w + x)];
auto color = pod::Vector3f{
(float)((sample >> 10) & 0x1F),
(float)((sample >> 5) & 0x1F),
(float)((sample ) & 0x1F),
} / 31.0f;
impl::encodeRGBE( color, &image.pixels[(y * w + x) * 4] );
}
}
if ( layer == 0 ) {
uf::atlas::add( ctx.lightmapAtlas, image, impl::darkFaceHash(c, i) );
} else {
int currentLayer = 1;
int lightID = -1;
for ( int bit = 0; bit < 32; ++bit ) {
if ( cell.lmInfos[i].animflags & (1 << bit) ) {
if ( currentLayer == layer ) {
if ( bit < cell.animLightList.size() ) {
lightID = cell.animLightList[bit];
}
break;
}
currentLayer++;
}
}
pod::Atlas::hash_t animHash = ::fmt::format("c_{}_p_{}_anim_{}", c, i, lightID);
uf::atlas::add( ctx.lightmapAtlas, image, animHash );
}
}
uf::atlas::add( ctx.lightmapAtlas, image, impl::darkFaceHash(c, i) );
} else {
offset += ( lmSizeBytes * lmCount );
}
}
// to-do: read light information
uint32_t lightCount;
if ( impl::readStruct( buffer, offset, lightCount ) ) {
offset += ( lightCount * sizeof(uint16_t) ); // skip
impl::readArray( buffer, offset, lightCount, cell.lightIndices );
}
}
@ -776,39 +797,41 @@ namespace impl {
}
// read position (and orientation) property
sPositionProp prop;
PropertyPosition prop;
if ( !impl::readStruct( buffer, offset, prop ) ) break;
auto nodeID = graph.nodes.size();
graph.root.children.emplace_back(nodeID);
auto& node = graph.nodes.emplace_back();
auto& metadata = node.metadata["dark"];
node.transform.position = impl::convertPos_NewDark( prop.position );
auto facing = pod::Vector3f{ prop.pitchRaw, prop.headingRaw, -prop.rollRaw } * M_PI / 32768.0f;
// intentionally out of order so the struct can stay in the same order
auto facing = pod::Vector3f{ prop.heading, prop.bank, prop.pitch } * M_PI / 32768.0f;
node.transform.position = impl::convertPos_NewDark(prop.position);
node.transform.orientation = uf::quaternion::euler( impl::convertPos_NewDark( facing, 1.0f ) );
auto qPitch = uf::quaternion::axisAngle(pod::Vector3f{1.0f, 0.0f, 0.0f}, -facing.x); // unknown if this needs to be negative?
auto qHeading = uf::quaternion::axisAngle(pod::Vector3f{0.0f, 1.0f, 0.0f}, -facing.y);
auto qBank = uf::quaternion::axisAngle(pod::Vector3f{0.0f, 0.0f, 1.0f}, -facing.z);
node.transform.orientation = uf::quaternion::multiply(qHeading, uf::quaternion::multiply(qBank, qPitch));
// deduce name
if ( ctx.customNames.count(entry.objectId) ) {
node.name = impl::sanitizeString(ctx.customNames.at(entry.objectId).c_str(), 128);
node.name = uf::stl::string(ctx.customNames.at(entry.objectId).c_str());
} else {
uf::stl::string symName;
if ( ctx.findInheritedProperty(entry.objectId, ctx.archetypes, symName) ) {
node.name = impl::sanitizeString(symName.c_str(), 128);
}
if ( node.name.empty()) {
node.name = ::fmt::format("object_{}", entry.objectId);
} else {
node.name = ::fmt::format("{}_{}", node.name, entry.objectId);
node.name = uf::stl::string(symName.c_str());
}
if ( node.name.empty()) node.name = ::fmt::format("object #{}", entry.objectId);
}
// deduce model
node.mesh = -1;
uf::stl::string modelName;
if ( ctx.findInheritedProperty( entry.objectId, ctx.modelNames, modelName ) && !modelName.empty() ) {
uf::stl::string model = sanitizeString(modelName.c_str(), 64);
uf::stl::string model = uf::stl::string(modelName.c_str());
std::transform( model.begin(), model.end(), model.begin(), ::tolower );
if ( !model.ends_with(".bin") ) model += ".bin";
@ -828,19 +851,17 @@ namespace impl {
}
// deduce light
sLightProp light;
if ( ctx.findInheritedProperty( entry.objectId, ctx.lightProps, light ) && light.active ) {
auto lightKey = ::fmt::format("light_{}", entry.objectId);
PropertyLight light;
if ( ctx.findInheritedProperty( entry.objectId, ctx.lightProps, light ) ) {
auto lightKey = ::fmt::format("{}_{}", node.name, nodeID);
auto& graphLight = graph.lights[lightKey];
graphLight.color = light.color;
graphLight.intensity = light.intensity;
graphLight.range = light.range;
metadata["light_source"] = lightKey;
graphLight.color = impl::hsvToRgb(light.hue, light.saturation, 1.0f);
graphLight.intensity = light.brightness / M_PI;
graphLight.range = light.radius;
}
// deduce sound
sAmbientSoundProp ambient;
PropertyAmbient ambient;
if (ctx.findInheritedProperty(entry.objectId, ctx.ambientSounds, ambient)) {
metadata["sound"]["schema"] = uf::stl::string(ambient.schemaName);
metadata["sound"]["volume"] = ambient.volume;
@ -892,9 +913,10 @@ namespace impl {
if ( !impl::readStruct( buffer, offset, chunkCount ) ) return;
for ( uint32_t i = 0; i < chunkCount; ++i ) {
impl::DarkDBInvItem item;
if ( !impl::readStruct(buffer, offset, item) ) break;
uf::stl::string name = impl::sanitizeString( item.name );
if ( !impl::readStruct( buffer, offset, item ) ) break;
uf::stl::string name = item.name;
inventory[name] = item;
// UF_MSG_DEBUG("{}", name);
}
// parse strings