diff --git a/bin/data/config.json b/bin/data/config.json index f7ace55c..8edb8bc5 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -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, diff --git a/engine/inc/uf/ext/ttlg/common.h b/engine/inc/uf/ext/ttlg/common.h index d38f3164..cf6caee8 100644 --- a/engine/inc/uf/ext/ttlg/common.h +++ b/engine/inc/uf/ext/ttlg/common.h @@ -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; } diff --git a/engine/inc/uf/utils/math/matrix/pod.inl b/engine/inc/uf/utils/math/matrix/pod.inl index 9ab5b70f..b5aa4686 100644 --- a/engine/inc/uf/utils/math/matrix/pod.inl +++ b/engine/inc/uf/utils/math/matrix/pod.inl @@ -336,19 +336,19 @@ template T uf::matrix::rotate( const T& matrix, const pod::Vector3t< T res = matrix; if (vector.x != 0) { - T Rx = uf::matrix::identity(); + T Rx = uf::matrix::identity(); 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 Ry = uf::matrix::identity(); 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 Rz = uf::matrix::identity(); 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); diff --git a/engine/src/ext/ttlg/bin.cpp b/engine/src/ext/ttlg/bin.cpp index ab583e5a..c617991a 100644 --- a/engine/src/ext/ttlg/bin.cpp +++ b/engine/src/ext/ttlg/bin.cpp @@ -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]; diff --git a/engine/src/ext/ttlg/common.cpp b/engine/src/ext/ttlg/common.cpp index 56662614..8e9e443d 100644 --- a/engine/src/ext/ttlg/common.cpp +++ b/engine/src/ext/ttlg/common.cpp @@ -1,6 +1,8 @@ #include 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; +*/ } \ No newline at end of file diff --git a/engine/src/ext/ttlg/mis.cpp b/engine/src/ext/ttlg/mis.cpp index 6edee889..6df2989d 100644 --- a/engine/src/ext/ttlg/mis.cpp +++ b/engine/src/ext/ttlg/mis.cpp @@ -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 parentMap; uf::stl::unordered_map modelNames; uf::stl::unordered_map customNames; - uf::stl::unordered_map lightProps; - uf::stl::unordered_map ambientSounds; + uf::stl::unordered_map lightProps; + uf::stl::unordered_map ambientSounds; uf::stl::unordered_map> scripts; uf::stl::vector 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(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> polyIndices; uf::stl::vector planes; uf::stl::vector lmInfos; + uf::stl::vector animLightList; + uf::stl::vector lightIndices; }; uf::stl::vector 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 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