material.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. typedef struct {
  8. ICollection *collection;
  9. Stack *temp;
  10. StringView shader;
  11. Material *mat;
  12. int depth;
  13. } MaterialContext;
  14. static VMFAction materialReadKeyValue(MaterialContext *ctx, const VMFKeyValue *kv) {
  15. if (kv->value.length > 127)
  16. return VMFAction_SemanticError;
  17. char value[128];
  18. memset(value, 0, sizeof value);
  19. memcpy(value, kv->value.str, kv->value.length);
  20. if (strncasecmp("$basetexture", kv->key.str, kv->key.length) == 0) {
  21. ctx->mat->base_texture.texture = textureGet(value, ctx->collection, ctx->temp);
  22. } else if (strncasecmp("$basetexturetransform", kv->key.str, kv->key.length) == 0) {
  23. AVec2f center, scale, translate;
  24. float rotate;
  25. if (7 != sscanf(value, "center %f %f scale %f %f rotate %f translate %f %f",
  26. &center.x, &center.y, &scale.x, &scale.y, &rotate, &translate.x, &translate.y)) {
  27. PRINTF("ERR: transform has wrong format: \"%s\"", value);
  28. } else {
  29. ctx->mat->base_texture.transform.scale = scale;
  30. ctx->mat->base_texture.transform.translate = translate;
  31. // TODO support rotation
  32. }
  33. } else if (strncasecmp("include", kv->key.str, kv->key.length) == 0) {
  34. char *vmt = strstr(value, ".vmt");
  35. if (vmt)
  36. *vmt = '\0';
  37. if (strstr(value, "materials/") == value)
  38. *ctx->mat = *materialGet(value + 10, ctx->collection, ctx->temp);
  39. }
  40. return VMFAction_Continue;
  41. }
  42. static VMFAction materialParserCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv);
  43. static VMFAction materialShaderCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv);
  44. static VMFAction materialPatchCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv);
  45. static VMFAction materialPatchCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  46. //PRINTF("Entry %d (%.*s -> %.*s)", entry, PRI_SVV(kv->key), PRI_SVV(kv->value));
  47. MaterialContext *ctx = state->user_data;
  48. VMFAction retval = VMFAction_SemanticError;
  49. switch (entry) {
  50. case VMFEntryType_KeyValue:
  51. retval = materialReadKeyValue(ctx, kv);
  52. break;
  53. case VMFEntryType_SectionOpen:
  54. ++ctx->depth;
  55. retval = VMFAction_Continue;
  56. break;
  57. case VMFEntryType_SectionClose:
  58. --ctx->depth;
  59. retval = ctx->depth == 0 ? VMFAction_Exit : VMFAction_Continue;
  60. break;
  61. }
  62. return retval;
  63. }
  64. static VMFAction materialShaderCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  65. //PRINTF("Entry %d (%.*s -> %.*s)", entry, PRI_SVV(kv->key), PRI_SVV(kv->value));
  66. MaterialContext *ctx = state->user_data;
  67. VMFAction retval = VMFAction_SemanticError;
  68. switch (entry) {
  69. case VMFEntryType_KeyValue:
  70. // Ignore any nested settings for no
  71. retval = (ctx->depth == 1) ? materialReadKeyValue(ctx, kv) : VMFAction_Continue;
  72. break;
  73. case VMFEntryType_SectionOpen:
  74. ++ctx->depth;
  75. retval = VMFAction_Continue;
  76. break;
  77. case VMFEntryType_SectionClose:
  78. --ctx->depth;
  79. retval = ctx->depth == 0 ? VMFAction_Exit : VMFAction_Continue;
  80. break;
  81. }
  82. return retval;
  83. }
  84. static void mtextureInit(MTexture *t) {
  85. memset(t, 0, sizeof(*t));
  86. t->transform.scale = aVec2f(1.f, 1.f);
  87. }
  88. static VMFAction materialParserCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  89. //PRINTF("Entry %d (%.*s -> %.*s)", entry, PRI_SVV(kv->key), PRI_SVV(kv->value));
  90. MaterialContext *ctx = state->user_data;
  91. ctx->mat->average_color = aVec3f(0,1,1);
  92. ctx->mat->shader = MShader_Unknown;
  93. mtextureInit(&ctx->mat->base_texture);
  94. VMFAction retval = VMFAction_SemanticError;
  95. switch (entry) {
  96. case VMFEntryType_KeyValue:
  97. break;
  98. case VMFEntryType_SectionOpen:
  99. ++ctx->depth;
  100. if (strncasecmp("patch", kv->key.str, kv->key.length) == 0) {
  101. state->callback = materialPatchCallback;
  102. } else {
  103. ctx->shader = kv->key;
  104. if (strncasecmp("unlitgeneric", kv->key.str, kv->key.length) == 0
  105. || strncasecmp("sky", kv->key.str, kv->key.length) == 0)
  106. ctx->mat->shader = MShader_UnlitGeneric;
  107. else if (strncasecmp("lightmappedgeneric", kv->key.str, kv->key.length) == 0
  108. || strncasecmp("worldvertextransition", kv->key.str, kv->key.length) == 0)
  109. ctx->mat->shader = MShader_LightmappedGeneric;
  110. else
  111. PRINTF("Unknown material shader " PRI_SV, PRI_SVV(kv->key));
  112. state->callback = materialShaderCallback;
  113. }
  114. retval = VMFAction_Continue;
  115. break;
  116. case VMFEntryType_SectionClose:
  117. break;
  118. }
  119. return retval;
  120. }
  121. static int materialLoad(struct IFile *file, struct ICollection *coll, Material *output, struct Stack *tmp) {
  122. char *buffer = stackAlloc(tmp, file->size);
  123. if (!buffer) {
  124. PRINT("Out of temp memory");
  125. return 0;
  126. }
  127. const int read_size = file->read(file, 0, file->size, buffer);
  128. if ((int)file->size != read_size) {
  129. PRINTF("Cannot read material file: %d != %d", (int)file->size, read_size);
  130. return 0;
  131. }
  132. MaterialContext ctx = {
  133. .collection = coll,
  134. .temp = tmp,
  135. .mat = output,
  136. .depth = 0,
  137. };
  138. VMFState parser_state = {
  139. .user_data = &ctx,
  140. .data = { .str = buffer, .length = file->size },
  141. .callback = materialParserCallback
  142. };
  143. const int success = VMFResult_Success == vmfParse(&parser_state);
  144. if (!success)
  145. PRINTF("Failed to read material with contents:\n" PRI_SV, PRI_SVV(parser_state.data));
  146. if (success && ctx.mat->base_texture.texture)
  147. ctx.mat->average_color = ctx.mat->base_texture.texture->avg_color;
  148. stackFreeUpToPosition(tmp, buffer);
  149. return success;
  150. }
  151. const Material *materialGet(const char *name, struct ICollection *collection, struct Stack *tmp) {
  152. const Material *mat = cacheGetMaterial(name);
  153. if (mat) return mat;
  154. struct IFile *matfile;
  155. if (CollectionOpen_Success != collectionChainOpen(collection, name, File_Material, &matfile)) {
  156. PRINTF("Material \"%s\" not found", name);
  157. return cacheGetMaterial("opensource/placeholder");
  158. }
  159. Material localmat;
  160. memset(&localmat, 0, sizeof localmat);
  161. if (materialLoad(matfile, collection, &localmat, tmp) == 0) {
  162. PRINTF("Material \"%s\" found, but could not be loaded", name);
  163. } else {
  164. cachePutMaterial(name, &localmat);
  165. mat = cacheGetMaterial(name);
  166. }
  167. matfile->close(matfile);
  168. return mat ? mat : cacheGetMaterial("opensource/placeholder");
  169. }