material.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. #include "material.h"
  2. #include "texture.h"
  3. #include "cache.h"
  4. #include "collection.h"
  5. #include "vmfparser.h"
  6. #include "common.h"
  7. struct MaterialContext {
  8. struct Material *mat;
  9. struct TokenContext tok;
  10. const char *key;
  11. int key_length;
  12. char value[128];
  13. };
  14. enum KeyValueResult {KeyValue_Read, KeyValue_Error, KeyValue_End};
  15. static enum KeyValueResult getNextKeyValue(struct MaterialContext *ctx) {
  16. for (;;) {
  17. enum TokenType type = getNextToken(&ctx->tok);
  18. if (type == Token_End || type == Token_CurlyClose) return KeyValue_End;
  19. if (type != Token_String) return KeyValue_Error;
  20. ctx->key = ctx->tok.str_start;
  21. ctx->key_length = ctx->tok.str_length;
  22. type = getNextToken(&ctx->tok);
  23. if (type == Token_CurlyOpen) {
  24. /* skip unsupported proxies and DX-level specifics */
  25. if (strncmp(ctx->key, "replace", ctx->key_length) != 0)
  26. PRINTF("Skipping section %.*s", ctx->key_length, ctx->key);
  27. int depth = 1;
  28. for (;;) {
  29. type = getNextToken(&ctx->tok);
  30. if (type == Token_CurlyClose) {
  31. if (--depth == 0)
  32. break;
  33. } else if (type == Token_CurlyOpen) {
  34. ++depth;
  35. } else if (type != Token_String)
  36. return KeyValue_Error;
  37. }
  38. } else if (type != Token_String) {
  39. return KeyValue_Error;
  40. } else {
  41. if (ctx->tok.str_length > (int)sizeof(ctx->value) - 1) {
  42. PRINTF("Value is too long: %d", ctx->tok.str_length);
  43. return KeyValue_Error;
  44. }
  45. memcpy(ctx->value, ctx->tok.str_start, ctx->tok.str_length);
  46. ctx->value[ctx->tok.str_length] = '\0';
  47. return KeyValue_Read;
  48. }
  49. } /* loop until key value pair found */
  50. } /* getNextKeyValue */
  51. static const char * const ignore_params[] = {
  52. "$surfaceprop", "$surfaceprop2", "$tooltexture",
  53. "%tooltexture", "%keywords", "%compilewater", "%detailtype",
  54. "%compilenolight", "%compilepassbullets",
  55. "replace", /* TODO implement */
  56. 0
  57. };
  58. static int materialLoad(struct IFile *file, struct ICollection *coll, struct Material *output, struct Stack *tmp) {
  59. char *buffer = stackAlloc(tmp, file->size + 1);
  60. int retval = 0;
  61. if (!buffer) {
  62. PRINT("Out of temp memory");
  63. return 0;
  64. }
  65. if (file->size != file->read(file, 0, file->size, buffer)) return 0;
  66. buffer[file->size] = '\0';
  67. struct MaterialContext ctx;
  68. ctx.mat = output;
  69. ctx.tok.cursor = buffer;
  70. ctx.tok.end = NULL;
  71. #define EXPECT_TOKEN(type) \
  72. if (getNextToken(&ctx.tok) != type) { \
  73. PRINTF("Unexpected token at position %zd, expecting %d; left: %s", ctx.tok.cursor - buffer, type, ctx.tok.cursor); \
  74. goto exit; \
  75. }
  76. EXPECT_TOKEN(Token_String);
  77. const char *shader = ctx.tok.str_start;
  78. const int shader_length = ctx.tok.str_length;
  79. EXPECT_TOKEN(Token_CurlyOpen);
  80. for (;;) {
  81. const enum KeyValueResult result = getNextKeyValue(&ctx);
  82. if (result == KeyValue_End)
  83. break;
  84. if (result != KeyValue_Read) {
  85. retval = -1;
  86. goto exit;
  87. }
  88. int skip = 0;
  89. for (const char *const *ignore = ignore_params; ignore[0] != 0; ++ignore)
  90. if (strncasecmp(ignore[0], ctx.key, ctx.key_length) == 0) {
  91. skip = 1;
  92. break;
  93. }
  94. if (skip) continue;
  95. if (strncasecmp("$basetexture", ctx.key, ctx.key_length) == 0) {
  96. output->base_texture[0] = textureGet(ctx.value, coll, tmp);
  97. } else if (strncasecmp("$basetexture2", ctx.key, ctx.key_length) == 0) {
  98. output->base_texture[1] = textureGet(ctx.value, coll, tmp);
  99. } else if (strncasecmp("$basetexturetransform", ctx.key, ctx.key_length) == 0) {
  100. } else if (strncasecmp("$basetexturetransform2", ctx.key, ctx.key_length) == 0) {
  101. } else if (strncasecmp("$detail", ctx.key, ctx.key_length) == 0) {
  102. output->detail = textureGet(ctx.value, coll, tmp);
  103. } else if (strncasecmp("$detailscale", ctx.key, ctx.key_length) == 0) {
  104. } else if (strncasecmp("$detailblendfactor", ctx.key, ctx.key_length) == 0) {
  105. } else if (strncasecmp("$detailblendmode", ctx.key, ctx.key_length) == 0) {
  106. } else if (strncasecmp("$parallaxmap", ctx.key, ctx.key_length) == 0) {
  107. } else if (strncasecmp("$parallaxmapscale", ctx.key, ctx.key_length) == 0) {
  108. } else if (strncasecmp("$bumpmap", ctx.key, ctx.key_length) == 0) {
  109. /* output->bump = textureGet(ctx.value, coll, tmp); */
  110. } else if (strncasecmp("$envmap", ctx.key, ctx.key_length) == 0) {
  111. /* output->envmap = textureGet(ctx.value, coll, tmp); */
  112. } else if (strncasecmp("$fogenable", ctx.key, ctx.key_length) == 0) {
  113. } else if (strncasecmp("$fogcolor", ctx.key, ctx.key_length) == 0) {
  114. } else if (strncasecmp("$alphatest", ctx.key, ctx.key_length) == 0) {
  115. } else if (strncasecmp("$translucent", ctx.key, ctx.key_length) == 0) {
  116. } else if (strncasecmp("$envmapcontrast", ctx.key, ctx.key_length) == 0) {
  117. } else if (strncasecmp("$envmapsaturation", ctx.key, ctx.key_length) == 0) {
  118. } else if (strncasecmp("$envmaptint", ctx.key, ctx.key_length) == 0) {
  119. } else if (strncasecmp("$normalmapalphaenvmapmask", ctx.key, ctx.key_length) == 0) {
  120. } else if (strncasecmp("$envmapmask", ctx.key, ctx.key_length) == 0) {
  121. } else if (strncasecmp("$nodiffusebumplighting", ctx.key, ctx.key_length) == 0) {
  122. } else if (strncasecmp("$AlphaTestReference", ctx.key, ctx.key_length) == 0) {
  123. } else if (strncasecmp("$basealphaenvmapmask", ctx.key, ctx.key_length) == 0) {
  124. } else if (strncasecmp("$selfillum", ctx.key, ctx.key_length) == 0) {
  125. } else if (strncasecmp("$reflectivity", ctx.key, ctx.key_length) == 0) {
  126. } else if (strncasecmp("include", ctx.key, ctx.key_length) == 0) {
  127. char *vmt = strstr(ctx.value, ".vmt");
  128. if (vmt)
  129. *vmt = '\0';
  130. if (strstr(ctx.value, "materials/") == ctx.value)
  131. *output = *materialGet(ctx.value + 10, coll, tmp);
  132. } else {
  133. PRINTF("Material shader:%.*s, unknown param %.*s = %s",
  134. shader_length, shader, ctx.key_length, ctx.key, ctx.value);
  135. }
  136. } /* for all properties */
  137. if (!output->base_texture[0]) {
  138. PRINT("Material doesn't have base texture");
  139. output->base_texture[0] = cacheGetTexture("opensource/placeholder");
  140. }
  141. retval = 1;
  142. exit:
  143. if (retval == -1)
  144. PRINTF("Error parsing material with shader %.*s: %s", shader_length, shader, ctx.tok.cursor);
  145. stackFreeUpToPosition(tmp, buffer);
  146. return retval;
  147. }
  148. const struct Material *materialGet(const char *name, struct ICollection *collection, struct Stack *tmp) {
  149. const struct Material *mat = cacheGetMaterial(name);
  150. if (mat) return mat;
  151. struct IFile *matfile;
  152. if (CollectionOpen_Success != collectionChainOpen(collection, name, File_Material, &matfile)) {
  153. PRINTF("Material \"%s\" not found", name);
  154. return cacheGetMaterial("opensource/placeholder");
  155. }
  156. struct Material localmat;
  157. memset(&localmat, 0, sizeof localmat);
  158. if (materialLoad(matfile, collection, &localmat, tmp) == 0) {
  159. PRINTF("Material \"%s\" found, but could not be loaded", name);
  160. } else {
  161. cachePutMaterial(name, &localmat);
  162. mat = cacheGetMaterial(name);
  163. }
  164. matfile->close(matfile);
  165. return mat ? mat : cacheGetMaterial("opensource/placeholder");
  166. }