2 fegdk: FE Game Development Kit
3 Copyright (C) 2001-2008 Alexey "waker" Yakovenko
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 waker@users.sourceforge.net
25 #include "f_mathexpression.h"
28 #include "f_parmmgr.h"
29 #include "f_helpers.h"
34 #include "f_console.h"
40 // TODO: implement table lookups
46 op_table1d dst, src0, src1
47 dst -- destination register
48 src0 -- pointer to a table of floats
49 src1.x -- index into table
58 script syntax example:
63 // TODO: intrinsic sin/cos (purposes???)
64 // TODO: 2d/3d/4d lookups (purposes???)
79 char nconsts; // number of constants
81 float constants[nconsts]; // array of constants
83 char ncmds; // number of commands
86 ((uchar)operator_t) op;
90 } commands[ncmds]; // array of commands
93 bits 0..13 - index of parameter (can be constant, register or variable)
94 bit 14..15 - 00 - constant; 01 - register; 10 - variable; 11 - reserved
98 app-defined variables are accessed through parmManager
99 the way it works should be as follows:
101 float *fval = engine->varManager ()->getFloat4ByName ("name");
105 ushort index = engine->varManager ()->getParmIndexByName ("name");
106 float *fval = engine->varManager ()->getFloat4ByIndex (index);
108 parm manager should (?) provide overloads for type-casting. e.g.:
110 float* getFloat4ByIndex (ushort index);
111 float4* getFloatByIndex (ushort index);
112 float4* getFloat3ByIndex (ushort index);
118 registers are shared between programs
119 registers are not guaranteed to keep meaningfull values between programs
122 void mathExpression::getwords (const cStr
&in
, std::vector
< cStr
> &expression
)
124 expression
.resize (0);
126 const char *s
= in
.c_str ();
143 expression
.push_back (ss
);
170 expression
.push_back (ss
);
175 bool mathExpression::validate (const std::vector
<cStr
> &expr
)
177 // +001: no imparity of brackets, e.g. "(()"
178 // +002: no double-operator cases, e.g. "a+*b"
179 // +003: no double-value cases, e.g. "a b"
180 // 004: no operator-only branches, e.g. "-"
181 // +005: no empty branches, e.g. "()"
182 // 006: no branches starting with operators other than "+" or "-"
190 for (i
= 0; i
< expr
.size (); i
++)
198 else if (expr
[i
] == ")")
205 else if (expr
[i
] == "*"
211 if (i
< expr
.size () - 1)
224 else if (expr
[i
] == "[")
230 else if (expr
[i
] == "]")
239 if (i
< expr
.size () - 1)
263 if (expr
.size () == 1
274 if (bcnt
== (int)expr
.size ())
277 if (sbcnt
== (int)expr
.size ())
283 cStr
mathExpression::opstring (int op
)
285 cStr s
[] = { "", "+", "-", "*", "/" };
289 mathExpression::operator_t
mathExpression::getoperator (const std::vector
<cStr
> &expression
, size_t curword
)
291 cStr s
= expression
[curword
];
305 bool mathExpression::iscomplex (const cStr
&s
)
312 mathExpression::node
* mathExpression::buildtree (std::vector
<cStr
> &expr
)
315 while (expr
[0] == "(" && expr
.back () == ")")
317 // handle patalogical cases like "((3.2)+ (1))"
320 for (i
= 1; i
< expr
.size (); i
++)
324 else if (expr
[i
] == ")")
326 if (cnt
== 0 && i
!= expr
.size () - 1)
330 if (i
== expr
.size ())
332 expr
.erase (expr
.begin ());
339 // handle table lookups
340 // this check ensures that we don't have a case like "a[x] + b[y]"
342 && getoperator (expr
, 0) == op_no
344 && expr
.back () == "]")
347 for (size_t i
= 2; i
< expr
.size (); i
++)
350 && i
!= expr
.size () - 1)
361 l
.push_back (expr
[0]);
362 for (size_t i
= 2; i
< expr
.size () - 1; i
++)
364 r
.push_back (expr
[i
]);
368 n
->left
= buildtree (l
);
369 n
->right
= buildtree (r
);
374 // handle expressions started w/ "+" or "-" signs
376 expr
.erase (expr
.begin ());
377 else if (expr
[0] == "-")
379 expr
.erase (expr
.begin ());
380 expr
[0] = "-" + expr
[0];
383 // handle single value
384 if (expr
.size () == 1)
388 // n->value = (float)atof (expr[0]);
389 n
->strvalue
= expr
[0];
393 // find lowest-priority op
394 // FIXME: should select op nearest to a center of expression. dunno if it matters..
395 size_t index
= (unsigned)-1;
396 operator_t minop
= op_max
;
398 for (i
= 0; i
< expr
.size (); i
++)
405 for (k
= i
+1; k
< expr
.size (); k
++)
413 else if (expr
[k
] == "(")
425 for (k
= i
+1; k
< expr
.size (); k
++)
433 else if (expr
[k
] == "[")
440 operator_t op
= getoperator (expr
, i
);
458 std::vector
<cStr
> leftexpr
, rightexpr
;
459 for (i
= 0; i
< expr
.size (); i
++)
463 leftexpr
.push_back (expr
[i
]);
470 rightexpr
.push_back (expr
[i
]);
479 if (!leftexpr
.empty ())
480 n
->left
= buildtree (leftexpr
);
481 if (!rightexpr
.empty ())
482 n
->right
= buildtree (rightexpr
);
487 cStr
mathExpression::strop (operator_t op
) const
489 cStr s
[] = { "", "add", "sub", "mul", "div", "dot" };
493 bool mathExpression::isconst (const char *s
)
495 if (*s
== '-' || *s
== '+')
499 if (!isdigit (*s
) && *s
!= '.')
507 int mathExpression::getparamidx (const char *n
) const
509 int parmIdx
= g_engine
->getParmMgr ()->getParmIndexByName (n
);
510 if (parmIdx
>= (1<<13))
511 Con_Printf ("param index is too big: %s(%d)\n", n
, parmIdx
);
514 parmIdx
= g_engine
->getCVarManager ()->getCVarIdx (n
);
516 Con_Printf ("cvar not found: %s\n", n
);
517 if (parmIdx
>= (1<<13))
518 Con_Printf ("param index is too big: %s(%d)\n", n
, parmIdx
);
521 return parmIdx
| varmask
;
524 float* mathExpression::getvarvalueptr (int v
) const
526 int i
= v
&~ parmtypemask
;
528 return &g_engine
->getCVarManager ()->cvarForIdx (i
&~ (1<<13))->fvalue
;
529 return g_engine
->getParmMgr ()->getFloat (i
);
532 void mathExpression::traverse (node
*n
, std::vector
< mathExpression::command_t
> &commands
, std::vector
< float > &constants
, int reg
)
534 assert (reg
< maxregisters
);
535 if (n
->root
&& n
->leaf
)
543 if (isconst (n
->strvalue
))
545 c
.src0
= (ushort
)constants
.size ();
547 constants
.push_back ((float)atof (n
->strvalue
));
551 c
.src0
= getparamidx (n
->strvalue
);
554 // printf ("mov r%d, %f\n", reg, n->value);
556 commands
.push_back (c
);
563 traverse (n
->left
, commands
, constants
, reg
);
565 traverse (n
->right
, commands
, constants
, n
->left
->leaf
? reg
: reg
+1);
566 assert (!n
->leaf
); // do nothing to leaf nodes, they already have correct values
567 // n->strvalue.printf ("r%d", reg);
574 if (!n
->left
->leaf
) // reg
576 c
.src0
= n
->left
->reg
| regmask
;
578 else if (isconst (n
->left
->strvalue
))
580 c
.src0
= (ushort
)constants
.size ();
582 constants
.push_back ((float)atof (n
->left
->strvalue
));
586 c
.src0
= getparamidx (n
->left
->strvalue
);
589 if (!n
->right
->leaf
) // reg
591 c
.src1
= n
->right
->reg
| regmask
;
593 else if (isconst (n
->right
->strvalue
))
595 c
.src1
= (ushort
)constants
.size ();
597 constants
.push_back ((float)atof (n
->right
->strvalue
));
601 c
.src1
= getparamidx (n
->right
->strvalue
);
604 commands
.push_back (c
);
606 // printf ("%s r%d, %s, %s\n", strop (n->op).c_str (), reg, n->left->strvalue.c_str (), n->right->strvalue.c_str ());
611 void mathExpression::printProgram (const std::vector
< mathExpression::command_t
> &commands
, const std::vector
< float > &constants
) const
614 for (size_t i
= 0; i
< commands
.size (); i
++)
616 command_t c
= commands
[i
];
621 printf ("mov r%d, ", c
.dst
);
622 if (constmask
== (c
.src0
& parmtypemask
))
623 printf ("%f\n", constants
[c
.src0
&~ parmtypemask
]);
625 printf ("%f\n", engine
->parmMgr ()->getFloat (c
.src0
&~ parmtypemask
));
632 printf ("%s r%d, ", strop ((operator_t
)c
.opcode
).c_str (), c
.dst
);
634 if (constmask
== (c
.src0
& parmtypemask
))
635 printf ("%f, ", constants
[c
.src0
&~ parmtypemask
]);
636 else if (varmask
== (c
.src0
& parmtypemask
))
637 printf ("%f, ", getvarvalue (c
.src0
));
638 else if (regmask
== (c
.src0
& parmtypemask
))
639 printf ("r%d, ", c
.src0
&~ parmtypemask
);
641 if (constmask
== (c
.src1
& parmtypemask
))
642 printf ("%f\n", constants
[c
.src1
&~ parmtypemask
]);
643 else if (varmask
== (c
.src1
& parmtypemask
))
644 printf ("%f\n", getvarvalue (c
.src1
));
645 else if (regmask
== (c
.src1
& parmtypemask
))
646 printf ("r%d\n", c
.src1
&~ parmtypemask
);
653 void mathExpression::deltree (node
*n
)
662 mathExpression::mathExpression (void)
666 mathExpression::mathExpression (const char *expr
)
669 std::vector
< cStr
> expression
;
670 getwords (expr
, expression
);
671 bool valid
= validate (expression
);
673 sys_error ("error in expression \"%s\".\n", expr
);
675 node
*n
= buildtree (expression
);
678 sys_error ("failed to build binary tree for expression \"%s\".\n", expr
);
681 traverse (n
, mCommands
, mConstants
);
685 printProgram (mCommands
, mConstants
);
689 mathExpression::~mathExpression (void)
693 float mathExpression::evaluate (void)
695 float registers
[maxregisters
];
698 for (size_t i
= 0; i
< mCommands
.size (); i
++)
700 command_t c
= mCommands
[i
];
705 if (constmask
== (c
.src0
& parmtypemask
))
706 registers
[c
.dst
] = mConstants
[c
.src0
&~ parmtypemask
];
708 registers
[c
.dst
] = *g_engine
->getParmMgr ()->getFloat (c
.src0
&~ parmtypemask
);
711 if (constmask
== (c
.src0
& parmtypemask
))
712 p1
= &mConstants
[c
.src0
&~ parmtypemask
];
713 else if (varmask
== (c
.src0
& parmtypemask
))
714 p1
= getvarvalueptr (c
.src0
);
715 else if (regmask
== (c
.src0
& parmtypemask
))
716 p1
= ®isters
[c
.src0
&~ parmtypemask
];
718 sys_error ("[mathExpression::evaluate] something wrong with mathexpression: '%s'", mName
.c_str ());
720 if (constmask
== (c
.src1
& parmtypemask
))
721 p2
= &mConstants
[c
.src1
&~ parmtypemask
];
722 else if (varmask
== (c
.src1
& parmtypemask
))
723 p2
= getvarvalueptr (c
.src1
);
724 else if (regmask
== (c
.src1
& parmtypemask
))
725 p2
= ®isters
[c
.src1
&~ parmtypemask
];
727 sys_error ("[mathExpression::evaluate] something wrong with mathexpression: '%s'", mName
.c_str ());
733 int low
= (int)floor (*p2
);
734 int hi
= (int)ceil (*p2
);
735 float frac
= *p2
- low
;
737 int sz
= g_engine
->getParmMgr ()->getParmSize (c
.src0
&~ parmtypemask
);
744 registers
[c
.dst
] = l
* (1-frac
) + r
* (frac
);
748 registers
[c
.dst
] = (*p1
) * (*p2
);
751 registers
[c
.dst
] = (*p1
) / (*p2
);
754 registers
[c
.dst
] = (*p1
) + (*p2
);
757 registers
[c
.dst
] = (*p1
) - (*p2
);
762 // d = p1->x * p2->x + p1->y * p2->y + p1->z * p2->z + p1->w * p2->w;
763 // registers[c.dst].x = registers[c.dst].y = registers[c.dst].z = registers[c.dst].w = d;
764 registers
[c
.dst
] = 1;