6ffc988ae3
The expression module now uses an EXP prefix and it follows a distribution similar to blender. Additionally the hash function in EXP_HashedPtr.h was simplified and the files EXP_C-Api.h &.EXP_C-Api.cpp were deleted because were unused. Reviewers: campbellbarton, moguri, sybren, hg1 Projects: #game_engine Differential Revision: https://developer.blender.org/D1221
544 lines
16 KiB
C++
544 lines
16 KiB
C++
/*
|
|
* Set random/camera stuff
|
|
*
|
|
*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file gameengine/GameLogic/SCA_RandomActuator.cpp
|
|
* \ingroup gamelogic
|
|
*/
|
|
|
|
|
|
#include <stddef.h>
|
|
#include <math.h>
|
|
|
|
#include "EXP_BoolValue.h"
|
|
#include "EXP_IntValue.h"
|
|
#include "EXP_FloatValue.h"
|
|
#include "SCA_IActuator.h"
|
|
#include "SCA_RandomActuator.h"
|
|
#include "MT_Transform.h"
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Native functions */
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
SCA_RandomActuator::SCA_RandomActuator(SCA_IObject *gameobj,
|
|
long seed,
|
|
SCA_RandomActuator::KX_RANDOMACT_MODE mode,
|
|
float para1,
|
|
float para2,
|
|
const STR_String &propName)
|
|
: SCA_IActuator(gameobj, KX_ACT_RANDOM),
|
|
m_propname(propName),
|
|
m_parameter1(para1),
|
|
m_parameter2(para2),
|
|
m_distribution(mode)
|
|
{
|
|
m_base = new SCA_RandomNumberGenerator(seed);
|
|
m_counter = 0;
|
|
enforceConstraints();
|
|
}
|
|
|
|
|
|
|
|
SCA_RandomActuator::~SCA_RandomActuator()
|
|
{
|
|
m_base->Release();
|
|
}
|
|
|
|
|
|
|
|
CValue* SCA_RandomActuator::GetReplica()
|
|
{
|
|
SCA_RandomActuator* replica = new SCA_RandomActuator(*this);
|
|
// replication just copy the m_base pointer => common random generator
|
|
replica->ProcessReplica();
|
|
return replica;
|
|
}
|
|
|
|
void SCA_RandomActuator::ProcessReplica()
|
|
{
|
|
SCA_IActuator::ProcessReplica();
|
|
// increment reference count so that we can release the generator at the end
|
|
m_base->AddRef();
|
|
}
|
|
|
|
|
|
|
|
bool SCA_RandomActuator::Update()
|
|
{
|
|
//bool result = false; /*unused*/
|
|
bool bNegativeEvent = IsNegativeEvent();
|
|
|
|
RemoveAllEvents();
|
|
|
|
|
|
CValue *tmpval = NULL;
|
|
|
|
if (bNegativeEvent)
|
|
return false; // do nothing on negative events
|
|
|
|
switch (m_distribution) {
|
|
case KX_RANDOMACT_BOOL_CONST: {
|
|
/* un petit peu filthy */
|
|
bool res = !(m_parameter1 < 0.5);
|
|
tmpval = new CBoolValue(res);
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_BOOL_UNIFORM: {
|
|
/* flip a coin */
|
|
bool res;
|
|
if (m_counter > 31) {
|
|
m_previous = m_base->Draw();
|
|
res = ((m_previous & 0x1) == 0);
|
|
m_counter = 1;
|
|
} else {
|
|
res = (((m_previous >> m_counter) & 0x1) == 0);
|
|
m_counter++;
|
|
}
|
|
tmpval = new CBoolValue(res);
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_BOOL_BERNOUILLI: {
|
|
/* 'percentage' */
|
|
bool res;
|
|
res = (m_base->DrawFloat() < m_parameter1);
|
|
tmpval = new CBoolValue(res);
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_INT_CONST: {
|
|
/* constant */
|
|
tmpval = new CIntValue((int) floor(m_parameter1));
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_INT_UNIFORM: {
|
|
/* uniform (toss a die) */
|
|
int res;
|
|
/* The [0, 1] interval is projected onto the [min, max+1] domain, */
|
|
/* and then rounded. */
|
|
res = (int)floor( ((m_parameter2 - m_parameter1 + 1) * m_base->DrawFloat()) + m_parameter1);
|
|
tmpval = new CIntValue(res);
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_INT_POISSON: {
|
|
/* poisson (queues) */
|
|
/* If x_1, x_2, ... is a sequence of random numbers with uniform */
|
|
/* distribution between zero and one, k is the first integer for */
|
|
/* which the product x_1*x_2*...*x_k < exp(-\lamba). */
|
|
float a, b;
|
|
int res = 0;
|
|
/* The - sign is important here! The number to test for, a, must be */
|
|
/* between 0 and 1. */
|
|
a = exp(-m_parameter1);
|
|
/* a quickly reaches 0.... so we guard explicitly for that. */
|
|
if (a < FLT_MIN) a = FLT_MIN;
|
|
b = m_base->DrawFloat();
|
|
while (b >= a) {
|
|
b = b * m_base->DrawFloat();
|
|
res++;
|
|
};
|
|
tmpval = new CIntValue(res);
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_FLOAT_CONST: {
|
|
/* constant */
|
|
tmpval = new CFloatValue(m_parameter1);
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_FLOAT_UNIFORM: {
|
|
float res = ((m_parameter2 - m_parameter1) * m_base->DrawFloat()) + m_parameter1;
|
|
tmpval = new CFloatValue(res);
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_FLOAT_NORMAL: {
|
|
/* normal (big numbers): para1 = mean, para2 = std dev */
|
|
|
|
/* 070301 - nzc: Changed the termination condition. I think I
|
|
* made a small mistake here, but it only affects distro's where
|
|
* the seed equals 0. In that case, the algorithm locks. Let's
|
|
* just guard that case separately.
|
|
*/
|
|
|
|
float x = 0.0, y = 0.0, s = 0.0, t = 0.0;
|
|
if (m_base->GetSeed() == 0) {
|
|
/* 070301 - nzc: Just taking the mean here seems reasonable. */
|
|
tmpval = new CFloatValue(m_parameter1);
|
|
}
|
|
else {
|
|
/* 070301 - nzc
|
|
* Now, with seed != 0, we will most assuredly get some
|
|
* sensible values. The termination condition states two
|
|
* things:
|
|
* 1. s >= 0 is not allowed: to prevent the distro from
|
|
* getting a bias towards high values. This is a small
|
|
* correction, really, and might also be left out.
|
|
* 2. s == 0 is not allowed: to prevent a division by zero
|
|
* when renormalising the drawn value to the desired
|
|
* distribution shape. As a side effect, the distro will
|
|
* never yield the exact mean.
|
|
* I am not sure whether this is consistent, since the error
|
|
* cause by #2 is of the same magnitude as the one
|
|
* prevented by #1. The error introduced into the SD will be
|
|
* improved, though. By how much? Hard to say... If you like
|
|
* the maths, feel free to analyse. Be aware that this is
|
|
* one of the really old standard algorithms. I think the
|
|
* original came in Fortran, was translated to Pascal, and
|
|
* then someone came up with the C code. My guess it that
|
|
* this will be quite sufficient here.
|
|
*/
|
|
do {
|
|
x = 2.0f * m_base->DrawFloat() - 1.0f;
|
|
y = 2.0f * m_base->DrawFloat() - 1.0f;
|
|
s = x * x + y * y;
|
|
} while ((s >= 1.0f) || (s == 0.0f));
|
|
t = x * sqrtf((-2.0 * log(s)) / s);
|
|
tmpval = new CFloatValue(m_parameter1 + m_parameter2 * t);
|
|
}
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL: {
|
|
/* 1st order fall-off. I am very partial to using the half-life as */
|
|
/* controlling parameter. Using the 'normal' exponent is not very */
|
|
/* intuitive... */
|
|
/* tmpval = new CFloatValue( (1.0 / m_parameter1) */
|
|
tmpval = new CFloatValue((m_parameter1) * (-log(1.0 - m_base->DrawFloat())));
|
|
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
/* unknown distribution... */
|
|
static bool randomWarning = false;
|
|
if (!randomWarning) {
|
|
randomWarning = true;
|
|
std::cout << "RandomActuator '" << GetName() << "' has an unknown distribution." << std::endl;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Round up: assign it */
|
|
CValue *prop = GetParent()->GetProperty(m_propname);
|
|
if (prop) {
|
|
prop->SetValue(tmpval);
|
|
}
|
|
tmpval->Release();
|
|
|
|
return false;
|
|
}
|
|
|
|
void SCA_RandomActuator::enforceConstraints()
|
|
{
|
|
/* The constraints that are checked here are the ones fundamental to */
|
|
/* the various distributions. Limitations of the algorithms are checked */
|
|
/* elsewhere (or they should be... ). */
|
|
switch (m_distribution) {
|
|
case KX_RANDOMACT_BOOL_CONST:
|
|
case KX_RANDOMACT_BOOL_UNIFORM:
|
|
case KX_RANDOMACT_INT_CONST:
|
|
case KX_RANDOMACT_INT_UNIFORM:
|
|
case KX_RANDOMACT_FLOAT_UNIFORM:
|
|
case KX_RANDOMACT_FLOAT_CONST:
|
|
; /* Nothing to be done here. We allow uniform distro's to have */
|
|
/* 'funny' domains, i.e. max < min. This does not give problems. */
|
|
break;
|
|
case KX_RANDOMACT_BOOL_BERNOUILLI:
|
|
/* clamp to [0, 1] */
|
|
if (m_parameter1 < 0.0) {
|
|
m_parameter1 = 0.0;
|
|
} else if (m_parameter1 > 1.0) {
|
|
m_parameter1 = 1.0;
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_INT_POISSON:
|
|
/* non-negative */
|
|
if (m_parameter1 < 0.0) {
|
|
m_parameter1 = 0.0;
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_FLOAT_NORMAL:
|
|
/* standard dev. is non-negative */
|
|
if (m_parameter2 < 0.0) {
|
|
m_parameter2 = 0.0;
|
|
}
|
|
break;
|
|
case KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL:
|
|
/* halflife must be non-negative */
|
|
if (m_parameter1 < 0.0) {
|
|
m_parameter1 = 0.0;
|
|
}
|
|
break;
|
|
default:
|
|
; /* unknown distribution... */
|
|
}
|
|
}
|
|
|
|
#ifdef WITH_PYTHON
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Python functions */
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Integration hooks ------------------------------------------------------- */
|
|
PyTypeObject SCA_RandomActuator::Type = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"SCA_RandomActuator",
|
|
sizeof(PyObjectPlus_Proxy),
|
|
0,
|
|
py_base_dealloc,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
py_base_repr,
|
|
0,0,0,0,0,0,0,0,0,
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
0,0,0,0,0,0,0,
|
|
Methods,
|
|
0,
|
|
0,
|
|
&SCA_IActuator::Type,
|
|
0,0,0,0,0,0,
|
|
py_base_new
|
|
};
|
|
|
|
PyMethodDef SCA_RandomActuator::Methods[] = {
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setBoolConst),
|
|
KX_PYMETHODTABLE_NOARGS(SCA_RandomActuator, setBoolUniform),
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setBoolBernouilli),
|
|
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setIntConst),
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setIntUniform),
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setIntPoisson),
|
|
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setFloatConst),
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setFloatUniform),
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setFloatNormal),
|
|
KX_PYMETHODTABLE(SCA_RandomActuator, setFloatNegativeExponential),
|
|
{NULL,NULL} //Sentinel
|
|
};
|
|
|
|
PyAttributeDef SCA_RandomActuator::Attributes[] = {
|
|
KX_PYATTRIBUTE_FLOAT_RO("para1",SCA_RandomActuator,m_parameter1),
|
|
KX_PYATTRIBUTE_FLOAT_RO("para2",SCA_RandomActuator,m_parameter2),
|
|
KX_PYATTRIBUTE_ENUM_RO("distribution",SCA_RandomActuator,m_distribution),
|
|
KX_PYATTRIBUTE_STRING_RW_CHECK("propName",0,MAX_PROP_NAME,false,SCA_RandomActuator,m_propname,CheckProperty),
|
|
KX_PYATTRIBUTE_RW_FUNCTION("seed",SCA_RandomActuator,pyattr_get_seed,pyattr_set_seed),
|
|
{ NULL } //Sentinel
|
|
};
|
|
|
|
PyObject *SCA_RandomActuator::pyattr_get_seed(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
|
|
{
|
|
SCA_RandomActuator* act = static_cast<SCA_RandomActuator*>(self);
|
|
return PyLong_FromLong(act->m_base->GetSeed());
|
|
}
|
|
|
|
int SCA_RandomActuator::pyattr_set_seed(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
|
|
{
|
|
SCA_RandomActuator* act = static_cast<SCA_RandomActuator*>(self);
|
|
if (PyLong_Check(value)) {
|
|
act->m_base->SetSeed(PyLong_AsLong(value));
|
|
return PY_SET_ATTR_SUCCESS;
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_TypeError, "actuator.seed = int: Random Actuator, expected an integer");
|
|
return PY_SET_ATTR_FAIL;
|
|
}
|
|
}
|
|
|
|
/* 11. setBoolConst */
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setBoolConst,
|
|
"setBoolConst(value)\n"
|
|
"\t- value: 0 or 1\n"
|
|
"\tSet this generator to produce a constant boolean value.\n")
|
|
{
|
|
int paraArg;
|
|
if (!PyArg_ParseTuple(args, "i:setBoolConst", ¶Arg)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_BOOL_CONST;
|
|
m_parameter1 = (paraArg) ? 1.0 : 0.0;
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 12. setBoolUniform, */
|
|
KX_PYMETHODDEF_DOC_NOARGS(SCA_RandomActuator, setBoolUniform,
|
|
"setBoolUniform()\n"
|
|
"\tSet this generator to produce true and false, each with 50%% chance of occurring\n")
|
|
{
|
|
/* no args */
|
|
m_distribution = KX_RANDOMACT_BOOL_UNIFORM;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 13. setBoolBernouilli, */
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setBoolBernouilli,
|
|
"setBoolBernouilli(value)\n"
|
|
"\t- value: a float between 0 and 1\n"
|
|
"\tReturn false value * 100%% of the time.\n")
|
|
{
|
|
float paraArg;
|
|
if (!PyArg_ParseTuple(args, "f:setBoolBernouilli", ¶Arg)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_BOOL_BERNOUILLI;
|
|
m_parameter1 = paraArg;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 14. setIntConst,*/
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntConst,
|
|
"setIntConst(value)\n"
|
|
"\t- value: integer\n"
|
|
"\tAlways return value\n")
|
|
{
|
|
int paraArg;
|
|
if (!PyArg_ParseTuple(args, "i:setIntConst", ¶Arg)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_INT_CONST;
|
|
m_parameter1 = paraArg;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 15. setIntUniform,*/
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntUniform,
|
|
"setIntUniform(lower_bound, upper_bound)\n"
|
|
"\t- lower_bound: integer\n"
|
|
"\t- upper_bound: integer\n"
|
|
"\tReturn a random integer between lower_bound and\n"
|
|
"\tupper_bound. The boundaries are included.\n")
|
|
{
|
|
int paraArg1, paraArg2;
|
|
if (!PyArg_ParseTuple(args, "ii:setIntUniform", ¶Arg1, ¶Arg2)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_INT_UNIFORM;
|
|
m_parameter1 = paraArg1;
|
|
m_parameter2 = paraArg2;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 16. setIntPoisson, */
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntPoisson,
|
|
"setIntPoisson(value)\n"
|
|
"\t- value: float\n"
|
|
"\tReturn a Poisson-distributed number. This performs a series\n"
|
|
"\tof Bernouilli tests with parameter value. It returns the\n"
|
|
"\tnumber of tries needed to achieve succes.\n")
|
|
{
|
|
float paraArg;
|
|
if (!PyArg_ParseTuple(args, "f:setIntPoisson", ¶Arg)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_INT_POISSON;
|
|
m_parameter1 = paraArg;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 17. setFloatConst */
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatConst,
|
|
"setFloatConst(value)\n"
|
|
"\t- value: float\n"
|
|
"\tAlways return value\n")
|
|
{
|
|
float paraArg;
|
|
if (!PyArg_ParseTuple(args, "f:setFloatConst", ¶Arg)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_FLOAT_CONST;
|
|
m_parameter1 = paraArg;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 18. setFloatUniform, */
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatUniform,
|
|
"setFloatUniform(lower_bound, upper_bound)\n"
|
|
"\t- lower_bound: float\n"
|
|
"\t- upper_bound: float\n"
|
|
"\tReturn a random integer between lower_bound and\n"
|
|
"\tupper_bound.\n")
|
|
{
|
|
float paraArg1, paraArg2;
|
|
if (!PyArg_ParseTuple(args, "ff:setFloatUniform", ¶Arg1, ¶Arg2)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_FLOAT_UNIFORM;
|
|
m_parameter1 = paraArg1;
|
|
m_parameter2 = paraArg2;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 19. setFloatNormal, */
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatNormal,
|
|
"setFloatNormal(mean, standard_deviation)\n"
|
|
"\t- mean: float\n"
|
|
"\t- standard_deviation: float\n"
|
|
"\tReturn normal-distributed numbers. The average is mean, and the\n"
|
|
"\tdeviation from the mean is characterized by standard_deviation.\n")
|
|
{
|
|
float paraArg1, paraArg2;
|
|
if (!PyArg_ParseTuple(args, "ff:setFloatNormal", ¶Arg1, ¶Arg2)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_FLOAT_NORMAL;
|
|
m_parameter1 = paraArg1;
|
|
m_parameter2 = paraArg2;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
/* 20. setFloatNegativeExponential, */
|
|
KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatNegativeExponential,
|
|
"setFloatNegativeExponential(half_life)\n"
|
|
"\t- half_life: float\n"
|
|
"\tReturn negative-exponentially distributed numbers. The half-life 'time'\n"
|
|
"\tis characterized by half_life.\n")
|
|
{
|
|
float paraArg;
|
|
if (!PyArg_ParseTuple(args, "f:setFloatNegativeExponential", ¶Arg)) {
|
|
return NULL;
|
|
}
|
|
|
|
m_distribution = KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL;
|
|
m_parameter1 = paraArg;
|
|
enforceConstraints();
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* eof */
|