GXS.TextureCombiners.pas 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.TextureCombiners;
  5. (* Texture combiners setup utility functions *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. Winapi.OpenGLext,
  11. System.SysUtils,
  12. System.Classes,
  13. GXS.Context;
  14. type
  15. TgxCombinerCommand = record
  16. ActiveUnit: Integer;
  17. Arg1: Integer;
  18. Arg2: Integer;
  19. end;
  20. TgxCombinerCache = array of TgxCombinerCommand;
  21. EVXTextureCombinerError = class(Exception);
  22. (* Parses a TC text description and setups combiners accordingly.
  23. *experimental*
  24. Knowledge of texture combiners is a requirement
  25. Syntax: pascal-like, one instruction per line, use '//' for comment.
  26. Examples:
  27. Tex1:=Tex0; // replace texture 1 with texture 0
  28. Tex1:=Tex0+Tex1; // additive blending between textures 0 and 1
  29. Tex1:=Tex0-Tex1; // subtractive blending between textures 0 and 1
  30. Tex1:=Tex0*Tex1; // modulation between textures 0 and 1
  31. Tex1:=Tex0+Tex1-0.5; // signed additive blending between textures 0 and 1
  32. Tex1:=Interpolate(Tex0, Tex1, PrimaryColor); // interpolation between textures 0 and 1 using primary color as factor
  33. Tex1:=Dot3(Tex0, Tex1); // dot3 product between textures 0 and 1
  34. Accepted tokens:
  35. Tex0, Tex1, etc. : texture unit
  36. PrimaryColor, Col : the primary color
  37. ConstantColor, EnvCol : texture environment constant color
  38. Tokens can be qualified with '.a' or '.alpha' to specify the alpha channel
  39. explicitly, and '.rgb' to specify color channels (default). You cannot mix
  40. alpha and rgb tokens in the same line. *)
  41. function GetTextureCombiners(const tcCode: TStringList): TgxCombinerCache;
  42. // ------------------------------------------------------------------
  43. implementation
  44. // ------------------------------------------------------------------
  45. var
  46. vActiveUnit: Integer;
  47. vCommandCache: TgxCombinerCache;
  48. procedure TCAssertCheck(const b: Boolean; const errMsg: string);
  49. begin
  50. if not b then
  51. raise EVXTextureCombinerError.Create(errMsg);
  52. end;
  53. function RemoveSpaces(const str: string): string;
  54. var
  55. c: Char;
  56. i, p, n: Integer;
  57. begin
  58. n := Length(str);
  59. SetLength(Result, n);
  60. p := 1;
  61. for i := 1 to n do
  62. begin
  63. c := str[i];
  64. if c <> ' ' then
  65. begin
  66. Result[p] := c;
  67. Inc(p);
  68. end;
  69. end;
  70. SetLength(Result, p - 1);
  71. end;
  72. procedure ProcessTextureCombinerArgument(arg: string; sourceEnum, operandEnum: Integer;
  73. const dest: string);
  74. var
  75. sourceValue, operandValue, n, p: Integer;
  76. origArg, qualifier: string;
  77. cmd: TgxCombinerCommand;
  78. begin
  79. origArg := arg;
  80. p := Pos('.', arg);
  81. if p > 0 then
  82. begin
  83. qualifier := Copy(arg, p + 1, MaxInt);
  84. arg := Copy(arg, 1, p - 1);
  85. end
  86. else
  87. qualifier := 'rgb';
  88. if qualifier = 'rgb' then
  89. begin
  90. if Copy(arg, 1, 1) = '~' then
  91. begin
  92. operandValue := GL_ONE_MINUS_SRC_COLOR;
  93. arg := Copy(arg, 2, MaxInt);
  94. end
  95. else if Copy(arg, 1, 2) = '1-' then
  96. begin
  97. operandValue := GL_ONE_MINUS_SRC_COLOR;
  98. arg := Copy(arg, 3, MaxInt);
  99. end
  100. else
  101. operandValue := GL_SRC_COLOR;
  102. end
  103. else if Copy(qualifier, 1, 1) = 'a' then
  104. begin
  105. if Copy(arg, 1, 1) = '~' then
  106. begin
  107. operandValue := GL_ONE_MINUS_SRC_ALPHA;
  108. arg := Copy(arg, 2, MaxInt);
  109. end
  110. else if Copy(arg, 1, 2) = '1-' then
  111. begin
  112. operandValue := GL_ONE_MINUS_SRC_ALPHA;
  113. arg := Copy(arg, 3, MaxInt);
  114. end
  115. else
  116. operandValue := GL_SRC_ALPHA;
  117. end
  118. else
  119. operandValue := 0;
  120. sourceValue := 0;
  121. if (arg = 'tex') or (arg = dest) then
  122. sourceValue := GL_TEXTURE
  123. else if ((arg = 'tex0') and (dest = 'tex1')) or ((arg = 'tex1') and (dest = 'tex2'))
  124. or ((arg = 'tex2') and (dest = 'tex3')) then
  125. sourceValue := GL_PREVIOUS_ARB
  126. else if (arg = 'col') or (arg = 'col0') or (arg = 'primarycolor') then
  127. sourceValue := GL_PRIMARY_COLOR_ARB
  128. else if (arg = 'envcol') or (arg = 'constcol') or (arg = 'constantcolor') then
  129. sourceValue := GL_CONSTANT_COLOR_EXT
  130. else if Copy(arg, 1, 3) = 'tex' then
  131. begin
  132. TCAssertCheck(False{GL_ARB_texture_env_crossbar or GL_NV_texture_env_combine4},
  133. 'Requires GL_ARB_texture_env_crossbar or NV_texture_env_combine4');
  134. n := StrToIntDef(Copy(arg, 4, MaxInt), -1);
  135. if n in [0..7] then
  136. sourceValue := GL_TEXTURE0_ARB + n;
  137. end;
  138. TCAssertCheck((operandValue > 0) and (sourceValue > 0),
  139. 'invalid argument : "' + origArg + '"');
  140. SetLength(vCommandCache, Length(vCommandCache)+2);
  141. cmd.ActiveUnit := vActiveUnit;
  142. cmd.Arg1 := sourceEnum;
  143. cmd.Arg2 := sourceValue;
  144. vCommandCache[High(vCommandCache)-1] := cmd;
  145. cmd.ActiveUnit := vActiveUnit;
  146. cmd.Arg1 := operandEnum;
  147. cmd.Arg2 := operandValue;
  148. vCommandCache[High(vCommandCache)] := cmd;
  149. end;
  150. procedure ProcessTextureCombinerLine(const tcLine: string);
  151. var
  152. line, dest, arg1, arg2, arg3, funcname: string;
  153. p: Integer;
  154. destEnum, operEnum: Integer;
  155. sourceBaseEnum, operandBaseEnum: Integer;
  156. sl: TStrings;
  157. cmd: TgxCombinerCommand;
  158. begin
  159. // initial filtering
  160. line := LowerCase(RemoveSpaces(Trim(tcLine)));
  161. if Copy(line, 1, 2) = '//' then
  162. Exit;
  163. if line = '' then
  164. Exit;
  165. if line[Length(line)] = ';' then
  166. begin
  167. line := Trim(Copy(line, 1, Length(line) - 1));
  168. if line = '' then
  169. Exit;
  170. end;
  171. // Parse destination
  172. p := Pos(':=', line);
  173. dest := Copy(line, 1, p - 1);
  174. line := Copy(line, p + 2, MaxInt);
  175. p := Pos('.', dest);
  176. destEnum := GL_COMBINE_RGB_ARB;
  177. sourceBaseEnum := GL_SOURCE0_RGB_ARB;
  178. operandBaseEnum := GL_OPERAND0_RGB_ARB;
  179. if p > 0 then
  180. begin
  181. if Copy(dest, p + 1, 1) = 'a' then
  182. begin
  183. destEnum := GL_COMBINE_ALPHA_ARB;
  184. sourceBaseEnum := GL_SOURCE0_ALPHA_ARB;
  185. operandBaseEnum := GL_OPERAND0_ALPHA_ARB;
  186. end;
  187. dest := Copy(dest, 1, p - 1);
  188. end;
  189. if Copy(dest, 1, 3) = 'tex' then
  190. begin
  191. p := StrToIntDef(Copy(dest, 4, MaxInt), -1);
  192. TCAssertCheck(p >= 0, 'Invalid destination texture unit "' + dest + '"');
  193. vActiveUnit := p;
  194. end
  195. else
  196. TCAssertCheck(False, 'Invalid destination "' + dest + '"');
  197. // parse combiner operator
  198. operEnum := 0;
  199. arg1 := '';
  200. arg2 := '';
  201. arg3 := '';
  202. p := Pos('+', line);
  203. if p > 0 then
  204. begin
  205. // ADD & ADD_SIGNED operators
  206. if Copy(line, Length(line) - 3, 4) = '-0.5' then
  207. begin
  208. operEnum := GL_ADD_SIGNED_ARB;
  209. SetLength(line, Length(line) - 4);
  210. end
  211. else
  212. operEnum := GL_ADD;
  213. arg1 := Copy(line, 1, p - 1);
  214. arg2 := Copy(line, p + 1, MaxInt);
  215. end;
  216. p := Pos('*', line);
  217. if p > 0 then
  218. begin
  219. // MODULATE operator
  220. operEnum := GL_MODULATE;
  221. arg1 := Copy(line, 1, p - 1);
  222. arg2 := Copy(line, p + 1, MaxInt);
  223. line := '';
  224. end;
  225. p := Pos('(', line);
  226. if p > 0 then
  227. begin
  228. // function
  229. sl := TStringList.Create;
  230. try
  231. funcName := Copy(line, 1, p - 1);
  232. p := Pos('(', line);
  233. line := Copy(line, p + 1, MaxInt);
  234. p := Pos(')', line);
  235. sl.CommaText := Copy(line, 1, p - 1);
  236. if funcName = 'interpolate' then
  237. begin
  238. // INTERPOLATE operator
  239. TCAssertCheck(sl.Count = 3, 'Invalid parameter count');
  240. operEnum := GL_INTERPOLATE_ARB;
  241. arg1 := sl[0];
  242. arg2 := sl[1];
  243. arg3 := sl[2];
  244. end
  245. else if funcName = 'dot3' then
  246. begin
  247. // DOT3 operator
  248. TCAssertCheck(sl.Count = 2, 'Invalid parameter count');
  249. TCAssertCheck(False{GL_ARB_texture_env_dot3}, 'Requires GL_ARB_texture_env_dot3');
  250. operEnum := GL_DOT3_RGB_ARB;
  251. arg1 := sl[0];
  252. arg2 := sl[1];
  253. end
  254. else
  255. TCAssertCheck(False, 'Invalid function "' + funcName + '"');
  256. finally
  257. sl.Free;
  258. end;
  259. line := '';
  260. end;
  261. p := Pos('-', line);
  262. if p > 0 then
  263. begin
  264. // SUBTRACT operator
  265. operEnum := GL_SUBTRACT_ARB;
  266. arg1 := Copy(line, 1, p - 1);
  267. arg2 := Copy(line, p + 1, MaxInt);
  268. line := '';
  269. end;
  270. if operEnum = 0 then
  271. begin
  272. // REPLACE by default
  273. operEnum := GL_REPLACE;
  274. arg1 := line;
  275. end;
  276. cmd.ActiveUnit := vActiveUnit;
  277. cmd.Arg1 := destEnum;
  278. cmd.Arg2 := operEnum;
  279. SetLength(vCommandCache, Length(vCommandCache)+1);
  280. vCommandCache[High(vCommandCache)] := cmd;
  281. // parse arguments
  282. if arg1 <> '' then
  283. ProcessTextureCombinerArgument(arg1, sourceBaseEnum, operandBaseEnum, dest);
  284. if arg2 <> '' then
  285. ProcessTextureCombinerArgument(arg2, sourceBaseEnum + 1, operandBaseEnum + 1, dest);
  286. if arg3 <> '' then
  287. ProcessTextureCombinerArgument(arg3, sourceBaseEnum + 2, operandBaseEnum + 2, dest);
  288. end;
  289. function GetTextureCombiners(const tcCode: TStringList): TgxCombinerCache;
  290. var
  291. i: Integer;
  292. sl: TStringList;
  293. begin
  294. vCommandCache := nil;
  295. TCAssertCheck(False{GL_ARB_texture_env_combine}, 'Requires GL_ARB_texture_env_combine support');
  296. sl := TStringList.Create;
  297. try
  298. sl.Assign(tcCode);
  299. for i := 0 to sl.Count - 1 do
  300. ProcessTextureCombinerLine(sl[i]);
  301. finally
  302. sl.Free;
  303. end;
  304. Result := vCommandCache;
  305. end;
  306. end.