📄 app_shell.cpp
字号:
/*
Warning... this App_Shell class is not intended for serious
shipping games. It contains whatever I needed to do in order
to get things running as quickly and simply as possible.
It leaks memory, because I tend not to bother with cleanup.
Also it has some O(n) functions that could be O(k). So if
you plan to use this in something else, you should review
it and fix whatever you perceive to be an issue.
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include "os_specific_opengl_headers.h"
#include <gl/glu.h>
#include "jpeg_load.h"
#include "glext.h"
#include "../framework.h"
App_Shell *app_shell;
// @Cleanup: I would like to ditch these globals at some point.
static HDC global_dc = 0;
static HGLRC global_gl_rc;
static HPALETTE global_palette = NULL;
static HINSTANCE global_hinstance;
static LARGE_INTEGER global_base_time; // @Refactor: Not require this?
bool app_is_active = false;
struct Loaded_Texture_Record {
char *name;
int texture_handle;
};
struct Font_Character_Info {
float u0, v0, u1, v1;
float a, b, advance;
};
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
static LARGE_INTEGER get_time_reading() {
LARGE_INTEGER freq;
LARGE_INTEGER time;
BOOL ok = QueryPerformanceFrequency(&freq);
assert(ok == TRUE);
freq.QuadPart = freq.QuadPart / 1000;
ok = QueryPerformanceCounter(&time);
assert(ok == TRUE);
time.QuadPart = time.QuadPart / freq.QuadPart;
return time;
}
void add_letter_quad(Font *font, Font_Character_Info *info, float xs, float ys, float iw) {
float u0 = info->u0;
float u1 = info->u1;
float npix = (u1 - u0) * iw;
float w = npix;
float h = font->character_height;
glTexCoord2f(u0, info->v0);
glVertex2f(xs, ys);
glTexCoord2f(u1, info->v0);
glVertex2f(xs+w, ys);
glTexCoord2f(u1, info->v1);
glVertex2f(xs+w, ys+h);
glTexCoord2f(u0, info->v1);
glVertex2f(xs, ys+h);
}
inline int remap_letter(Font *font, unsigned int letter) {
if (letter < font->character_range_low) return -1;
if (letter > font->character_range_high) return -1;
letter -= font->character_range_low;
return letter;
}
float Font::get_string_width_in_pixels(char *s) {
float sum = 0;
while (*s) {
int letter = remap_letter(this, *s);
if (letter != -1) {
Font_Character_Info *info = &character_info[letter];
sum += info->advance;
}
s++;
}
return sum;
}
void App_Shell::draw_text(Font *font, float xs, float ys, char *s, float cr, float cg, float cb, float ca,
float max_width) {
int len = strlen(s);
glBegin(GL_QUADS);
xs -= 0.5f;
glColor4f(cr, cg, cb, ca);
float iw = (float)font->texture_width;
float width = 0;
int i;
for (i = 0; i < len; i++) {
int letter = remap_letter(font, s[i]);
if (letter == -1) continue;
Font_Character_Info *info = &font->character_info[letter];
if (max_width && (width + info->advance > max_width)) break;
add_letter_quad(font, info, xs, ys, iw);
xs += info->advance;
width += info->advance;
}
glEnd();
}
void App_Shell::draw_texture_quad(int texture_handle, float x, float y,
float width, float height) {
bitmap_mode_begin(texture_handle);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(x, y);
glTexCoord2f(1, 0);
glVertex2f(x + width, y);
glTexCoord2f(1, 1);
glVertex2f(x + width, y + height);
glTexCoord2f(0, 1);
glVertex2f(x, y + height);
glEnd();
bitmap_mode_end();
}
GLuint gl_texture_from_bitmap(char *bits, int width, int height) {
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = width;
rect.bottom = height;
GLuint texture_id;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
gluBuild2DMipmaps(GL_TEXTURE_2D, 4, width, height, GL_BGRA_EXT,
GL_UNSIGNED_BYTE, (void *)bits);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
return texture_id;
}
unsigned char *do_dumb_alpha_thing(unsigned char *src, int width, int height) {
int npixels = width * height;
unsigned char *dest = (unsigned char *)malloc(npixels * 4);
int i;
for (i = 0; i < npixels; i++) {
int r = src[i * 3 + 0];
int g = src[i * 3 + 1];
int b = src[i * 3 + 2];
// int a = (r + g + b) / 3;
int a = 255;
dest[i * 4 + 0] = b;
dest[i * 4 + 1] = g;
dest[i * 4 + 2] = r;
dest[i * 4 + 3] = a;
}
free(src);
return dest;
}
unsigned char *do_dumb_alpha_thing_for_font(unsigned char *src, int width, int height) {
int npixels = width * height;
unsigned char *dest = (unsigned char *)malloc(npixels * 4);
int i;
for (i = 0; i < npixels; i++) {
int r = src[i * 3 + 0];
int g = src[i * 3 + 1];
int b = src[i * 3 + 2];
int a = r;
r = 255;
g = 255;
b = 255;
dest[i * 4 + 0] = r;
dest[i * 4 + 1] = g;
dest[i * 4 + 2] = b;
dest[i * 4 + 3] = a;
}
free(src);
return dest;
}
bool texture_from_file(char *filename, GLuint *result,
int *width_result, int *height_result, bool for_font) {
unsigned char *bitmap;
int width, height;
bool success = load_jpeg_file(filename, &bitmap, &width, &height);
if (success == false) return false;
if (for_font) {
bitmap = do_dumb_alpha_thing_for_font(bitmap, width, height);
} else {
bitmap = do_dumb_alpha_thing(bitmap, width, height);
}
GLuint texture = gl_texture_from_bitmap((char *)bitmap, width, height);
// Give the appropriate data values back to the caller.
*result = texture;
*width_result = width;
*height_result = height;
// @Leak:
// By the way... since we aren't remembering the pointer to 'bitmap'
// anywhere, we can never deallocate it, so it gets leaked. Not a big
// deal in this demo, but for real software, you want to change that.
return true;
}
// Wacky structures used in the font file format.
struct Glx_Glyph_Data {
int a, b;
int advance;
float left, top, right, bottom;
};
struct Glx_Tex_Font_Data {
int num_glyphs;
int bmp_height, bmp_width;
int character_height;
Glx_Glyph_Data glyphs[94];
};
#ifdef NOT
Type glxGLYPHDATA
'glyph metrics, in pixels
A As Long 'distance to add to the current position before drawing the character glyph
B As Long 'width of the drawn portion of the character glyph
gAdvance As Long 'The total width of a character to use when drawing
'tex coords of the glyph
TexLeft As Single
TexTop As Single
TexRight As Single
TexBottom As Single
End Type
Each font file begins with a header which contains the array:
Type glxTEXFONTDATA
Name As String
NumGlyphs As Long
'size in pixels
BmpHeight As Long
BmpWidth As Long
BmpFontHeight As Long 'total height of a font character
'size in tex coords
Glyph(1 To 94) As glxGLYPHDATA
End Type
#endif NOT
char *append(char *name, char *extension) {
int len_total = strlen(name) + strlen(extension);
char *filename = new char[len_total + 1];
strcpy(filename, name);
strcat(filename, extension);
return filename;
}
bool App_Shell::load_character_map(Font *font, char *name) {
char *filename = append(name, ".font_map");
FILE *f = fopen(filename, "rb");
if (!f) return false;
Glx_Tex_Font_Data data;
unsigned int low = fgetc(f);
unsigned int high = fgetc(f);
int num_chars_to_eat = (high << 8) | low;
int i;
for (i = 0; i < num_chars_to_eat; i++) {
fgetc(f);
}
int len = fread(&data, sizeof(data), 1, f);
delete [] filename;
fclose(f);
if (len != 1) return false;
int num_characters = 94;
font->character_info = new Font_Character_Info[num_characters];
font->character_range_low = 32;
font->character_range_high = font->character_range_low + num_characters - 1;
font->character_height = data.character_height;
for (i = 0; i < num_characters; i++) {
Font_Character_Info *info = &font->character_info[i];
Glx_Glyph_Data *glyph = &data.glyphs[i];
info->a = glyph->a;
info->b = glyph->b;
info->advance = glyph->advance;
info->u0 = glyph->left;
info->u1 = glyph->right;
info->v0 = 1.0f - glyph->bottom;
info->v1 = 1.0f - glyph->top;
}
return true;
}
Font *App_Shell::load_font(char *name) {
Loaded_Texture_Info info;
char *texture_name = append(name, ".jpg");
load_texture(&info, texture_name, true);
delete [] texture_name;
if (!info.loaded_successfully) return NULL;
Font *result = new Font();
result->texture_handle = info.texture_handle;
result->texture_width = info.width;
result->texture_height = info.height;
bool success = load_character_map(result, name);
if (!success) {
delete result;
return NULL;
}
return result;
}
void App_Shell::load_texture(Loaded_Texture_Info *info, char *name, bool for_font) {
GLuint handle;
bool success = texture_from_file(name, &handle,
&info->width, &info->height, for_font);
if (!success) success = texture_from_file("data\\ground.jpg", &handle,
&info->width, &info->height, for_font);
info->texture_handle = handle;
info->loaded_successfully = success;
}
int App_Shell::find_or_load_texture(char *name, char *prefix) {
Loaded_Texture_Record *record;
Foreach(loaded_textures, record) {
if (strcmp(name, record->name) == 0) return record->texture_handle;
} Endeach;
char *suffix = ".jpg";
int len = strlen(name) + strlen(prefix) + strlen(suffix) + 1;
char *full_name = new char[len];
strcpy(full_name, prefix);
strcat(full_name, name);
strcat(full_name, suffix);
Loaded_Texture_Info info;
load_texture(&info, full_name);
delete [] full_name;
if (!info.loaded_successfully) return 0;
record = new Loaded_Texture_Record();
record->name = app_strdup(name);
record->texture_handle = info.texture_handle;
loaded_textures->add(record);
return info.texture_handle;
}
void App_Shell::init_textures() {
}
void App_Shell::line_mode_begin() {
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, screen_width, 0, screen_height, 0, -100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDepthFunc(GL_ALWAYS);
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
draw_mode = MODE_LINE;
}
void App_Shell::line_mode_end() {
draw_mode = MODE_NONE;
}
void App_Shell::triangle_mode_begin() {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LESS);
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
draw_mode = MODE_TRIANGLE;
}
void App_Shell::triangle_mode_end() {
draw_mode = MODE_NONE;
}
void App_Shell::bitmap_mode_begin(int texture_handle) {
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, screen_width, 0, screen_height, 0, -100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDepthFunc(GL_ALWAYS);
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glBindTexture(GL_TEXTURE_2D, texture_handle);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glEnable(GL_TEXTURE_2D);
GLfloat ambient[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
draw_mode = MODE_BITMAP;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -