好的,所以我想弄清楚这个游戏是如何运作的。它不是由我创建的,但我对它如何结合两个纹理、切出一个圆圈并根据角色位置将该部分渲染到屏幕上的方式很感兴趣。
纹理 #1 是 Map.bmp:
纹理 #2 是 MM.bmp:
然后将它们组合起来,在巨型地图中创建一个圆形切口。我编写了一个模拟器来执行与游戏完全相同的命令,并且它确实以与游戏相同的方式将其删除。
代码如下:
位图.h:
#ifndef BITMAP_H_INCLUDED
#define BITMAP_H_INCLUDED
#include <iostream>
#include <fstream>
#include <vector>
#include <stdexcept>
class Bitmap
{
private:
std::vector<std::uint8_t> Pixels;
std::uint32_t width, height;
std::uint16_t BitsPerPixel;
public:
Bitmap(const char* FilePath);
inline std::uint16_t Bits()
{
return BitsPerPixel;
}
inline int Width() const
{
return width;
}
inline int Height() const
{
return height;
}
inline std::uint8_t* GetPixels()
{
return Pixels.data();
}
};
#endif // BITMAP_H_INCLUDED
位图.cpp:
#include "Bitmap.h"
Bitmap::Bitmap(const char* FilePath) : Pixels(0), width(0), height(0), BitsPerPixel(0)
{
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
if (!hFile.is_open()) throw std::invalid_argument("Error: File Not Found.");
hFile.seekg(0, std::ios::end);
int Length = hFile.tellg();
hFile.seekg(0, std::ios::beg);
std::vector<std::uint8_t> FileInfo(Length);
hFile.read(reinterpret_cast<char*>(FileInfo.data()), 54);
if(FileInfo[0] != 'B' && FileInfo[1] != 'M')
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. Bitmap Required.");
}
if (FileInfo[28] != 24 && FileInfo[28] != 32)
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. 24 or 32 bit Image Required.");
}
BitsPerPixel = FileInfo[28];
width = FileInfo[18] + (FileInfo[19] << 8);
height = FileInfo[22] + (FileInfo[23] << 8);
std::uint32_t PixelsOffset = FileInfo[10] + (FileInfo[11] << 8);
std::uint32_t size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
Pixels.resize(size);
hFile.seekg (PixelsOffset, std::ios::beg);
hFile.read(reinterpret_cast<char*>(Pixels.data()), size);
hFile.close();
}
最后是模拟游戏的 Main.cpp:
主要.cpp:
#include <windows.h>
#include <iostream>
#include <GL/gl.h>
#include <GL/glext.h>
#include "Bitmap.h"
int Width = 781, Height = 591;
std::string Title = "Example";
std::string ClassName = "Example";
template<typename T, typename U>
bool xCast(T &FunctionDefinition, U FunctionAddress)
{
return (FunctionDefinition = reinterpret_cast<T>(FunctionAddress));
}
void EnableOpenGL(HWND hwnd, HDC* hDC, HGLRC* hRC);
void DisableOpenGL (HWND hwnd, HDC hDC, HGLRC hRC);
GLuint LoadTexture(std::string FilePath, GLenum Target);
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
WNDCLASSEX WndClass =
{
sizeof(WNDCLASSEX), CS_DBLCLKS, WindowProcedure,
0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION),
LoadCursor(nullptr, IDC_ARROW), HBRUSH(COLOR_WINDOW + 1),
nullptr, ClassName.c_str(), LoadIcon (nullptr, IDI_APPLICATION)
};
if(RegisterClassEx(&WndClass))
{
HDC DC = nullptr;
HGLRC HRC = nullptr;
HWND WindowHandle = CreateWindowEx(0, ClassName.c_str(), Title.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
if(WindowHandle)
{
bool Quit = false;
MSG msg = {nullptr};
EnableOpenGL(WindowHandle, &DC, &HRC);
ShowWindow(WindowHandle, SW_SHOWDEFAULT);
glViewport(0, 0, 765, 553);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 765, 553, 0.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
void (__stdcall *ptr_glActiveTextureARB) (GLenum texture);
void (__stdcall *ptr_glMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t);
xCast(ptr_glActiveTextureARB, wglGetProcAddress("glActiveTextureARB"));
xCast(ptr_glMultiTexCoord2fARB, wglGetProcAddress("glMultiTexCoord2fARB"));
GLint Map = LoadTexture("Map.bmp", GL_TEXTURE_2D);
GLint MM = LoadTexture("MM.bmp", GL_TEXTURE_RECTANGLE);
while(!Quit)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
Quit = true;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
glPushMatrix();
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Combines the two textures and draws them to the screen.
glEnable(GL_SCISSOR_TEST);
glScissor(550, 341, 154, 154);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_TEXTURE_RECTANGLE);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Map);
ptr_glActiveTextureARB(GL_TEXTURE1);
glEnable(GL_TEXTURE_RECTANGLE);
glBindTexture(GL_TEXTURE_RECTANGLE, MM);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glBegin(GL_QUADS);
glColor3f(1, 1, 1);
ptr_glMultiTexCoord2fARB(GL_TEXTURE0, 0, 1);
ptr_glMultiTexCoord2fARB(GL_TEXTURE1, -194.55215, 353.57529);
glVertex2f(355.44785, -141.57529);
ptr_glMultiTexCoord2fARB(GL_TEXTURE0, 0, 0);
ptr_glMultiTexCoord2fARB(GL_TEXTURE1, -212.4122, -194.13358);
glVertex2f(337.5878, 406.13358);
ptr_glMultiTexCoord2fARB(GL_TEXTURE0, 1, 0);
ptr_glMultiTexCoord2fARB(GL_TEXTURE1, 335.29663, -211.99362);
glVertex2f(885.29663, 423.99362);
ptr_glMultiTexCoord2fARB(GL_TEXTURE0, 1, 1);
ptr_glMultiTexCoord2fARB(GL_TEXTURE1, 353.15674, 335.71524);
glVertex2f(903.15674, -123.71524);
glEnd();
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
glDisable(GL_TEXTURE_RECTANGLE);
ptr_glActiveTextureARB(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D);
glDisable(GL_SCISSOR_TEST);
glPopMatrix();
SwapBuffers(DC);
Sleep(1);
}
}
DisableOpenGL(WindowHandle, DC, HRC);
return msg.wParam;
}
}
return 0;
}
//Loads textures:
GLuint LoadTexture(std::string FilePath, GLenum Target)
{
Bitmap Img(FilePath.c_str());
GLuint ID = 0;
glGenTextures(1, &ID);
glBindTexture(Target, ID);
glTexParameteri(Target, GL_TEXTURE_WRAP_S, Target == GL_TEXTURE_2D ? GL_REPEAT : GL_CLAMP_TO_EDGE);
glTexParameteri(Target, GL_TEXTURE_WRAP_T, Target == GL_TEXTURE_2D ? GL_REPEAT : GL_CLAMP_TO_EDGE);
glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(Target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(Target, 0, Img.Bits() == 32 ? GL_RGBA : GL_RGB, Img.Width(), Img.Height(), 0, Img.Bits() == 32 ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, Img.GetPixels());
return ID;
}
void EnableOpenGL(HWND hwnd, HDC* hDC, HGLRC* hRC)
{
PIXELFORMATDESCRIPTOR pfd;
int iFormat;
*hDC = GetDC(hwnd);
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
iFormat = ChoosePixelFormat(*hDC, &pfd);
SetPixelFormat(*hDC, iFormat, &pfd);
*hRC = wglCreateContext(*hDC);
wglMakeCurrent(*hDC, *hRC);
}
void DisableOpenGL (HWND hwnd, HDC hDC, HGLRC hRC)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hwnd, hDC);
}
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_KEYDOWN:
{
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
}
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
输出如下:
我想知道的是,这些负坐标是如何工作的。例如,glMultiTexCoord2fArb 和 glVertex2f 的坐标超出了视口的范围。视口为 765x553。它也不适合剪刀区域。
它如何确定要合并的纹理的哪些部分,有没有办法我自己可以做到这一点,这样我就不必使用负坐标?换句话说,我怎样才能将这些负坐标转换为常规正坐标并找出两个纹理的哪些部分被合并?我已经编写了上面的示例来测试并弄清楚它是如何工作的,但我似乎看不出这些负坐标和包装是如何适应的。