makeMapModel.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. /**
  2. Program to export maps from Anarch as a 3D model in OBJ format. The code isn't
  3. nicest, it serves just to dig out the data. Usage is this: define which map
  4. you want to export (plus other things) down below, compile the program, run it
  5. and it will write out the 3D model in OBJ format, so just redirect it to some
  6. file, then you can happily import the model in any 3D software.
  7. by drummyfish, released under CC0 1.0, public domain
  8. */
  9. #include <stdio.h>
  10. #include "../game.h"
  11. int8_t SFG_keyPressed(uint8_t key) { return 0; }
  12. void SFG_getMouseOffset(int16_t *x, int16_t *y) { }
  13. uint32_t SFG_getTimeMs() { return 0; }
  14. void SFG_sleepMs(uint16_t timeMs) { }
  15. static inline void SFG_setPixel(uint16_t x, uint16_t y, uint8_t colorIndex) { }
  16. void SFG_playSound(uint8_t soundIndex, uint8_t volume) { }
  17. void SFG_setMusic(uint8_t value) { }
  18. void SFG_processEvent(uint8_t event, uint8_t data) { }
  19. void SFG_save(uint8_t data[SFG_SAVE_SIZE]) { }
  20. uint8_t SFG_load(uint8_t data[SFG_SAVE_SIZE]) { return 0; }
  21. #define EXPORT_LEVEL 0 // which level to export
  22. #define EXPORT_SPRITES 1 // whether to export items and monsters
  23. #define MAX_ELEMENTS 131072
  24. #define UNIT 8
  25. #define HALF_UNIT (UNIT - UNIT / 2)
  26. #define HEIGHT_STEP 2
  27. #define SKY_OFFSET (UNIT * 2)
  28. #define UV_STEPS 8
  29. typedef struct
  30. {
  31. int x;
  32. int y;
  33. int z;
  34. } Vertex;
  35. typedef struct
  36. {
  37. unsigned int v0;
  38. unsigned int v1;
  39. unsigned int v2;
  40. unsigned int material; /* 0 - 16: walls,
  41. 0x01XX: flat color XX
  42. 0x02XX: item XX
  43. 0x03XX: monster XX
  44. 0x04XX: skybox XX */
  45. } Triangle;
  46. Vertex vertices[MAX_ELEMENTS];
  47. unsigned int vertexCount;
  48. Triangle triangles[MAX_ELEMENTS];
  49. unsigned int triangleCount;
  50. void addTriangle(int coords[9], unsigned int material)
  51. {
  52. Triangle t;
  53. if (triangleCount >= MAX_ELEMENTS)
  54. return;
  55. t.material = material;
  56. int *c = coords;
  57. for (int i = 0; i < 3; ++i)
  58. {
  59. int index = -1;
  60. for (int j = 0; j < vertexCount; ++j)
  61. if (vertices[j].x == c[0] && vertices[j].y == c[1] &&
  62. vertices[j].z == c[2])
  63. {
  64. // vertex already exists
  65. index = j;
  66. break;
  67. }
  68. if (index < 0)
  69. {
  70. if (vertexCount < MAX_ELEMENTS)
  71. {
  72. index = vertexCount;
  73. vertices[vertexCount].x = c[0];
  74. vertices[vertexCount].y = c[1];
  75. vertices[vertexCount].z = c[2];
  76. vertexCount++;
  77. }
  78. else
  79. return; // can't add vertex => can't add triangle
  80. }
  81. if (i == 0)
  82. t.v0 = index;
  83. else if (i == 1)
  84. t.v1 = index;
  85. else
  86. t.v2 = index;
  87. c += 3;
  88. }
  89. triangles[triangleCount] = t;
  90. triangleCount++;
  91. }
  92. void flipTriangle(Triangle *triangle)
  93. {
  94. int tmpV = triangle->v0;
  95. triangle->v0 = triangle->v1;
  96. triangle->v1 = tmpV;
  97. }
  98. void makeTriangleUVs(Triangle *t, int *uv0, int *uv1, int *uv2)
  99. {
  100. #define determineUV(va,vb,vc,c1,c2) \
  101. 2 * (!((vertices[t->vb].c1 > vertices[t->va].c1) || (vertices[t->vc].c1 > vertices[t->va].c1))) + \
  102. (vertices[t->vb].c2 < vertices[t->va].c2 || vertices[t->vc].c2 < vertices[t->va].c2)
  103. #define wind(c0,c1) \
  104. (vertices[t->v1].c0 - vertices[t->v0].c0) * (vertices[t->v2].c1 - vertices[t->v1].c1) - \
  105. (vertices[t->v1].c1 - vertices[t->v0].c1) * (vertices[t->v2].c0 - vertices[t->v1].c0)
  106. int winding = 0;
  107. int xAligned = (vertices[t->v0].z == vertices[t->v1].z) &&
  108. (vertices[t->v0].z == vertices[t->v2].z);
  109. if (vertices[t->v0].y == vertices[t->v1].y &&
  110. vertices[t->v0].y == vertices[t->v2].y)
  111. {
  112. // aligned with Y
  113. *uv0 = determineUV(v0,v1,v2,x,z);
  114. *uv1 = determineUV(v1,v0,v2,x,z);
  115. *uv2 = determineUV(v2,v0,v1,x,z);
  116. }
  117. else if ((vertices[t->v0].z == vertices[t->v1].z) &&
  118. (vertices[t->v0].z == vertices[t->v2].z))
  119. {
  120. // aligned with X
  121. *uv0 = determineUV(v0,v1,v2,y,x);
  122. *uv1 = determineUV(v1,v0,v2,y,x);
  123. *uv2 = determineUV(v2,v0,v1,y,x);
  124. winding = wind(y,x);
  125. }
  126. else
  127. {
  128. // aligned with Z
  129. *uv0 = determineUV(v0,v1,v2,y,z);
  130. *uv1 = determineUV(v1,v0,v2,y,z);
  131. *uv2 = determineUV(v2,v0,v1,y,z);
  132. winding = wind(y,z);
  133. }
  134. if (winding > 0)
  135. {
  136. *uv0 = *uv0 ^ 0x01;
  137. *uv1 = *uv1 ^ 0x01;
  138. *uv2 = *uv2 ^ 0x01;
  139. }
  140. if (vertices[t->v0].x < 0 || vertices[t->v0].x > (63 * UNIT)) // skybox?
  141. {
  142. if (vertices[t->v0].y == vertices[t->v1].y &&
  143. vertices[t->v0].y == vertices[t->v2].y)
  144. {
  145. *uv0 = 2 * (vertices[t->v0].y > 0);
  146. *uv1 = *uv0;
  147. *uv2 = *uv0;
  148. }
  149. return;
  150. }
  151. int height = 1;
  152. int dy = (vertices[t->v0].y - vertices[t->v1].y) / UNIT;
  153. if (dy < 0)
  154. dy *= -1;
  155. if (dy > height)
  156. height = dy;
  157. dy = (vertices[t->v0].y - vertices[t->v2].y) / UNIT;
  158. if (dy < 0)
  159. dy *= -1;
  160. if (dy > height)
  161. height = dy;
  162. if (height >= UV_STEPS)
  163. height = UV_STEPS - 1;
  164. height = (height - 1) * 2;
  165. if (*uv0 > 1)
  166. *uv0 += height;
  167. if (*uv1 > 1)
  168. *uv1 += height;
  169. if (*uv2 > 1)
  170. *uv2 += height;
  171. }
  172. void printObj(void)
  173. {
  174. for (int i = 0; i < UV_STEPS; ++i)
  175. printf("vt 0 %d\nvt 1 %d\n",i,i);
  176. for (int i = 0; i < vertexCount; ++i)
  177. printf("v %.4f %.4f %.4f\n",
  178. ((float) vertices[i].x) / ((float) UNIT),
  179. ((float) vertices[i].y) / ((float) UNIT),
  180. ((float) vertices[i].z) / ((float) UNIT));
  181. puts("s off");
  182. int lastMaterial = -1;
  183. for (int i = 0; i < triangleCount; ++i)
  184. {
  185. if (triangles[i].material != lastMaterial)
  186. {
  187. lastMaterial = triangles[i].material;
  188. switch (lastMaterial >> 8)
  189. {
  190. case 0: printf("usemtl wall%d\n",lastMaterial); break;
  191. case 1: printf("usemtl flat%d\n",lastMaterial & 0xff); break;
  192. case 2: printf("usemtl item%d\n",lastMaterial & 0xff); break;
  193. case 3: printf("usemtl mons%d\n",lastMaterial & 0xff); break;
  194. case 4: printf("usemtl back%d\n",lastMaterial & 0xff); break;
  195. default: break;
  196. }
  197. }
  198. int uv0, uv1, uv2;
  199. makeTriangleUVs(triangles + i,&uv0,&uv1,&uv2);
  200. printf("f %d/%d %d/%d %d/%d\n",
  201. triangles[i].v0 + 1,uv0 + 1,
  202. triangles[i].v1 + 1,uv1 + 1,
  203. triangles[i].v2 + 1,uv2 + 1);
  204. }
  205. }
  206. int coords[9];
  207. void addHorizontalTile(int x, int y, int z, int flip, unsigned int material)
  208. {
  209. flip = flip ? 3 : 0;
  210. x *= UNIT;
  211. z *= UNIT;
  212. coords[0] = x; coords[1] = y; coords[2] = z;
  213. coords[3 + flip] = x; coords[4 + flip] = y; coords[5 + flip] = z + UNIT;
  214. coords[6 - flip] = x + UNIT; coords[7 - flip] = y; coords[8 - flip] = z;
  215. addTriangle(coords,material);
  216. coords[0] = x + UNIT; coords[1] = y; coords[2] = z + UNIT;
  217. coords[3 + flip] = x + UNIT; coords[4 + flip] = y; coords[5 + flip] = z;
  218. coords[6 - flip] = x; coords[7 - flip] = y; coords[8 - flip] = z + UNIT;
  219. addTriangle(coords,material);
  220. }
  221. void addSprite(int x, int y, unsigned int material, int doubleSided)
  222. {
  223. int z = (SFG_floorHeightAt(x,y) / SFG_WALL_HEIGHT_STEP) * HEIGHT_STEP;
  224. x = 63 - x;
  225. x *= UNIT;
  226. y *= UNIT;
  227. int xc = x + HALF_UNIT;
  228. int yc = y + HALF_UNIT;
  229. for (int i = 0; i < 2; ++i)
  230. {
  231. coords[0] = i ? xc : x; coords[1] = z; coords[2] = i ? y : yc;
  232. coords[3] = i ? xc : x; coords[4] = z + UNIT; coords[5] = i ? y : yc;
  233. coords[6] = i ? xc : (x + UNIT); coords[7] = z + UNIT; coords[8] = i ? y + UNIT : yc;
  234. addTriangle(coords,material);
  235. if (doubleSided)
  236. {
  237. addTriangle(coords,material);
  238. flipTriangle(triangles + triangleCount - 1);
  239. }
  240. coords[3] = i ? xc : (x + UNIT); coords[4] = z; coords[5] = i ? y + UNIT : yc;
  241. addTriangle(coords,material);
  242. flipTriangle(triangles + triangleCount - 1);
  243. if (doubleSided)
  244. addTriangle(coords,material);
  245. }
  246. }
  247. void addVerticalTile(int x, int y1, int y2, int z, int turn, int flip, unsigned int material)
  248. {
  249. int addX = turn ? UNIT : 0;
  250. int addZ = turn ? 0 : UNIT;
  251. Vertex tmpV;
  252. x *= UNIT;
  253. z *= UNIT;
  254. if (y2 < y1)
  255. {
  256. int tmp = y1;
  257. y1 = y2;
  258. y2 = tmp;
  259. }
  260. coords[0] = x; coords[1] = y1; coords[2] = z;
  261. coords[3] = x + addX; coords[4] = y1; coords[5] = z + addZ;
  262. coords[6] = x; coords[7] = y2; coords[8] = z;
  263. addTriangle(coords,material);
  264. if (flip)
  265. flipTriangle(triangles + triangleCount - 1);
  266. coords[0] = x + addX; coords[1] = y2; coords[2] = z + addZ;
  267. coords[3] = x; coords[4] = y2; coords[5] = z;
  268. coords[6] = x + addX; coords[7] = y1; coords[8] = z + addZ;
  269. addTriangle(coords,material);
  270. if (flip)
  271. flipTriangle(triangles + triangleCount - 1);
  272. }
  273. void getTile(int x, int y, int ceiling, int *height, int *texture)
  274. {
  275. if (x < 0 || x >= 64 || y < 0 || y >= 64)
  276. {
  277. *height = 0;
  278. *texture = -1;
  279. return;
  280. }
  281. uint8_t p;
  282. SFG_TileDefinition t = SFG_getMapTile(SFG_currentLevel.levelPointer,63 - x,y,&p);
  283. RCL_Unit (*heightFunc)(int16_t, int16_t) = ceiling ?
  284. SFG_ceilingHeightAt : SFG_floorHeightAt;
  285. *height = (heightFunc(63 - x,y) / SFG_WALL_HEIGHT_STEP) * HEIGHT_STEP;
  286. *texture = ceiling ? (SFG_TILE_CEILING_TEXTURE(t)) : (SFG_TILE_FLOOR_TEXTURE(t));
  287. *texture = (*texture != SFG_TILE_TEXTURE_TRANSPARENT) ?
  288. (
  289. ((!ceiling) && p == SFG_TILE_PROPERTY_DOOR) ?
  290. SFG_currentLevel.levelPointer->doorTextureIndex :
  291. SFG_currentLevel.levelPointer->textureIndices[*texture]
  292. ) : -1;
  293. }
  294. int main(void)
  295. {
  296. SFG_init();
  297. SFG_setAndInitLevel(EXPORT_LEVEL);
  298. int sky = SFG_currentLevel.levelPointer->backgroundImage + 0x0400;
  299. // skybox:
  300. for (int i = 0; i < 3; ++i)
  301. {
  302. #define ADD_LEN (64 * UNIT + 2 * SKY_OFFSET)
  303. int cx = i, cy = (i + 1) % 3, cz = (i + 2) % 3;
  304. for (int j = 0; j < 9; ++j)
  305. coords[j] = -1 * SKY_OFFSET;
  306. coords[3 + cx] += ADD_LEN;
  307. coords[6 + cx] = coords[3 + cx];
  308. coords[6 + cy] = coords[3 + cx];
  309. addTriangle(coords,sky);
  310. coords[cz] += ADD_LEN;
  311. coords[3 + cz] += ADD_LEN;
  312. coords[6 + cz] += ADD_LEN;
  313. addTriangle(coords,sky);
  314. flipTriangle(triangles + triangleCount - 1);
  315. coords[3 + cx] = coords[cx];
  316. coords[3 + cy] = coords[6 + cx];
  317. addTriangle(coords,sky);
  318. coords[cz] -= ADD_LEN;
  319. coords[3 + cz] -= ADD_LEN;
  320. coords[6 + cz] -= ADD_LEN;
  321. addTriangle(coords,sky);
  322. flipTriangle(triangles + triangleCount - 1);
  323. }
  324. RCL_Unit (*heightFunc)(int16_t, int16_t) = SFG_floorHeightAt;
  325. for (int i = 0; i < 2; ++i) // floor and ceiling
  326. {
  327. for (int y = 0; y < 64; ++y)
  328. {
  329. for (int x = 0; x < 64; ++x)
  330. {
  331. int h, t, h2, t2;
  332. getTile(x,y,i,&h,&t);
  333. getTile(x,y,!i,&h2,&t2);
  334. if (h != h2 && (i == 0 || h != (SFG_CEILING_MAX_HEIGHT / SFG_WALL_HEIGHT_STEP) * HEIGHT_STEP))
  335. addHorizontalTile(x,h,y,i,0x0100 +
  336. (i ? SFG_currentLevel.ceilingColor : SFG_currentLevel.floorColor));
  337. for (int j = 0; j < 2; ++j) // walls
  338. {
  339. int x2 = x + (j == 0);
  340. int y2 = y + (j == 1);
  341. if (x2 < 64 && y2 < 64)
  342. getTile(x2,y2,i,&h2,&t2);
  343. else
  344. h2 = h;
  345. if (h2 != h)
  346. {
  347. int goingUp = h2 > h;
  348. int texture = (goingUp != i) ? t2 : t;
  349. if (texture != -1)
  350. addVerticalTile(x2,h,h2,y2,j,goingUp != (i == j),texture);
  351. }
  352. }
  353. }
  354. }
  355. }
  356. if (EXPORT_SPRITES)
  357. for (int i = 0; i < SFG_MAX_LEVEL_ELEMENTS; ++i)
  358. {
  359. SFG_LevelElement e = SFG_currentLevel.levelPointer->elements[i];
  360. if (e.type != SFG_LEVEL_ELEMENT_NONE && e.type != SFG_LEVEL_ELEMENT_LOCK0 &&
  361. e.type != SFG_LEVEL_ELEMENT_LOCK1 && e.type != SFG_LEVEL_ELEMENT_LOCK2 &&
  362. e.type != SFG_LEVEL_ELEMENT_BLOCKER)
  363. addSprite(e.coords[0],e.coords[1],0x0200 + ((e.type & 0xf0) == 0x20) * 0x0100 + e.type,1);
  364. }
  365. printObj();
  366. return 0;
  367. }