GLS.TextureCombiners.pas 8.8 KB

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