mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-03 20:03:50 +00:00
Merge commit '32bb0becc8' into chore/game-screen-extract
This commit is contained in:
commit
43aecab1ef
145 changed files with 3237 additions and 2849 deletions
|
|
@ -3,6 +3,7 @@
|
|||
#include "core/window.hpp"
|
||||
#include "core/input.hpp"
|
||||
#include "game/character.hpp"
|
||||
#include "game/game_services.hpp"
|
||||
#include "pipeline/blp_loader.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
|
@ -126,6 +127,7 @@ private:
|
|||
|
||||
static Application* instance;
|
||||
|
||||
game::GameServices gameServices_;
|
||||
std::unique_ptr<Window> window;
|
||||
std::unique_ptr<rendering::Renderer> renderer;
|
||||
std::unique_ptr<ui::UIManager> uiManager;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,9 @@ public:
|
|||
float impliedVX = (destX - fromX) / durationSec;
|
||||
float impliedVY = (destY - fromY) / durationSec;
|
||||
float impliedVZ = (destZ - fromZ) / durationSec;
|
||||
// Exponentially smooth velocity so jittery packet timing doesn't snap speed.
|
||||
// Exponential moving average on velocity — 65% new sample, 35% previous.
|
||||
// Smooths out jitter from irregular server update intervals (~200-600ms)
|
||||
// without introducing visible lag on direction changes.
|
||||
const float alpha = 0.65f;
|
||||
velX_ = alpha * impliedVX + (1.0f - alpha) * velX_;
|
||||
velY_ = alpha * impliedVY + (1.0f - alpha) * velY_;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "game/quest_handler.hpp"
|
||||
#include "game/movement_handler.hpp"
|
||||
#include "game/entity_controller.hpp"
|
||||
#include "game/game_services.hpp"
|
||||
#include "network/packet.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
|
|
@ -130,9 +131,11 @@ public:
|
|||
using TalentEntry = game::TalentEntry;
|
||||
using TalentTabEntry = game::TalentTabEntry;
|
||||
|
||||
GameHandler();
|
||||
explicit GameHandler(GameServices& services);
|
||||
~GameHandler();
|
||||
|
||||
const GameServices& services() const { return services_; }
|
||||
|
||||
/** Access the active opcode table (wire ↔ logical mapping). */
|
||||
const OpcodeTable& getOpcodeTable() const { return opcodeTable_; }
|
||||
OpcodeTable& getOpcodeTable() { return opcodeTable_; }
|
||||
|
|
@ -2298,6 +2301,9 @@ private:
|
|||
float localOrientation);
|
||||
void clearTransportAttachment(uint64_t childGuid);
|
||||
|
||||
// Explicit service dependencies (owned by Application)
|
||||
GameServices& services_;
|
||||
|
||||
// Domain handlers — each manages a specific concern extracted from GameHandler
|
||||
std::unique_ptr<ChatHandler> chatHandler_;
|
||||
std::unique_ptr<MovementHandler> movementHandler_;
|
||||
|
|
|
|||
23
include/game/game_services.hpp
Normal file
23
include/game/game_services.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering { class Renderer; }
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace game { class ExpansionRegistry; }
|
||||
|
||||
namespace game {
|
||||
|
||||
// Explicit service dependencies for game handlers.
|
||||
// Owned by Application, passed by reference to GameHandler at construction.
|
||||
// Replaces hidden Application::getInstance() singleton access.
|
||||
struct GameServices {
|
||||
rendering::Renderer* renderer = nullptr;
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
ExpansionRegistry* expansionRegistry = nullptr;
|
||||
uint32_t gryphonDisplayId = 0;
|
||||
uint32_t wyvernDisplayId = 0;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
@ -70,7 +70,12 @@ class Inventory {
|
|||
public:
|
||||
static constexpr int BACKPACK_SLOTS = 16;
|
||||
static constexpr int KEYRING_SLOTS = 32;
|
||||
// WoW slot layout: 0-22 are equipment (head, neck, ... tabard, mainhand, offhand, ranged, ammo).
|
||||
// Backpack inventory starts at slot 23 in bag 0xFF, so packet slot = NUM_EQUIP_SLOTS + backpackIndex.
|
||||
static constexpr int NUM_EQUIP_SLOTS = 23;
|
||||
// Bag containers occupy equipment slots 19-22 (bag1, bag2, bag3, bag4).
|
||||
// Packet bag byte = FIRST_BAG_EQUIP_SLOT + bagIndex.
|
||||
static constexpr int FIRST_BAG_EQUIP_SLOT = 19;
|
||||
static constexpr int NUM_BAG_SLOTS = 4;
|
||||
static constexpr int MAX_BAG_SIZE = 36;
|
||||
static constexpr int BANK_SLOTS = 28;
|
||||
|
|
|
|||
|
|
@ -122,8 +122,11 @@ public:
|
|||
};
|
||||
|
||||
const std::unordered_map<uint32_t, TaxiNode>& getTaxiNodes() const { return taxiNodes_; }
|
||||
// WotLK 3.3.5a TaxiNodes.dbc has 384 entries; the known-taxi bitmask
|
||||
// is 12 × uint32 = 384 bits. Node IDs outside this range are invalid.
|
||||
static constexpr uint32_t kMaxTaxiNodeId = 384;
|
||||
bool isKnownTaxiNode(uint32_t nodeId) const {
|
||||
if (nodeId == 0 || nodeId > 384) return false;
|
||||
if (nodeId == 0 || nodeId > kMaxTaxiNodeId) return false;
|
||||
uint32_t idx = nodeId - 1;
|
||||
return (knownTaxiMask_[idx / 32] & (1u << (idx % 32))) != 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,6 +249,9 @@ private:
|
|||
void handleChannelUpdate(network::Packet& packet);
|
||||
|
||||
// --- Internal helpers ---
|
||||
// Find the on-use spell for an item (trigger=0 Use or trigger=5 NoDelay).
|
||||
// CMSG_USE_ITEM requires a valid spellId or the server silently ignores it.
|
||||
uint32_t findOnUseSpellId(uint32_t itemId) const;
|
||||
void loadSpellNameCache() const;
|
||||
void loadSkillLineAbilityDbc();
|
||||
void categorizeTrainerSpells();
|
||||
|
|
|
|||
|
|
@ -138,6 +138,10 @@ public:
|
|||
*/
|
||||
std::vector<uint8_t> readData(uint32_t address, size_t size);
|
||||
|
||||
// Look up an already-registered API stub address by DLL and function name.
|
||||
// Returns 0 if not found. Used by WardenModule::bindAPIs() for IAT patching.
|
||||
uint32_t getAPIAddress(const std::string& dllName, const std::string& funcName) const;
|
||||
|
||||
private:
|
||||
uc_engine* uc_; // Unicorn engine instance
|
||||
uint32_t moduleBase_; // Module base address
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace game {
|
|||
|
||||
// Forward declarations
|
||||
class WardenEmulator;
|
||||
class WardenCrypto;
|
||||
|
||||
/**
|
||||
* Represents Warden callback functions exported by loaded module
|
||||
|
|
@ -36,18 +37,19 @@ struct WardenFuncList {
|
|||
* Warden module loader and executor
|
||||
*
|
||||
* IMPLEMENTATION STATUS:
|
||||
* ✅ Module metadata parsing
|
||||
* ✅ Basic validation framework
|
||||
* ⏳ RC4 decryption (uses existing WardenCrypto)
|
||||
* ❌ RSA signature verification (TODO - requires OpenSSL RSA)
|
||||
* ❌ zlib decompression (TODO - requires zlib library)
|
||||
* ❌ Custom executable format parsing (TODO - major reverse engineering)
|
||||
* ❌ Address relocation (TODO - x86 address fixups)
|
||||
* ❌ API binding (TODO - kernel32/user32 function resolution)
|
||||
* ❌ Native code execution (TODO - execute loaded x86 code)
|
||||
* ✅ Module metadata parsing and validation
|
||||
* ✅ RC4 decryption (WardenCrypto)
|
||||
* ✅ RSA-2048 signature verification (OpenSSL EVP — real Blizzard modulus)
|
||||
* ✅ zlib decompression
|
||||
* ✅ Custom executable format parsing (3 pair-format variants)
|
||||
* ✅ Address relocation (delta-encoded fixups)
|
||||
* ✅ x86 emulation via Unicorn Engine (cross-platform)
|
||||
* ✅ Client callbacks (sendPacket, validateModule, generateRC4)
|
||||
* ✅ API binding / IAT patching (parses import table, auto-stubs unknown APIs)
|
||||
* ✅ RSA modulus verified (Blizzard key, same across 1.12.1/2.4.3/3.3.5a)
|
||||
*
|
||||
* For strict servers like Warmane, ALL TODOs must be implemented.
|
||||
* For permissive servers, fake responses in GameHandler work.
|
||||
* Non-fatal verification: RSA mismatch logs warning but continues loading,
|
||||
* so private-server modules signed with custom keys still work.
|
||||
*/
|
||||
class WardenModule {
|
||||
public:
|
||||
|
|
@ -126,6 +128,12 @@ public:
|
|||
size_t getModuleSize() const { return moduleSize_; }
|
||||
const std::vector<uint8_t>& getDecompressedData() const { return decompressedData_; }
|
||||
|
||||
// Inject dependencies for module callbacks (sendPacket, generateRC4).
|
||||
// Must be called before initializeModule() so callbacks can reach the
|
||||
// network layer and crypto state.
|
||||
using SendPacketFunc = std::function<void(const uint8_t*, size_t)>;
|
||||
void setCallbackDependencies(WardenCrypto* crypto, SendPacketFunc sendFunc);
|
||||
|
||||
private:
|
||||
bool loaded_; // Module successfully loaded
|
||||
std::vector<uint8_t> md5Hash_; // Module identifier
|
||||
|
|
@ -142,6 +150,11 @@ private:
|
|||
std::unique_ptr<WardenEmulator> emulator_; // Cross-platform x86 emulator
|
||||
uint32_t emulatedPacketHandlerAddr_ = 0; // Raw emulated VA for 4-arg PacketHandler call
|
||||
|
||||
// Dependencies injected via setCallbackDependencies() for module callbacks.
|
||||
// These are NOT owned — the handler owns the crypto and socket lifetime.
|
||||
WardenCrypto* callbackCrypto_ = nullptr;
|
||||
SendPacketFunc callbackSendPacket_;
|
||||
|
||||
// Validation and loading steps
|
||||
bool verifyMD5(const std::vector<uint8_t>& data,
|
||||
const std::vector<uint8_t>& expectedHash);
|
||||
|
|
|
|||
|
|
@ -1,129 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
// Forward declare StormLib handle
|
||||
typedef void* HANDLE;
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* MPQManager - Manages MPQ archive loading and file reading
|
||||
*
|
||||
* WoW 3.3.5a stores all game assets in MPQ archives.
|
||||
* This manager loads multiple archives and provides unified file access.
|
||||
*/
|
||||
class MPQManager {
|
||||
public:
|
||||
MPQManager();
|
||||
~MPQManager();
|
||||
|
||||
/**
|
||||
* Initialize the MPQ system
|
||||
* @param dataPath Path to WoW Data directory
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool initialize(const std::string& dataPath);
|
||||
|
||||
/**
|
||||
* Shutdown and close all archives
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Load a single MPQ archive
|
||||
* @param path Full path to MPQ file
|
||||
* @param priority Priority for file resolution (higher = checked first)
|
||||
* @return true if archive loaded successfully
|
||||
*/
|
||||
bool loadArchive(const std::string& path, int priority = 0);
|
||||
|
||||
/**
|
||||
* Check if a file exists in any loaded archive
|
||||
* @param filename Virtual file path (e.g., "World\\Maps\\Azeroth\\Azeroth.wdt")
|
||||
* @return true if file exists
|
||||
*/
|
||||
bool fileExists(const std::string& filename) const;
|
||||
|
||||
/**
|
||||
* Read a file from MPQ archives
|
||||
* @param filename Virtual file path
|
||||
* @return File contents as byte vector (empty if not found)
|
||||
*/
|
||||
std::vector<uint8_t> readFile(const std::string& filename) const;
|
||||
|
||||
/**
|
||||
* Get file size without reading it
|
||||
* @param filename Virtual file path
|
||||
* @return File size in bytes (0 if not found)
|
||||
*/
|
||||
uint32_t getFileSize(const std::string& filename) const;
|
||||
|
||||
/**
|
||||
* Check if MPQ system is initialized
|
||||
*/
|
||||
bool isInitialized() const { return initialized; }
|
||||
|
||||
/**
|
||||
* Get list of loaded archives
|
||||
*/
|
||||
const std::vector<std::string>& getLoadedArchives() const { return archiveNames; }
|
||||
|
||||
private:
|
||||
struct ArchiveEntry {
|
||||
HANDLE handle;
|
||||
std::string path;
|
||||
int priority;
|
||||
};
|
||||
|
||||
bool initialized = false;
|
||||
std::string dataPath;
|
||||
std::vector<ArchiveEntry> archives;
|
||||
std::vector<std::string> archiveNames;
|
||||
|
||||
/**
|
||||
* Find archive containing a file
|
||||
* @param filename File to search for
|
||||
* @return Archive handle or nullptr if not found
|
||||
*/
|
||||
HANDLE findFileArchive(const std::string& filename) const;
|
||||
|
||||
/**
|
||||
* Load patch archives (e.g., patch.MPQ, patch-2.MPQ, etc.)
|
||||
*/
|
||||
bool loadPatchArchives();
|
||||
|
||||
/**
|
||||
* Load locale-specific archives
|
||||
* @param locale Locale string (e.g., "enUS")
|
||||
*/
|
||||
bool loadLocaleArchives(const std::string& locale);
|
||||
|
||||
void logMissingFileOnce(const std::string& filename) const;
|
||||
|
||||
// Cache for mapping "virtual filename" -> archive handle (or INVALID_HANDLE_VALUE for not found).
|
||||
// This avoids scanning every archive for repeated lookups, which can otherwise appear as a hang
|
||||
// on screens that trigger many asset probes (character select, character preview, etc.).
|
||||
//
|
||||
// Important: caching misses can blow up memory if the game probes many unique non-existent filenames.
|
||||
// Miss caching is disabled by default and must be explicitly enabled.
|
||||
mutable std::shared_mutex fileArchiveCacheMutex_;
|
||||
mutable std::unordered_map<std::string, HANDLE> fileArchiveCache_;
|
||||
size_t fileArchiveCacheMaxEntries_ = 500000;
|
||||
bool fileArchiveCacheMisses_ = false;
|
||||
|
||||
mutable std::mutex missingFileMutex_;
|
||||
mutable std::unordered_set<std::string> missingFileWarnings_;
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
|
|
@ -81,6 +81,8 @@ private:
|
|||
// ImGui texture handle for displaying the preview (VkDescriptorSet in Vulkan backend)
|
||||
VkDescriptorSet imguiTextureId_ = VK_NULL_HANDLE;
|
||||
|
||||
// 4:5 portrait aspect ratio — taller than wide to show full character body
|
||||
// from head to feet in the character creation/selection screen
|
||||
static constexpr int fboWidth_ = 400;
|
||||
static constexpr int fboHeight_ = 500;
|
||||
|
||||
|
|
|
|||
|
|
@ -251,6 +251,10 @@ public:
|
|||
|
||||
|
||||
private:
|
||||
// Create 1×1 fallback textures used when real textures are missing or still loading.
|
||||
// Called during both init and clear to ensure valid descriptor bindings at all times.
|
||||
void createFallbackTextures(VkDevice device);
|
||||
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
VkRenderPass renderPassOverride_ = VK_NULL_HANDLE;
|
||||
VkSampleCountFlagBits msaaSamplesOverride_ = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
struct Vertex {
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 texCoord;
|
||||
};
|
||||
|
||||
class Mesh {
|
||||
public:
|
||||
Mesh() = default;
|
||||
~Mesh();
|
||||
|
||||
void create(const std::vector<Vertex>& vertices, const std::vector<uint32_t>& indices);
|
||||
void destroy();
|
||||
void draw() const;
|
||||
|
||||
private:
|
||||
GLuint VAO = 0;
|
||||
GLuint VBO = 0;
|
||||
GLuint EBO = 0;
|
||||
size_t indexCount = 0;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
@ -30,7 +30,6 @@ namespace rendering {
|
|||
|
||||
class Camera;
|
||||
class CameraController;
|
||||
class Scene;
|
||||
class TerrainRenderer;
|
||||
class TerrainManager;
|
||||
class PerformanceHUD;
|
||||
|
|
@ -54,7 +53,6 @@ class Minimap;
|
|||
class WorldMap;
|
||||
class QuestMarkerRenderer;
|
||||
class CharacterPreview;
|
||||
class Shader;
|
||||
class AmdFsr3Runtime;
|
||||
|
||||
class Renderer {
|
||||
|
|
@ -119,7 +117,6 @@ public:
|
|||
|
||||
Camera* getCamera() { return camera.get(); }
|
||||
CameraController* getCameraController() { return cameraController.get(); }
|
||||
Scene* getScene() { return scene.get(); }
|
||||
TerrainRenderer* getTerrainRenderer() const { return terrainRenderer.get(); }
|
||||
TerrainManager* getTerrainManager() const { return terrainManager.get(); }
|
||||
PerformanceHUD* getPerformanceHUD() { return performanceHUD.get(); }
|
||||
|
|
@ -219,7 +216,6 @@ private:
|
|||
core::Window* window = nullptr;
|
||||
std::unique_ptr<Camera> camera;
|
||||
std::unique_ptr<CameraController> cameraController;
|
||||
std::unique_ptr<Scene> scene;
|
||||
std::unique_ptr<TerrainRenderer> terrainRenderer;
|
||||
std::unique_ptr<TerrainManager> terrainManager;
|
||||
std::unique_ptr<PerformanceHUD> performanceHUD;
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Mesh;
|
||||
|
||||
class Scene {
|
||||
public:
|
||||
Scene() = default;
|
||||
~Scene() = default;
|
||||
|
||||
void addMesh(std::shared_ptr<Mesh> mesh);
|
||||
void removeMesh(const std::shared_ptr<Mesh>& mesh);
|
||||
void clear();
|
||||
|
||||
const std::vector<std::shared_ptr<Mesh>>& getMeshes() const { return meshes; }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Mesh>> meshes;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
Shader() = default;
|
||||
~Shader();
|
||||
|
||||
[[nodiscard]] bool loadFromFile(const std::string& vertexPath, const std::string& fragmentPath);
|
||||
[[nodiscard]] bool loadFromSource(const std::string& vertexSource, const std::string& fragmentSource);
|
||||
|
||||
void use() const;
|
||||
void unuse() const;
|
||||
|
||||
void setUniform(const std::string& name, int value);
|
||||
void setUniform(const std::string& name, float value);
|
||||
void setUniform(const std::string& name, const glm::vec2& value);
|
||||
void setUniform(const std::string& name, const glm::vec3& value);
|
||||
void setUniform(const std::string& name, const glm::vec4& value);
|
||||
void setUniform(const std::string& name, const glm::mat3& value);
|
||||
void setUniform(const std::string& name, const glm::mat4& value);
|
||||
void setUniformMatrixArray(const std::string& name, const glm::mat4* matrices, int count);
|
||||
|
||||
GLuint getProgram() const { return program; }
|
||||
|
||||
// Adopt an externally-created program (no ownership of individual shaders)
|
||||
void setProgram(GLuint prog) { program = prog; }
|
||||
// Release ownership without deleting (caller retains the GL program)
|
||||
void releaseProgram() { program = 0; vertexShader = 0; fragmentShader = 0; }
|
||||
|
||||
private:
|
||||
bool compile(const std::string& vertexSource, const std::string& fragmentSource);
|
||||
GLint getUniformLocation(const std::string& name) const;
|
||||
|
||||
GLuint program = 0;
|
||||
GLuint vertexShader = 0;
|
||||
GLuint fragmentShader = 0;
|
||||
|
||||
// Cache uniform locations to avoid expensive glGetUniformLocation calls
|
||||
mutable std::unordered_map<std::string, GLint> uniformLocationCache;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
typedef unsigned int GLuint;
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class VideoPlayer {
|
||||
public:
|
||||
VideoPlayer();
|
||||
~VideoPlayer();
|
||||
|
||||
bool open(const std::string& path);
|
||||
void update(float deltaTime);
|
||||
void close();
|
||||
|
||||
bool isReady() const { return textureReady; }
|
||||
GLuint getTextureId() const { return textureId; }
|
||||
int getWidth() const { return width; }
|
||||
int getHeight() const { return height; }
|
||||
|
||||
private:
|
||||
bool decodeNextFrame();
|
||||
void uploadFrame();
|
||||
|
||||
void* formatCtx = nullptr;
|
||||
void* codecCtx = nullptr;
|
||||
void* frame = nullptr;
|
||||
void* rgbFrame = nullptr;
|
||||
void* packet = nullptr;
|
||||
void* swsCtx = nullptr;
|
||||
|
||||
int videoStreamIndex = -1;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
double frameTime = 1.0 / 30.0;
|
||||
double accumulator = 0.0;
|
||||
bool eof = false;
|
||||
|
||||
GLuint textureId = 0;
|
||||
bool textureReady = false;
|
||||
std::string sourcePath;
|
||||
std::vector<uint8_t> rgbBuffer;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
@ -68,6 +68,8 @@ public:
|
|||
|
||||
private:
|
||||
void generateMipmaps(VkContext& ctx, VkFormat format, uint32_t width, uint32_t height);
|
||||
// Shared sampler finalization: prefer the global cache, fall back to direct creation
|
||||
bool finalizeSampler(VkDevice device, const VkSamplerCreateInfo& samplerInfo);
|
||||
|
||||
AllocatedImage image_{};
|
||||
VkSampler sampler_ = VK_NULL_HANDLE;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue