From b5bf529a2cd6b67bfb9da3f53f681139f820e8c8 Mon Sep 17 00:00:00 2001 From: Yaski Date: Fri, 6 Jul 2012 03:22:23 +0600 Subject: [PATCH] Procedure: complex operators for RPN expressions --- .../materials/compiler/CommandType.as | 68 ++++++++++- .../engine3d/materials/compiler/Procedure.as | 114 ++++++++++++------ 2 files changed, 140 insertions(+), 42 deletions(-) diff --git a/src/alternativa/engine3d/materials/compiler/CommandType.as b/src/alternativa/engine3d/materials/compiler/CommandType.as index 9f3253a..b82571c 100644 --- a/src/alternativa/engine3d/materials/compiler/CommandType.as +++ b/src/alternativa/engine3d/materials/compiler/CommandType.as @@ -7,8 +7,7 @@ * */ package alternativa.engine3d.materials.compiler { - - /** +/** * @private */ public class CommandType { @@ -51,7 +50,68 @@ package alternativa.engine3d.materials.compiler { ["mov", "add", "sub", "mul", "div", "rcp", "min", "max", "frc", "sqt", "rsq", "pow", "log", "exp", "nrm", "sin", "cos", "crs", "dp3", "dp4", "abs", "neg", "sat", "m33", "m44", "m34","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26", "kil", "tex", "sge", "slt", "2b", "seq", "sne"] ); - public function CommandType() { + + public static const commands:Object = formCommandsList(); + + private static function formCommandsList():Object { + // dp3( ,) +- */ + var result:Object = {}; + result[","] = new CommandType(0, 1); + result[")"] = new CommandType(0, 1); + result["+"] = new CommandType(2, 2); + result["-"] = new CommandType(2, 2); + result["*"] = new CommandType(2, 3); + result["/"] = new CommandType(2, 3); + result["mov"] = new CommandType(1); + result["add"] = new CommandType(); + result["sub"] = new CommandType(); + result["mul"] = new CommandType(); + result["div"] = new CommandType(); + result["rcp"] = new CommandType(1); + result["min"] = new CommandType(); + result["max"] = new CommandType(); + result["frc"] = new CommandType(1); + result["sqt"] = new CommandType(1); + result["rsq"] = new CommandType(1); + result["pow"] = new CommandType(); + result["log"] = new CommandType(1); + result["exp"] = new CommandType(1); + result["nrm"] = new CommandType(1); + result["sin"] = new CommandType(1); + result["cos"] = new CommandType(1); + result["crs"] = new CommandType(); + result["dp3"] = new CommandType(); + result["dp4"] = new CommandType(); + result["abs"] = new CommandType(1); + result["neg"] = new CommandType(1); + result["sat"] = new CommandType(1); + result["m33"] = new CommandType(); + result["m44"] = new CommandType(); + result["m34"] = new CommandType(); +// result["kil"] = new CommandType(); +// result["tex"] = new CommandType(); + result["sge"] = new CommandType(); + result["slt"] = new CommandType(); + result["seq"] = new CommandType(); + result["sne"] = new CommandType(); + // setting ids + for (var s:String in result) { + result[s].id = s; + } + result["+"].id = "add"; + result["-"].id = "sub"; + result["*"].id = "mul"; + result["/"].id = "div"; + return result; + } + + public var id:String; + public var priority:int; + public var numInputs:int; + + public function CommandType(numInputs:int = 2, priority:int = 0) { + this.numInputs = numInputs; + this.priority = priority; } } -} \ No newline at end of file +} diff --git a/src/alternativa/engine3d/materials/compiler/Procedure.as b/src/alternativa/engine3d/materials/compiler/Procedure.as index 3201ba7..03336d9 100644 --- a/src/alternativa/engine3d/materials/compiler/Procedure.as +++ b/src/alternativa/engine3d/materials/compiler/Procedure.as @@ -8,13 +8,13 @@ package alternativa.engine3d.materials.compiler { - import alternativa.engine3d.alternativa3d; +import alternativa.engine3d.alternativa3d; - import flash.display3D.Context3DProgramType; - import flash.utils.ByteArray; - import flash.utils.Endian; +import flash.display3D.Context3DProgramType; +import flash.utils.ByteArray; +import flash.utils.Endian; - use namespace alternativa3d; +use namespace alternativa3d; /** * @private @@ -188,7 +188,9 @@ package alternativa.engine3d.materials.compiler { // 7) swizzle handled like in AGAL compiler // TODO: handle swizzle smartly (.zw -> .zwzw) - // TODO: implement complex operators (dp3, nrm, sat) + // TODO: implement operators inputs size check + // TODO: implement operators auto output size + // TODO: implement operators output swizzle // TODO: minimize output temporaries count (sort operators by priority) // TODO: write to ByteArray directly // TODO: implement negate unary operator (-x) @@ -203,7 +205,7 @@ package alternativa.engine3d.materials.compiler { if (commentIndex >= 0) { source = source.substr(0, commentIndex); } - var operands:Array = source.match(/([activo]((\[.+\])|(\d+))(\.[xyzw]{1,4})?|[+\-*\/=])/g); + var operands:Array = source.match(/[a-z]+(((\[.+\])|(\d+))(\.[xyzw]{1,4})?)?|[+\-*\/=(),]/g); var numOperands:int = operands.length; if (numOperands < 3) return; if (operands[1] != "=") { @@ -226,19 +228,22 @@ package alternativa.engine3d.materials.compiler { var outputVar:String = (maskIndex >= 0) ? output.substr(0, maskIndex) : output; if (outputMaskLen == 4) output = outputVar; - var operators:Vector. = new Vector.(); + var operators:Vector. = new Vector.(); var variables:Vector. = new Vector.(); - function getPriority(command:String):int { - return ((command == "+" || command == "-") ? 1 : 2); + function getPriority(command:CommandType):int { + return command.priority; } function getSwizzleLen(value:String):uint { var i:int = value.lastIndexOf("."); return (i < 0 ? 4 : value.length - i - 1); } - function writeCommand(command:String, operandIndex:int, isLastOperator:Boolean):void { - var b:String = variables.pop(); + function writeCommand(command:CommandType, numInputs:int, operandIndex:int, isLastOperator:Boolean):void { + if (numInputs != command.numInputs) { + throw new Error("Syntax error. Operator " + command.id + " inputs count wrong. Expected " + command.numInputs + "."); + } + var b:String = (numInputs > 1) ? variables.pop() : null; var a:String = variables.pop(); - if (a == null || b == null) throw new Error("Syntax error. Variable expected after " + command + "."); + if (a == null || (numInputs > 1 && b == null)) throw new Error("Syntax error. Variable expected after " + command + "."); // Check can we use output for writing var i:int; for (i = 0; i < variables.length; i++) { @@ -253,12 +258,17 @@ package alternativa.engine3d.materials.compiler { throw new Error("Expression is too complex. Output used as source."); } } - var aSwizzleLen:uint = getSwizzleLen(a); - var bSwizzleLen:uint = getSwizzleLen(b); - if (aSwizzleLen != bSwizzleLen && aSwizzleLen != 1 && bSwizzleLen != 1) { - throw new Error("Variables size mistmatch " + a + " and " + b + "."); - } var maxSwizzle:uint = (aSwizzleLen > bSwizzleLen) ? aSwizzleLen : bSwizzleLen; + if (numInputs <= 1) { + maxSwizzle = getSwizzleLen(a); + } else { + var aSwizzleLen:uint = getSwizzleLen(a); + var bSwizzleLen:uint = getSwizzleLen(b); + if (aSwizzleLen != bSwizzleLen && aSwizzleLen != 1 && bSwizzleLen != 1) { + throw new Error("Variables size mistmatch " + a + " and " + b + "."); + } + maxSwizzle = (aSwizzleLen > bSwizzleLen) ? aSwizzleLen : bSwizzleLen; + } if (maxSwizzle > outputMaskLen || (isLastOperator && maxSwizzle != outputMaskLen && maxSwizzle != 1)) { throw new Error("Expression differs in size with output " + output + "."); } @@ -273,19 +283,10 @@ package alternativa.engine3d.materials.compiler { out = outputVar + ".xyz"; } } - switch (command) { - case "+": - writeAGALExpression("add " + out + " " + a + " " + b); - break; - case "-": - writeAGALExpression("sub " + out + " " + a + " " + b); - break; - case "*": - writeAGALExpression("mul " + out + " " + a + " " + b); - break; - case "/": - writeAGALExpression("div " + out + " " + a + " " + b); - break; + if (numInputs > 1) { + writeAGALExpression(command.id + " " + out + " " + a + " " + b); + } else { + writeAGALExpression(command.id + " " + out + " " + a); } variables.push(out); } @@ -297,6 +298,7 @@ package alternativa.engine3d.materials.compiler { } writeAGALExpression("mov " + output + " " + operand); } + var command:CommandType; var wasVariable:Boolean = false; for (i = 2; i < numOperands; i++) { operand = operands[i]; @@ -306,23 +308,59 @@ package alternativa.engine3d.materials.compiler { case "*": case "/": if (!wasVariable) throw new Error("Syntax error. Variable expected before " + operand + "."); + command = CommandType.commands[operand]; // process operators from stack while their priority is higher or equal - while (operators.length > 0 && getPriority(operators[operators.length - 1]) >= getPriority(operand)) { - writeCommand(operators.pop(), i, false); + while (operators.length > 0 && getPriority(operators[operators.length - 1]) >= getPriority(command)) { + writeCommand(operators.pop(), 2, i, false); } - operators.push(operand); + operators.push(command); wasVariable = false; break; + case ")": + case ",": + if (!wasVariable) throw new Error("Syntax error. Variable expected before " + operand + "."); + command = CommandType.commands[operand]; + // process all commands before until comma or left bracket + while (operators.length > 0 && getPriority(operators[operators.length - 1]) > getPriority(command)) { + writeCommand(operators.pop(), 2, i, false); + } + if (operand == ",") { + operators.push(command); + wasVariable = false; + } else { + // count all commas until function + var numParams:int = 1; + while ((command = operators.pop()) != null && command.priority != 0) { + numParams++; + } + writeCommand(command, numParams, i, i == numOperands - 1); + wasVariable = true; + } + break; default: if (wasVariable) throw new Error("Syntax error. Operator expected before " + operand + "."); - variables.push(operand); - wasVariable = true; + + command = CommandType.commands[operand]; + if (command != null) { + // is command + // test bracket + if (i + 1 >= numOperands || operands[i + 1] != "(") { + throw new Error("Syntax error. Expected bracket after " + operand + "."); + } + operators.push(command); + i++; // skip bracket +// wasVariable = false; + } else { + // is variable + variables.push(operand); + wasVariable = true; + } break; } } // process remained operators - while ((operand = operators.pop()) != null) { - writeCommand(operand, numOperands, operators.length == 0); + while ((command = operators.pop()) != null) { + writeCommand(command, 2, numOperands, operators.length == 0); } if (variables.length > 1) throw new Error("Syntax error. Unknown novel error."); }