Browse Source

Added PCX image reading support

Thomas Buck 10 years ago
parent
commit
f9d10c89c4
9 changed files with 239 additions and 15 deletions
  1. 2
    0
      ChangeLog.md
  2. 1
    5
      TODO.md
  3. 3
    9
      include/Camera.h
  4. 7
    1
      include/global.h
  5. 18
    0
      include/utils/pcx.h
  6. 7
    0
      src/Render.cpp
  7. 13
    0
      src/Texture.cpp
  8. 1
    0
      src/utils/CMakeLists.txt
  9. 187
    0
      src/utils/pcx.cpp

+ 2
- 0
ChangeLog.md View File

6
     * No longer crashes simply by walking in the wrong place
6
     * No longer crashes simply by walking in the wrong place
7
     * Added walk Action, supposed to switch to slow walking/sidesteps
7
     * Added walk Action, supposed to switch to slow walking/sidesteps
8
     * Removed unused Camera functionality (side and up vector)
8
     * Removed unused Camera functionality (side and up vector)
9
+    * Wrote simple PCX image reading library with proof-of-concept
10
+      code to load the TR2 menu background as splash screen
9
 
11
 
10
     [ 20140517 ]
12
     [ 20140517 ]
11
     * Wrote new assert() implementation for Unix that prints a call stack
13
     * Wrote new assert() implementation for Unix that prints a call stack

+ 1
- 5
TODO.md View File

17
 ## Changes
17
 ## Changes
18
 
18
 
19
 * Using std::vector with [] is not bound checked. Segfaults were caused because the upper bound of the argument was never checked and garbage was returned... Do consistent checks, or use .at() as it throws an exception
19
 * Using std::vector with [] is not bound checked. Segfaults were caused because the upper bound of the argument was never checked and garbage was returned... Do consistent checks, or use .at() as it throws an exception
20
-* Creating the Room class has introduced a bug that crashes when walking the level:
21
-
22
-    Assertion failed: (sector < (int)mRooms.at(room)->sizeSectors()), function isWall, file /Users/thomas/Projekte/OpenRaider/src/World.cpp, line 180.
23
-
24
 * The wrong SkeletalModels are used by entities, except for Lara...?
20
 * The wrong SkeletalModels are used by entities, except for Lara...?
25
 
21
 
26
 ## Cmake
22
 ## Cmake
32
 ## Future Features
28
 ## Future Features
33
 
29
 
34
 * Use only assets from old TR games?
30
 * Use only assets from old TR games?
35
-* PCX image reading for eg. TR2 built-in menu background
31
+* [PCX image reading](http://bespin.org/~qz/pc-gpe/pcx.txt) for eg. TR2 built-in menu background
36
     * Cut TGA image reader, currently only used for menu background?!
32
     * Cut TGA image reader, currently only used for menu background?!
37
 * When cutscene rendering is working, use TR4/5 style menu?
33
 * When cutscene rendering is working, use TR4/5 style menu?
38
 
34
 

+ 3
- 9
include/Camera.h View File

65
     void update();
65
     void update();
66
 
66
 
67
     /*!
67
     /*!
68
-     * \brief Rotate the camera
69
-     * \param angle angle in radians
70
-     * \param x X coordinate of axis
71
-     * \param y Y coordinate of axis
72
-     * \param z Z coordinate of axis
73
-     */
74
-    void rotate(vec_t angle, vec_t x, vec_t y, vec_t z);
75
-
76
-    /*!
77
      * \brief Sends interactive command to camera
68
      * \brief Sends interactive command to camera
78
      * \param cmd valid camera command
69
      * \param cmd valid camera command
79
      */
70
      */
80
     void command(enum camera_command cmd);
71
     void command(enum camera_command cmd);
81
 
72
 
82
 private:
73
 private:
74
+
75
+    void rotate(vec_t angle, vec_t x, vec_t y, vec_t z);
76
+
83
     Quaternion mQ;         //!< Quaternion for rotation
77
     Quaternion mQ;         //!< Quaternion for rotation
84
     vec4_t mPos;           //!< Location in 3 space (aka eye)
78
     vec4_t mPos;           //!< Location in 3 space (aka eye)
85
     vec4_t mTarget;        //!< Postition we're looking at
79
     vec4_t mTarget;        //!< Postition we're looking at

+ 7
- 1
include/global.h View File

24
 #include <GL/gl.h>
24
 #include <GL/gl.h>
25
 #endif
25
 #endif
26
 
26
 
27
+// If available, use our own assert that prints the call stack
27
 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS)
28
 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS)
28
 #ifndef NDEBUG
29
 #ifndef NDEBUG
29
 [[noreturn]] void assertImplementation(const char *exp, const char *file, int line);
30
 [[noreturn]] void assertImplementation(const char *exp, const char *file, int line);
32
 #define assert(x)
33
 #define assert(x)
33
 #endif
34
 #endif
34
 #else
35
 #else
36
+// Fall back to the default C assert
35
 #include <cassert>
37
 #include <cassert>
36
 #endif
38
 #endif
37
 
39
 
40
+// Colors used for Rendering
38
 const float BLACK[]       = {  0.0f,  0.0f,  0.0f, 1.0f };
41
 const float BLACK[]       = {  0.0f,  0.0f,  0.0f, 1.0f };
39
 const float DIM_WHITE[]   = {  0.5f,  0.5f,  0.5f, 1.0f };
42
 const float DIM_WHITE[]   = {  0.5f,  0.5f,  0.5f, 1.0f };
40
 const float WHITE[]       = {  1.0f,  1.0f,  1.0f, 1.0f };
43
 const float WHITE[]       = {  1.0f,  1.0f,  1.0f, 1.0f };
46
 const float YELLOW[]      = {  1.0f,  1.0f,  0.0f, 1.0f };
49
 const float YELLOW[]      = {  1.0f,  1.0f,  0.0f, 1.0f };
47
 const float CYAN[]        = {  0.0f,  1.0f,  1.0f, 1.0f };
50
 const float CYAN[]        = {  0.0f,  1.0f,  1.0f, 1.0f };
48
 
51
 
52
+// Actions that can be bound to a key and sent to the game engine
49
 typedef enum {
53
 typedef enum {
50
     menuAction = 0,
54
     menuAction = 0,
51
     consoleAction, // menu and console should always be the first two items
55
     consoleAction, // menu and console should always be the first two items
62
     ActionEventCount // Should always be at the end
66
     ActionEventCount // Should always be at the end
63
 } ActionEvents;
67
 } ActionEvents;
64
 
68
 
69
+// Keys available for binding
65
 typedef enum {
70
 typedef enum {
66
     zeroKey = '0', oneKey = '1', twoKey = '2',
71
     zeroKey = '0', oneKey = '1', twoKey = '2',
67
     threeKey = '3', fourKey = '4', fiveKey = '5',
72
     threeKey = '3', fourKey = '4', fiveKey = '5',
83
     rightguiKey, rightbracketKey, rightshiftKey, scrolllockKey,
88
     rightguiKey, rightbracketKey, rightshiftKey, scrolllockKey,
84
     semicolonKey, slashKey, spaceKey, tabKey,
89
     semicolonKey, slashKey, spaceKey, tabKey,
85
     leftmouseKey, middlemouseKey, rightmouseKey,
90
     leftmouseKey, middlemouseKey, rightmouseKey,
86
-    unknownKey
91
+
92
+    unknownKey // Should always be at the end
87
 } KeyboardButton;
93
 } KeyboardButton;
88
 
94
 
89
 #endif
95
 #endif

+ 18
- 0
include/utils/pcx.h View File

1
+/*!
2
+ * \file include/utils/pcx.h
3
+ * \brief PCX image reader
4
+ *
5
+ * Based on official technical documentation from ZSoft:
6
+ * http://bespin.org/~qz/pc-gpe/pcx.txt
7
+ *
8
+ * \author xythobuz
9
+ */
10
+
11
+#ifndef _UTILS_PCX_H_
12
+#define _UTILS_PCX_H_
13
+
14
+// Returns 0 on success
15
+int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height);
16
+
17
+#endif
18
+

+ 7
- 0
src/Render.cpp View File

101
     if (mTexture.loadColorTexture(color, 32, 32) > -1)
101
     if (mTexture.loadColorTexture(color, 32, 32) > -1)
102
         numTextures++;
102
         numTextures++;
103
 
103
 
104
+#ifdef PCX_PROOF_OF_CONCEPT
105
+    filename = bufferString("/Users/thomas/.OpenRaider/paks/tr2/TITLE.PCX");
106
+    if (mTexture.loadTGA(filename) > -1)
107
+        numTextures++;
108
+    delete [] filename;
109
+#else
104
     //! \fixme Error Checking. Return negative error code, check in calling place too
110
     //! \fixme Error Checking. Return negative error code, check in calling place too
105
     filename = bufferString("%s/%s", textureDir, "splash.tga");
111
     filename = bufferString("%s/%s", textureDir, "splash.tga");
106
     if (mTexture.loadTGA(filename) > -1)
112
     if (mTexture.loadTGA(filename) > -1)
107
         numTextures++;
113
         numTextures++;
108
     delete [] filename;
114
     delete [] filename;
115
+#endif
109
 
116
 
110
     return numTextures;
117
     return numTextures;
111
 }
118
 }

+ 13
- 0
src/Texture.cpp View File

12
 #include <stdarg.h>
12
 #include <stdarg.h>
13
 
13
 
14
 #include "global.h"
14
 #include "global.h"
15
+#include "utils/pcx.h"
15
 #include "utils/strings.h"
16
 #include "utils/strings.h"
16
 #include "utils/tga.h"
17
 #include "utils/tga.h"
17
 #include "Texture.h"
18
 #include "Texture.h"
279
 }
280
 }
280
 
281
 
281
 int Texture::loadTGA(const char *filename) {
282
 int Texture::loadTGA(const char *filename) {
283
+#ifdef PCX_PROOF_OF_CONCEPT
284
+    unsigned char *image;
285
+    unsigned int w, h;
286
+    int id = -1;
287
+    int error = pcxLoad(filename, &image, &w, &h);
288
+    if (error == 0) {
289
+        id = loadBuffer(image, w, h, RGBA, 32);
290
+        delete [] image;
291
+    }
292
+    return id;
293
+#else
282
     FILE *f;
294
     FILE *f;
283
     unsigned char *image = NULL;
295
     unsigned char *image = NULL;
284
     unsigned char *image2 = NULL;
296
     unsigned char *image2 = NULL;
321
     }
333
     }
322
 
334
 
323
     return id;
335
     return id;
336
+#endif
324
 }
337
 }
325
 
338
 
326
 int Texture::nextPower(int seed) {
339
 int Texture::nextPower(int seed) {

+ 1
- 0
src/utils/CMakeLists.txt View File

1
 # Source files
1
 # Source files
2
+set (UTIL_SRCS ${UTIL_SRCS} "pcx.cpp")
2
 set (UTIL_SRCS ${UTIL_SRCS} "strings.cpp")
3
 set (UTIL_SRCS ${UTIL_SRCS} "strings.cpp")
3
 set (UTIL_SRCS ${UTIL_SRCS} "tga.cpp")
4
 set (UTIL_SRCS ${UTIL_SRCS} "tga.cpp")
4
 set (UTIL_SRCS ${UTIL_SRCS} "time.cpp")
5
 set (UTIL_SRCS ${UTIL_SRCS} "time.cpp")

+ 187
- 0
src/utils/pcx.cpp View File

1
+/*!
2
+ * \file src/utils/pcx.cpp
3
+ * \brief PCX image reader
4
+ *
5
+ * Based on official technical documentation from ZSoft:
6
+ * http://bespin.org/~qz/pc-gpe/pcx.txt
7
+ *
8
+ * \author xythobuz
9
+ */
10
+
11
+#include <fstream>
12
+
13
+#include "global.h"
14
+#include "utils/pcx.h"
15
+
16
+#include "Console.h"
17
+
18
+#ifdef DEBUG
19
+#define pcxPrint getConsole().print
20
+#else
21
+void pcxPrint(...) { }
22
+#endif
23
+
24
+int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height) {
25
+    std::ifstream file(filename, std::ios::in | std::ios::binary);
26
+
27
+    // Read raw PCX header, 128 bytes
28
+    unsigned char *header = new unsigned char[128];
29
+
30
+    // Basic validation
31
+    if (!file.read((char *)(&header[0]), 128)) {
32
+        pcxPrint("File not big enough for valid PCX header!");
33
+        delete [] header;
34
+        return -1;
35
+    }
36
+
37
+    if (header[0] != 0x0A) {
38
+        pcxPrint("Magic number at file start is wrong (0x%X != 0x0A)", header[0]);
39
+        delete [] header;
40
+        return -2;
41
+    }
42
+
43
+    if ((header[1] != 0) && ((header[1] < 2) || (header[1] > 5))) {
44
+        // Valid: 0, 2, 3, 4, 5
45
+        pcxPrint("Unknown PCX file format version (%d)", header[1]);
46
+        delete [] header;
47
+        return -3;
48
+    }
49
+
50
+    if ((header[2] != 0) && (header[2] != 1)) {
51
+        pcxPrint("Unknown PCX file encoding (%d)", header[2]);
52
+        delete [] header;
53
+        return -4;
54
+    }
55
+
56
+    if (header[3] != 8) {
57
+        pcxPrint("Only supporting 8bit (%dbit)", header[3]);
58
+        delete [] header;
59
+        return -5;
60
+    }
61
+
62
+    if (header[64] != 0) {
63
+        pcxPrint("Reserved field is  used (%d != 0)", header[64]);
64
+        delete [] header;
65
+        return -6;
66
+    }
67
+
68
+    // Read header informations
69
+    bool versionFive = (header[1] == 5);
70
+    bool compressed = (header[2] == 1);
71
+    //unsigned char bitsPerPixel = header[3];
72
+    unsigned int xMin = header[4] | (header[5] << 8);
73
+    unsigned int yMin = header[6] | (header[7] << 8);
74
+    unsigned int xMax = header[8] | (header[9] << 8);
75
+    unsigned int yMax = header[10] | (header[11] << 8);
76
+    //unsigned int hDPI = header[12] | (header[13] << 8);
77
+    //unsigned int vDPI = header[14] | (header[15] << 8);
78
+    //unsigned char *colormap = header + 16;
79
+    unsigned char nPlanes = header[65];
80
+    unsigned int bytesPerLine = header[66] | (header[67] << 8);
81
+    //unsigned int paletteInfo = header[68] | (header[69] << 8);
82
+    //unsigned int hScreenSize = header[70] | (header[71] << 8); // Only in some versionFive files
83
+    //unsigned int vScreenSize = header[72] | (header[73] << 8); // Only in some versionFive files
84
+
85
+    delete [] header;
86
+
87
+    // Calculations
88
+    *width = xMax - xMin + 1;
89
+    *height = yMax - yMin + 1;
90
+    unsigned long totalBytes = nPlanes * bytesPerLine; // total bytes per scan line
91
+    unsigned long imageSize = totalBytes * *height;
92
+    unsigned char *buffer = new unsigned char[imageSize];
93
+    unsigned long b = 0;
94
+
95
+    // Read encoded pixel data
96
+    for (unsigned long i = 0; i < imageSize;) {
97
+        unsigned int n = 1; // Run-length-encoding assumes 1
98
+        int c = file.get();
99
+        if (!file) {
100
+            pcxPrint("Could not read data (%lu%s)", i, (file.eof() ? " EOF" : ""));
101
+            delete buffer;
102
+            return -7;
103
+        }
104
+
105
+        // Run-Length-Encoding
106
+        if (compressed) {
107
+            if ((c & 0xC0) == 0xC0) {
108
+                n = c & 0x3F;
109
+                c = file.get();
110
+                if (!file) {
111
+                    pcxPrint("Could not read data rle (%lu%s)", i, (file.eof() ? " EOF" : ""));
112
+                    return -8;
113
+                }
114
+            }
115
+        }
116
+
117
+        for (unsigned int j = 0; j < n; j++)
118
+            buffer[b++] = (unsigned char)c;
119
+
120
+        i += n;
121
+    }
122
+
123
+    // Read color palette
124
+    unsigned char *palette = NULL;
125
+    if (versionFive) {
126
+        int c = file.get();
127
+        if ((c == 12) && file) {
128
+            palette = new unsigned char[768];
129
+            for (unsigned int i = 0; i < 768; i++) {
130
+                palette[i] = (unsigned char)file.get();
131
+                if (!file) {
132
+                    pcxPrint("Could not read 256 color palette (%d)", i);
133
+                    return -9;
134
+                }
135
+            }
136
+        }
137
+    }
138
+
139
+    // Bring buffer into preferred format
140
+    unsigned long size = *width * *height * 4;
141
+    *image = new unsigned char[size];
142
+    for (unsigned int y = 0; y < *height; y++) {
143
+        for (unsigned int x = 0; x < *width; x++) {
144
+            unsigned long baseIndex = (x + (y * *width)) * 4;
145
+            unsigned char alpha = 255, red = 0, green = 0, blue = 0;
146
+
147
+            if (palette != NULL) {
148
+                if (nPlanes == 1) {
149
+                    red = palette[buffer[(y * totalBytes) + x] * 3];
150
+                    green = palette[(buffer[(y * totalBytes) + x] * 3) + 1];
151
+                    blue = palette[(buffer[(y * totalBytes) + x] * 3) + 2];
152
+                } else {
153
+                    pcxPrint("Unsupported number of planes (%d)", nPlanes);
154
+                    delete [] buffer;
155
+                    delete [] *image;
156
+                    return -10;
157
+                }
158
+            } else {
159
+                if ((nPlanes == 3) || (nPlanes == 4)) {
160
+                    red = buffer[(y * totalBytes) + x];
161
+                    green = buffer[(y * totalBytes) + *width + x];
162
+                    blue = buffer[(y * totalBytes) + (2 * *width) + x];
163
+                    if (nPlanes == 4)
164
+                        alpha = buffer[(y * totalBytes) + (3 * *width) + x];
165
+                } else if (nPlanes == 1) {
166
+                    red = green = blue = buffer[(y * totalBytes) + x];
167
+                } else {
168
+                    pcxPrint("Unsupported number of planes (%d)", nPlanes);
169
+                    delete [] buffer;
170
+                    delete [] *image;
171
+                    return -11;
172
+                }
173
+            }
174
+
175
+            (*image)[baseIndex + 0] = red;
176
+            (*image)[baseIndex + 1] = green;
177
+            (*image)[baseIndex + 2] = blue;
178
+            (*image)[baseIndex + 3] = alpha;
179
+        }
180
+    }
181
+
182
+    if (palette != NULL)
183
+        delete [] palette;
184
+
185
+    return 0;
186
+}
187
+

Loading…
Cancel
Save