* Add PatchManager - Allows you to patch terrain.png and items.png on the fly.

This can be used for modding, since you don't need to share copyrighted Mojang assets alongside your texture patches.
This commit is contained in:
iProgramInCpp
2023-08-09 22:13:04 +03:00
parent a7f56c5581
commit df47842c5e
14 changed files with 311 additions and 8 deletions

3
.gitignore vendored
View File

@@ -401,6 +401,9 @@ FodyWeavers.xsd
/game/assets/particles.png
/game/assets/terrain.png
# Avoid including Minecraft Classic assets from Mojang, for now.
/game/assets/patches/*.png
# Ignore keep directory - where you can keep files for later use
/keep

View File

@@ -0,0 +1,28 @@
#Texture patch data for Minecraft PE. The # at the start denotes a comment, removing it makes it a command.
# Commands you can use:
# - terrain|x|y|filename -- Patches terrain.png
# - items|x|y|filename -- Patches gui/items.png
# - vegetation_tint|bool -- Enables default vegetation tint. (grass and leaves)
# - metal_block_sides|int -- Makes metal blocks have separate side textures, and allows specification of their Y offset. -1 to disable.
# * The filename parameter will be a PNG file that can be found relative to this directory. For example, '../quiver.png' will load it from assets/, 'chain.png' will load it from assets/patches/.
# * The X and Y destination coordinates will be multiplied by 16.
# * The texture doesn't have to be 16x16, all of it will be patched on to terrain.png.
# Below is an example of what you can do:
#terrain|0|0|c_grass_top.png
#terrain|4|3|c_leaves_tra.png
#terrain|5|3|c_leaves_opa.png
#terrain|7|0|c_bricks.png
#terrain|12|0|c_rose.png
#terrain|6|11|c_iron.png
#terrain|7|11|c_gold.png
#terrain|8|11|c_emerald.png
#items|7|0|c_i_coal.png
#items|7|1|c_i_iron.png
#items|7|2|c_i_gold.png
#items|7|3|c_i_emerald.png
#vegetation_tint|false
#metal_block_sides|10

View File

@@ -183,8 +183,13 @@ Texture AppPlatform_windows::loadTexture(const std::string& str, bool b)
goto _error;
}
uint32_t* img2 = new uint32_t[width * height];
memcpy(img2, img, width * height * sizeof(uint32_t));
stbi_image_free(img);
img = nullptr;
fclose(f);
return Texture(width, height, (uint32_t*)img, 1, 0);
return Texture(width, height, img2, 1, 0);
}
std::vector<std::string> AppPlatform_windows::getOptionStrings()
@@ -233,6 +238,18 @@ void AppPlatform_windows::setOptionStrings(const std::vector<std::string>& str)
os << str[i] << ':' << str[i + 1] << '\n';
}
std::string AppPlatform_windows::getPatchData()
{
std::ifstream ifs("assets/patches/patch_data.txt");
if (!ifs.is_open())
return "";
std::stringstream ss;
ss << ifs.rdbuf();
return ss.str();
}
void AppPlatform_windows::setScreenSize(int width, int height)
{
m_ScreenWidth = width;

View File

@@ -52,6 +52,9 @@ public:
// Also add these to allow saving options.
void setOptionStrings(const std::vector <std::string>& str) override;
// Also add this to allow dynamic texture patching.
std::string getPatchData() override;
void setScreenSize(int width, int height);
private:

View File

@@ -126,3 +126,8 @@ bool AppPlatform::shiftPressed()
void AppPlatform::setOptionStrings(const std::vector<std::string>& vec)
{
}
std::string AppPlatform::getPatchData()
{
return "";
}

View File

@@ -52,6 +52,8 @@ public:
virtual bool shiftPressed();
// Also add this to allow option saving.
virtual void setOptionStrings(const std::vector<std::string>& vec);
// Also add this to allow dynamic patching.
virtual std::string getPatchData();
#endif

View File

@@ -23,6 +23,9 @@
#include "client/player/input/ControllerTurnInput.hpp"
#endif
// custom:
#include "client/renderer/PatchManager.hpp"
// note: Nothing changes these, so it'll think we're always running at 854x480 even if not
int Minecraft::width = C_DEFAULT_SCREEN_WIDTH;
int Minecraft::height = C_DEFAULT_SCREEN_HEIGHT;
@@ -684,6 +687,14 @@ void Minecraft::init()
reloadOptions();
m_pFont = new Font(&m_options, "font/default.png", m_pTextures);
// Patch Manager
GetPatchManager()->LoadPatchData(platform()->getPatchData());
m_pTextures->loadAndBindTexture(C_TERRAIN_NAME);
GetPatchManager()->PatchTextures(platform(), TYPE_TERRAIN);
m_pTextures->loadAndBindTexture(C_ITEMS_NAME);
GetPatchManager()->PatchTextures(platform(), TYPE_ITEMS);
}
Minecraft::~Minecraft()

View File

@@ -0,0 +1,136 @@
#include "PatchManager.hpp"
#include "AppPlatform.hpp"
#include "client/common/Utils.hpp"
#include "compat/GL.hpp"
#define PM_SEPARATOR ('|')
PatchManager* g_pPatchManager;
PatchManager* GetPatchManager()
{
if (!g_pPatchManager)
g_pPatchManager = new PatchManager;
return g_pPatchManager;
}
PatchManager::PatchManager()
{
m_bGrassTinted = true;
m_nMetalSideYOffset = -1;
}
void PatchManager::LoadPatchData(const std::string& patchData)
{
std::stringstream patchDataStream(patchData);
std::string currLine;
while (std::getline(patchDataStream, currLine))
{
if (currLine.empty()) continue;
if (currLine[0] == '#') continue;
std::string command;
std::stringstream lineStream(currLine);
// read command type
if (!std::getline(lineStream, command, PM_SEPARATOR))
continue;
if (command == "terrain" || command == "items")
{
bool bIsItems = command == "items";
std::string xStr, yStr, fileName;
if (!std::getline(lineStream, xStr, PM_SEPARATOR)) continue;
if (!std::getline(lineStream, yStr, PM_SEPARATOR)) continue;
if (!std::getline(lineStream, fileName, PM_SEPARATOR)) continue;
// turn the xStr and yStr into ints.
int x, y;
if (!sscanf(xStr.c_str(), "%d", &x)) continue;
if (!sscanf(yStr.c_str(), "%d", &y)) continue;
m_patchData.push_back(PatchData(bIsItems ? TYPE_ITEMS : TYPE_TERRAIN, x, y, fileName));
continue;
}
// features -- TODO un-hardcode this
if (command == "vegetation_tint")
{
ReadBool(lineStream, m_bGrassTinted);
continue;
}
// features -- TODO un-hardcode this
if (command == "metal_block_sides")
{
ReadInt(lineStream, m_nMetalSideYOffset);
continue;
}
LogMsg("Unknown command %s from patch data.", command.c_str());
}
}
void PatchManager::PatchTextures(AppPlatform* pAppPlatform, ePatchType patchType)
{
// Use glTexSubImage2D to patch the terrain.png texture on the fly.
for (int i = 0; i < int(m_patchData.size()); i++)
{
PatchData& pd = m_patchData[i];
if (pd.m_type != patchType)
continue;
Texture texture = pAppPlatform->loadTexture("patches/" + pd.m_filename, true);
if (texture.m_width == 0)
{
LogMsg("Image %s has width 0, not found?! Skipping", pd.m_filename.c_str());
continue;
}
glTexSubImage2D(
GL_TEXTURE_2D,
0,
pd.m_destX,
pd.m_destY,
texture.m_width,
texture.m_height,
GL_RGBA,
GL_UNSIGNED_BYTE,
texture.m_pixels
);
SAFE_DELETE_ARRAY(texture.m_pixels);
}
}
bool PatchManager::IsGrassTinted()
{
return m_bGrassTinted;
}
int PatchManager::GetMetalSideYOffset()
{
return m_nMetalSideYOffset;
}
void PatchManager::ReadBool(std::istream& is, bool& b)
{
std::string flag;
if (!std::getline(is, flag, PM_SEPARATOR))
return;
b = (flag == "true" || flag == "1" || flag == "TRUE");
}
void PatchManager::ReadInt(std::istream& is, int& i)
{
std::string flag;
if (!std::getline(is, flag, PM_SEPARATOR))
return;
int x = -1;
if (sscanf(flag.c_str(), "%d", &x))
i = x;
}

View File

@@ -0,0 +1,80 @@
#pragma once
#include <string>
#include <vector>
#include <sstream>
class AppPlatform;
enum ePatchType
{
TYPE_NONE,
TYPE_TERRAIN,
TYPE_ITEMS,
TYPE_FEATURE,
};
enum ePatchOption
{
PO_NONE,
PO_GRASS_TINT,
};
struct PatchData
{
ePatchType m_type;
ePatchOption m_option;
int m_destX, m_destY;
std::string m_filename;
bool m_bEnable;
PatchData(ePatchType type, int x, int y, const std::string& fn)
{
_init();
m_type = type;
m_destX = x * 16;
m_destY = y * 16;
m_filename = fn;
}
PatchData(ePatchType type, ePatchOption opt, bool enable)
{
_init();
m_type = type;
m_option = opt;
m_bEnable = enable;
}
void _init()
{
m_type = TYPE_NONE;
m_option = PO_NONE;
m_destX = m_destY = 0;
m_bEnable = false;
}
};
class PatchManager
{
public:
PatchManager();
void LoadPatchData(const std::string& patchData);
void PatchTextures(AppPlatform*, ePatchType);
// Features
bool IsGrassTinted();
int GetMetalSideYOffset();
private:
void ReadBool(std::istream& is, bool& b);
void ReadInt(std::istream& is, int& b);
private:
bool m_bGrassTinted;
int m_nMetalSideYOffset;
std::vector<PatchData> m_patchData;
};
PatchManager* GetPatchManager();

View File

@@ -8,6 +8,7 @@
#include "Tile.hpp"
#include "world/level/Level.hpp"
#include "client/renderer/PatchManager.hpp"
GrassTile::GrassTile(int id, Material* c) : Tile(id, c)
{
@@ -17,10 +18,10 @@ GrassTile::GrassTile(int id, Material* c) : Tile(id, c)
int GrassTile::getColor(LevelSource*, int x, int y, int z)
{
#ifdef MOD_DONT_COLOR_GRASS
if (GetPatchManager()->IsGrassTinted())
return 0x339933;
return 0xffffff;
#endif
return 0x339933;
}
int GrassTile::getResource(int i, Random* random)

View File

@@ -8,6 +8,7 @@
#include "Tile.hpp"
#include "world/level/Level.hpp"
#include "client/renderer/PatchManager.hpp"
LeafTile::LeafTile(int id) : TransparentTile(id, TEXTURE_LEAVES_TRANSPARENT, Material::leaves, false)
{
@@ -33,10 +34,10 @@ void LeafTile::die(Level* level, int x, int y, int z)
int LeafTile::getColor(LevelSource* level, int x, int y, int z)
{
#ifdef MOD_DONT_COLOR_GRASS
if (GetPatchManager()->IsGrassTinted())
return 0x339933;
return 0xffffff;
#endif
return 0x339933;
}
int LeafTile::getTexture(int dir, int data)

View File

@@ -8,6 +8,7 @@
#include "Tile.hpp"
#include "world/level/Level.hpp"
#include "client/renderer/PatchManager.hpp"
MetalTile::MetalTile(int ID, int texture, Material* pMtl) : Tile(ID, pMtl)
{
@@ -19,5 +20,12 @@ MetalTile::MetalTile(int ID, int texture, Material* pMtl) : Tile(ID, pMtl)
// textures for these tiles. :)
int MetalTile::getTexture(int dir)
{
return m_TextureFrame;
int yoff = GetPatchManager()->GetMetalSideYOffset();
if (yoff < 0)
return m_TextureFrame;
if (dir == DIR_YPOS) return m_TextureFrame;
if (dir == DIR_YNEG) return m_TextureFrame + 16 * (yoff + 1);
return m_TextureFrame + 16 * (yoff + 0);
}

View File

@@ -113,6 +113,7 @@
<ClInclude Include="..\source\client\renderer\LevelRenderer.hpp" />
<ClInclude Include="..\source\client\renderer\LightLayer.hpp" />
<ClInclude Include="..\source\client\renderer\LightUpdate.hpp" />
<ClInclude Include="..\source\client\renderer\PatchManager.hpp" />
<ClInclude Include="..\source\client\renderer\RenderChunk.hpp" />
<ClInclude Include="..\source\client\renderer\RenderList.hpp" />
<ClInclude Include="..\source\client\renderer\Tesselator.hpp" />
@@ -426,6 +427,7 @@
<ClCompile Include="..\source\client\renderer\LevelRenderer.cpp" />
<ClCompile Include="..\source\client\renderer\LightLayer.cpp" />
<ClCompile Include="..\source\client\renderer\LightUpdate.cpp" />
<ClCompile Include="..\source\client\renderer\PatchManager.cpp" />
<ClCompile Include="..\source\client\renderer\RenderChunk.cpp" />
<ClCompile Include="..\source\client\renderer\RenderList.cpp" />
<ClCompile Include="..\source\client\renderer\Tesselator.cpp" />

View File

@@ -996,6 +996,9 @@
<ClInclude Include="..\GameMods.hpp">
<Filter>source\client</Filter>
</ClInclude>
<ClInclude Include="..\source\client\renderer\PatchManager.hpp">
<Filter>source\client\renderer</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\thirdparty\raknet\TwoWayAuthentication.cpp">
@@ -1967,6 +1970,9 @@
<ClCompile Include="..\source\client\renderer\FireTexture.cpp">
<Filter>source\client\renderer</Filter>
</ClCompile>
<ClCompile Include="..\source\client\renderer\PatchManager.cpp">
<Filter>source\client\renderer</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="..\thirdparty\raknet\CMakeLists.txt">