/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
//
// LayerSlider
//
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
//#include "precomp.h"
#include "CKAll.h"
CKObjectDeclaration *FillBehaviorLayerSliderDecl();
CKERROR CreateLayerSliderProto(CKBehaviorPrototype **);
int LayerSlider(const CKBehaviorContext& behcontext);
CKERROR LayerSliderCallBack(const CKBehaviorContext& behcontext); // CallBack Function
inline BOOL A_Calculate_Ellipse_Vector( float x, float y, float R1, float R2, float *X, float *Y );
// Inner struct
typedef struct {
VxVector Old_Pos;
} A_LS;
//---
CKObjectDeclaration *FillBehaviorLayerSliderDecl()
{
CKObjectDeclaration *od = CreateCKObjectDeclaration("Layer Slider");
od->SetDescription("Impede the 3d object to enter into no-null squares of a specific layers.");
/* rem:
On : Activates the building block (it will be called every frames).
Off : Deactivate the building block.
Contact : activate each time there's a contact.
No Contact : activate each time there's no contact.
Influence Radius : amount of the object bounding sphere radius that will be taken into concideration to impede obstacles penetration.
( in most case 30% or 40% for a character would be fine )
A new functionnality concerning the camera has been added : the influence radius is taken into concideration for the cameras.
Layer To Slide On : layer type to slide on.
Reaction Vector : (Parameter added by settings) 3d vector response (beware, this vector isn't normalized).
Reaction Vector : boolean value to be check if you want the reaction vector to be output as a parameter.
Output Contact Count : defines the number of output parameters to be added, so you can get all the contact points between the object (modelized as a cylinder) and the non-nul squares of the specified layers.
Accuracy : maximum accuracy for object collision (higher values for bad FPS, or if the velocity is too high).
You can specify has many layer has you want.
*/
/* warning:
- 'Layer Slider' doesn't take the Grid's Priority into consideration. All the grids are taken into consideration.
- 'Layer Slider' needs accurate environnement;
1 - the object proportional diameter must be inferior to the width and length of a grid square.
2 - the object should not move with a too great velocity (ie : greater than it's radius).
3 - if you have a bad frame rate and do not want your object to pass trough walls when frame rate is too low, you should increment the Accurate setting.
- for cameras, try to put a high influence radius : as the near clip plane is quite a small value, the resulting velocity with regard to the camera radius is often very high.
*/
od->SetType( CKDLL_BEHAVIORPROTOTYPE );
od->SetCategory("Grids/Basic");
od->SetGuid(CKGUID(0x7bac6da2,0x1cbe76ed));
od->SetAuthorGuid(VIRTOOLS_GUID);
od->SetAuthorName("Virtools");
od->SetVersion(0x00020000);
od->SetCreationFunction(CreateLayerSliderProto);
od->SetCompatibleClassId(CKCID_3DENTITY);
return od;
}
CKERROR CreateLayerSliderProto(CKBehaviorPrototype **pproto)
{
CKBehaviorPrototype *proto = CreateCKBehaviorPrototype("Layer Slider");
if(!proto) return CKERR_OUTOFMEMORY;
proto->DeclareInput("On");
proto->DeclareInput("Off");
proto->DeclareOutput("Contact");
proto->DeclareOutput("No Contact");
proto->DeclareInParameter( "Influence Radius", CKPGUID_PERCENTAGE, "50" );
proto->DeclareInParameter( "Layer To Slide On", CKPGUID_LAYERTYPE );
proto->DeclareSetting( "Reaction Vector", CKPGUID_BOOL, "FALSE");
proto->DeclareSetting( "Output Contact Count", CKPGUID_INT, "0");
proto->DeclareLocalParameter(NULL, CKPGUID_VOIDBUF ); // inner struct LS
proto->DeclareSetting("Accuracy", CKPGUID_INT, "0");
proto->SetFlags(CK_BEHAVIORPROTOTYPE_NORMAL);
proto->SetBehaviorFlags((CK_BEHAVIOR_FLAGS)(CKBEHAVIOR_VARIABLEPARAMETERINPUTS|CKBEHAVIOR_TARGETABLE|CKBEHAVIOR_INTERNALLYCREATEDINPUTPARAMS));
proto->SetFunction(LayerSlider);
proto->SetBehaviorCallbackFct( LayerSliderCallBack );
*pproto = proto;
return CK_OK;
}
int LayerSlider(const CKBehaviorContext& behcontext)
{
CKBehavior* beh = behcontext.Behavior;
CKContext* ctx = behcontext.Context;
CK3dEntity *obj = (CK3dEntity*)beh->GetTarget();
if( !obj ) return CKBR_OWNERERROR;
A_LS *ls = NULL; // get local param
beh->GetLocalParameterValue(2, &ls);
VxVector init_pos;
obj->GetPosition( &init_pos );
//__________ OFF
if( beh->IsInputActive(1) ){
beh->ActivateInput(1,FALSE);
return CKBR_OK;
}
//__________ IN
if( beh->IsInputActive(0) ){
beh->ActivateInput(0, FALSE);
ls->Old_Pos = init_pos;
}
beh->ActivateInput(0, FALSE);
CKGridManager *gm = (CKGridManager *) ctx->GetManagerByGuid(GRID_MANAGER_GUID);
const XObjectPointerArray& grids = gm->GetGridArray();
if (!grids.Size()){
beh->ActivateOutput(1, TRUE);
return CKBR_OK;
}
int contact_count=0; // get setting : Contact Count
beh->GetLocalParameterValue(1, &contact_count);
int index_contact = 0;
CKBOOL reaction = TRUE; // get setting : Reaction Vector ?
if( !contact_count ){
reaction = FALSE;
beh->GetLocalParameterValue(0, &reaction);
}
float influence = 0.5f; // get the influence radius
beh->GetInputParameterValue(0, &influence );
CK_CLASSID classid = obj->GetClassID();
if( (classid == CKCID_CAMERA) || (classid == CKCID_TARGETCAMERA) ){ // if obj is camera then radius = f( NearClipPlane, Fov )
float fov = ((CKCamera*)obj)->GetFov();
float d = ((CKCamera*)obj)->GetFrontPlane();
influence = d * acosf(fov*0.5f) * 1.2f * influence;
if( influence<0.5f ) influence = 0.5f;
} else if( (classid!=CKCID_SPRITE3D) && (classid!=CKCID_3DENTITY) ){
influence *= obj->GetRadius();
}
int w, l, x, y;
int v1,v3,v5,v7,vC;
float p1,p3,p5,p7;
float tmp_minx, tmp_maxx, tmp_minz, tmp_maxz;
CKGrid *grid;
CKLayer *layer;
VxVector pos, pos_current, is; // pos, influence_radius_scaled
VxVector t(0,0,0), total;
int layer_type, input_index, input_total = beh->GetInputParameterCount();
BOOL contact=FALSE, posallreadycalc, scaleYallreadycalc, scaleXallreadycalc, scaleZallreadycalc;
int precis = 0;
beh->GetLocalParameterValue(3, &precis);
if( precis < 0 ) precis = 0;
int precis_tmp = precis;
VxVector obj_step;
if( precis ){
precis_tmp = (int) (Magnitude( init_pos - ls->Old_Pos ) / (influence-0.1f));
if( precis_tmp > precis ) precis_tmp = precis;
obj_step = ( init_pos - ls->Old_Pos ) / (precis_tmp + 1.0f);
pos_current = ls->Old_Pos;
} else {
pos_current = init_pos;
}
do{
if( precis ){
pos_current += obj_step;
obj->SetPosition( &pos_current );
}
for (CKObject** o = grids.Begin() ; o != grids.End() ; ++o ){ // For all grids in Scene
grid = (CKGrid*)*o;
if( grid->IsActive() ){
posallreadycalc = FALSE;
scaleYallreadycalc = FALSE;
scaleXallreadycalc = FALSE;
scaleZallreadycalc = FALSE;
for( input_index=1 ; input_indexGetInputParameterValue(input_index, &layer_type );
layer = grid->GetLayer( layer_type );
if( layer ){
if( !posallreadycalc ){ // do not calc pos if it has been yet for prev layer
grid->InverseTransform( &pos, &pos_current );
posallreadycalc = TRUE;
}
// get grid's world scale
CKBOOL hasparent;
const VxMatrix& WMat = grid->GetWorldMatrix();
if( !scaleYallreadycalc ){
hasparent = (grid->GetParent() != NULL);
if( hasparent ){
is.y=Magnitude(WMat[1]);
} else {
grid->GetScale( &is );
}
is.y = influence / is.y;
scaleYallreadycalc = TRUE;
}
if( (pos.y>=-is.y) && (pos.y<1.0f+is.y) ){
w = grid->GetWidth();
if( !scaleXallreadycalc ){
if( hasparent ) is.x = Magnitude(WMat[0]);
is.x = influence / is.x;
scaleXallreadycalc = TRUE;
}
tmp_minx=pos.x-is.x;
tmp_maxx=pos.x+is.x;
if( (tmp_minx=0.0f) ){
l = grid->GetLength();
if( !scaleZallreadycalc ){
if( hasparent ) is.z = Magnitude(WMat[2]);
is.z = influence / is.z;
scaleZallreadycalc = TRUE;
}
tmp_minz=pos.z-is.z;
tmp_maxz=pos.z+is.z;
if( (tmp_minz=0.0f) ){
x = (pos.x>=0.0f)? (int)pos.x:(int)(pos.x-1.0f);
y = (pos.z>=0.0f)? (int)pos.z:(int)(pos.z-1.0f);
/*
:---:---:---: o = the square containing the sphere
| 0 | 1 | 2 |
:---:---:---:
| 7 | o | 3 |
:---:---:---:
| 6 | 5 | 4 |
:---:---:---:
*/
p1=p3=p5=p7=1.0f; // penetration in column and raw (negative = penetration)
if( tmp_minx>=0.0f ) p7 = tmp_minx-x;
if( tmp_maxx=0.0f ) p5 = tmp_minz-y;
if( tmp_maxz=0) && (yGetValue(x-1, y, &v7);
if( v7 ) t.x = -p7;
else pflag |= 0x0001;
} else pflag |= 0x0001;
} else if( p3<=0.0f ){
if( (y>=0) && (yGetValue(x+1, y, &v3);
if( v3 ) t.x = p3;
else pflag |= 0x0010;
} else pflag |= 0x0010;
}
if( p1<=0.0f ){ // penetration on raw
if( (x>=0) && (xGetValue(x, y+1, &v1);
if( v1 ) t.z = p1;
else pflag |= 0x0100;
} else pflag |= 0x0100;
} else if( p5<=0.0f ){
if( (x>=0) && (xGetValue(x, y-1, &v5);
if( v5 ) t.z = -p5;
else pflag |= 0x1000;
} else pflag |= 0x1000;
}
// Check penetration FLAG for corner contact
switch( pflag ){
case 0x0101: // Left-Top
{
layer->GetValue(x-1, y+1, &vC);
if( vC ){
float X, Y;
if( A_Calculate_Ellipse_Vector( pos.x-x, y+1-pos.z, is.x, is.z, &X, &Y ) ){
t.x = X;
t.z = -Y;
}
}
}
break;
case 0x1001: // Left-Bottom
{
layer->GetValue(x-1, y-1, &vC);
if( vC ){
float X, Y;
if( A_Calculate_Ellipse_Vector( pos.x-x, pos.z-y, is.x, is.z, &X, &Y ) ){
t.x = X;
t.z = Y;
}
}
}
break;
case 0x0110: // Rigth-Top
{
layer->GetValue(x+1, y+1, &vC);
if( vC ){
float X, Y;
if( A_Calculate_Ellipse_Vector( x+1-pos.x, y+1-pos.z, is.x, is.z, &X, &Y ) ){
t.x = -X;
t.z = -Y;
}
}
}
break;
case 0x1010: // Rigth-Bottom
{
layer->GetValue(x+1, y-1, &vC);
if( vC ){
float X, Y;
if( A_Calculate_Ellipse_Vector( x+1-pos.x, pos.z-y, is.x, is.z, &X, &Y ) ){
t.x = -X;
t.z = Y;
}
}
}
break;
}
if( t.x || t.z ){
obj->Translate( &t, grid );
contact = TRUE;
if( reaction ){
grid->TransformVector( &t, &t );
total += t;
}
if( index_contact < contact_count ){
index_contact++;
t.Normalize();
t *= influence;
VxVector contact_pos;
obj->GetPosition( &contact_pos );
contact_pos -= t;
beh->SetOutputParameterValue( index_contact, &contact_pos );
}
precis_tmp = 0;
}
}
}
}
}
}
}
}
} while( precis_tmp-- );
if( precis ){
obj->GetPosition( &ls->Old_Pos );
}
if( contact ){ // Contact
beh->ActivateOutput(0);
if( reaction ){
beh->SetOutputParameterValue(0, &total);
}
} else { // No Contact
beh->ActivateOutput(1);
}
return CKBR_ACTIVATENEXTFRAME;
}
/***********************************************/
/* Calculate Ellipse Vector */
/***********************************************/
inline BOOL A_Calculate_Ellipse_Vector( float x, float y, float R1, float R2, float *X, float *Y ){
float xdy = x/y;
float R1dR2 = R1/R2;
*Y = R1 / sqrtf(xdy*xdy+R1dR2*R1dR2);
if(*Y<=y) return FALSE;// (x,y) is outside ellipse
*X = *Y*xdy-x;
*Y -= y;
return TRUE;
}
/*******************************************************/
/* CALLBACK */
/*******************************************************/
CKERROR LayerSliderCallBack(const CKBehaviorContext& behcontext)
{
CKBehavior* beh = behcontext.Behavior;
switch( behcontext.CallbackMessage ){
case CKM_BEHAVIORCREATE:
case CKM_BEHAVIORLOAD:
{
A_LS *ls = new A_LS;
//--- if version < 2 create local parameter and settings
if( beh->GetLocalParameterCount() < 4 ){
CKParameterLocal *p;
p = beh->CreateLocalParameter( "Reaction Vector", CKPGUID_BOOL );
CKBOOL k = FALSE;
p->SetValue( &k );
p = beh->CreateLocalParameter( "Output Contact Count", CKPGUID_INT );
int c = 0;
p->SetValue( &c );
p = beh->CreateLocalParameter( NULL, CKPGUID_VOIDBUF ); // inner struct LS
p = beh->CreateLocalParameter( "Accuracy", CKPGUID_INT );
p->SetValue( &c );
}
beh->SetLocalParameterValue( 2, &ls, sizeof(ls));
} break;
case CKM_BEHAVIORDELETE:
{
A_LS *ls=NULL;
beh->GetLocalParameterValue(2, &ls);
if( ls ){
delete ls;
ls=NULL;
beh->SetLocalParameterValue(2, &ls);
}
} break;
case CKM_BEHAVIOREDITED:
{
CKParameterIn *pin;
CKParameter*pout;
for(int i=1 ; iGetInputParameterCount() ; i++) {
pin = beh->GetInputParameter(i);
if( pin->GetGUID() != CKPGUID_LAYERTYPE ){
pin->SetGUID( CKPGUID_LAYERTYPE );
if( pout = pin->GetRealSource() ){
pout->SetGUID( CKPGUID_LAYERTYPE );
}
}
}
} break;
case CKM_BEHAVIORSETTINGSEDITED:
{
CKBOOL reaction = FALSE;
//____ Output Contact Count
int contact_count = 0;
beh->GetLocalParameterValue(1, &contact_count);
if( contact_count<0 ) contact_count=0;
if( contact_count ){
reaction = TRUE;
beh->SetLocalParameterValue(0, &reaction);
} else { //____ Recation ?
beh->GetLocalParameterValue(0, &reaction);
}
if( beh->GetOutputParameterCount() ){
if( !reaction ) CKDestroyObject( beh->RemoveOutputParameter(0) );
} else {
if( reaction ) beh->CreateOutputParameter("Reaction", CKPGUID_VECTOR);
}
//----
char str[64];
int dif = contact_count - beh->GetOutputParameterCount();
if( dif>=0 && contact_count>0 ){ // add output
for( int a=0 ; a<(dif+1) ; a++){
sprintf( str, "contact%d", beh->GetOutputParameterCount());
beh->CreateOutputParameter(str, CKPGUID_VECTOR);
}
}
if (dif<-1 && !reaction)
dif--;
while( dif<-1 ){ // remove output
CKDestroyObject(
beh->RemoveOutputParameter( beh->GetOutputParameterCount()-1 )
);
dif++;
}
} break;
}
return CKBR_OK;
}