mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-11-28 21:05:53 +01:00
444 lines
12 KiB
C++
444 lines
12 KiB
C++
|
220
|
||
|
%{
|
||
|
#include "Entities/StdH/StdH.h"
|
||
|
%}
|
||
|
|
||
|
uses "Entities/WorldLink";
|
||
|
uses "Entities/Player";
|
||
|
uses "Entities/CameraMarker";
|
||
|
|
||
|
class CCamera : CMovableModelEntity
|
||
|
{
|
||
|
name "Camera";
|
||
|
thumbnail "Thumbnails\\Camera.tbn";
|
||
|
features "HasName", "IsTargetable", "IsImportant";
|
||
|
|
||
|
|
||
|
properties:
|
||
|
|
||
|
1 FLOAT m_tmTime "Time" 'E' = 5.0f, // how long to show the scene
|
||
|
2 FLOAT m_fFOV "FOV" 'F' = 90.0f, // camera fov
|
||
|
5 FLOAT m_fLastFOV = 90.0f,
|
||
|
3 CEntityPointer m_penTarget "Target" 'T' COLOR(C_lBLUE|0xFF),
|
||
|
4 CTString m_strName "Name" 'N' = "Camera",
|
||
|
6 CEntityPointer m_penOnBreak "OnBreak" 'B' COLOR(C_lRED|0xFF),
|
||
|
7 BOOL m_bWideScreen "WideScreen" 'W' = TRUE,
|
||
|
|
||
|
10 FLOAT m_tmAtMarker = 0.0f, // time when current marker was reached
|
||
|
11 FLOAT m_tmDelta = 0.0f, // time to reach next marker
|
||
|
13 FLOAT3D m_vPNp0 = FLOAT3D(0,0,0),
|
||
|
14 FLOAT3D m_vPNp1 = FLOAT3D(0,0,0),
|
||
|
15 FLOAT3D m_vTNp0 = FLOAT3D(0,0,0),
|
||
|
16 FLOAT3D m_vTNp1 = FLOAT3D(0,0,0),
|
||
|
17 FLOAT m_fFOVp0 = 0.0f,
|
||
|
18 FLOAT m_fFOVp1 = 0.0f,
|
||
|
19 FLOAT m_fTFOVp0 = 0.0f,
|
||
|
20 FLOAT m_fTFOVp1 = 0.0f,
|
||
|
31 FLOATquat3D m_qPNp0 = FLOATquat3D(0,0,0,0),
|
||
|
32 FLOATquat3D m_qPNp1 = FLOATquat3D(0,0,0,0),
|
||
|
33 FLOATquat3D m_qANp0 = FLOATquat3D(0,0,0,0),
|
||
|
34 FLOATquat3D m_qANp1 = FLOATquat3D(0,0,0,0),
|
||
|
|
||
|
40 CEntityPointer m_penLast, // previous marker
|
||
|
41 CEntityPointer m_penPlayer, // player viewing this camera
|
||
|
42 CTString m_strDescription = "",
|
||
|
43 BOOL m_bStopMoving = FALSE, // stop moving camera on next target
|
||
|
|
||
|
50 COLOR m_colFade0 = 0, // camera fading color
|
||
|
51 COLOR m_colFade1 = 0,
|
||
|
52 BOOL m_bMoving = FALSE, // set while moving
|
||
|
|
||
|
components:
|
||
|
|
||
|
1 model MODEL_CAMERA "Models\\Editor\\Camera.mdl",
|
||
|
2 texture TEXTURE_CAMERA "Models\\Editor\\Camera.tex"
|
||
|
|
||
|
|
||
|
functions:
|
||
|
|
||
|
// render particles
|
||
|
void RenderParticles(void)
|
||
|
{
|
||
|
if (Particle_GetViewer()==this) {
|
||
|
Particles_ViewerLocal(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check if entity is moved on a route set up by its targets
|
||
|
BOOL MovesByTargetedRoute( CTString &strTargetProperty) const
|
||
|
{
|
||
|
strTargetProperty = "Target";
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Check if entity can drop marker for making linked route
|
||
|
BOOL DropsMarker( CTFileName &fnmMarkerClass, CTString &strTargetProperty) const
|
||
|
{
|
||
|
fnmMarkerClass = CTFILENAME( "Classes\\CameraMarker.ecl");
|
||
|
strTargetProperty = "Target";
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// returns camera description
|
||
|
const CTString &GetDescription(void) const
|
||
|
{
|
||
|
if (m_penTarget!=NULL) {
|
||
|
((CTString&)m_strDescription).PrintF("->%s",(const char*) m_penTarget->GetName());
|
||
|
} else {
|
||
|
((CTString&)m_strDescription).PrintF("-><none>");
|
||
|
}
|
||
|
return m_strDescription;
|
||
|
}
|
||
|
|
||
|
|
||
|
CPlacement3D GetLerpedPlacement(void) const
|
||
|
{
|
||
|
FLOAT fLerpFactor;
|
||
|
if (IsPredictor()) {
|
||
|
fLerpFactor = _pTimer->GetLerpFactor();
|
||
|
} else {
|
||
|
fLerpFactor = _pTimer->GetLerpFactor2();
|
||
|
}
|
||
|
return LerpPlacementsPrecise(en_plLastPlacement, en_plPlacement, fLerpFactor);
|
||
|
//return CMovableEntity::GetLerpedPlacement();
|
||
|
}
|
||
|
|
||
|
void PreMoving()
|
||
|
{
|
||
|
// remember old placement for lerping
|
||
|
en_plLastPlacement = en_plPlacement;
|
||
|
}
|
||
|
|
||
|
|
||
|
void DoMoving()
|
||
|
{
|
||
|
if (!m_bMoving) {
|
||
|
return;
|
||
|
}
|
||
|
// read current tick
|
||
|
FLOAT tmCurrent = _pTimer->CurrentTick();
|
||
|
// lerping is initially enabled
|
||
|
BOOL bLerping = TRUE;
|
||
|
|
||
|
// if we hit a marker
|
||
|
if( tmCurrent > (m_tmAtMarker+m_tmDelta - _pTimer->TickQuantum*3/2))
|
||
|
{
|
||
|
// get markers
|
||
|
CCameraMarker *pcmNm1 = &(CCameraMarker&)*m_penLast;
|
||
|
CCameraMarker *pcmNp0 = &(CCameraMarker&)*m_penTarget;
|
||
|
CCameraMarker *pcmNp1 = &(CCameraMarker&)*pcmNp0->m_penTarget;
|
||
|
CCameraMarker *pcmNp2 = &(CCameraMarker&)*pcmNp1->m_penTarget;
|
||
|
|
||
|
// repeat
|
||
|
FOREVER {
|
||
|
// if there is a trigger at the hit marker
|
||
|
if (pcmNp0->m_penTrigger!=NULL) {
|
||
|
// trigger it
|
||
|
SendToTarget(pcmNp0->m_penTrigger, EET_TRIGGER, m_penPlayer);
|
||
|
}
|
||
|
|
||
|
// if the marker should not be skipped
|
||
|
if (!pcmNp0->m_bSkipToNext) {
|
||
|
// stop skipping
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// go to next marker immediately
|
||
|
pcmNm1 = pcmNp0;
|
||
|
pcmNp0 = pcmNp1;
|
||
|
pcmNp1 = pcmNp2;
|
||
|
pcmNp2 = (CCameraMarker*)&*pcmNp2->m_penTarget;
|
||
|
// disable lerping
|
||
|
bLerping = FALSE;
|
||
|
}
|
||
|
|
||
|
// update markers for next interval
|
||
|
m_penTarget = pcmNp1;
|
||
|
m_penLast = pcmNp0;
|
||
|
|
||
|
// get markers
|
||
|
CCameraMarker &cmNm1 = *pcmNm1;
|
||
|
CCameraMarker &cmNp0 = *pcmNp0;
|
||
|
CCameraMarker &cmNp1 = *pcmNp1;
|
||
|
CCameraMarker &cmNp2 = *pcmNp2;
|
||
|
|
||
|
// get positions from four markers
|
||
|
const FLOAT3D &vPNm1 = cmNm1.GetPlacement().pl_PositionVector;
|
||
|
const FLOAT3D &vPNp0 = cmNp0.GetPlacement().pl_PositionVector;
|
||
|
const FLOAT3D &vPNp1 = cmNp1.GetPlacement().pl_PositionVector;
|
||
|
const FLOAT3D &vPNp2 = cmNp2.GetPlacement().pl_PositionVector;
|
||
|
ANGLE3D aPNm1 = cmNm1.GetPlacement().pl_OrientationAngle;
|
||
|
ANGLE3D aPNp0 = cmNp0.GetPlacement().pl_OrientationAngle;
|
||
|
ANGLE3D aPNp1 = cmNp1.GetPlacement().pl_OrientationAngle;
|
||
|
ANGLE3D aPNp2 = cmNp2.GetPlacement().pl_OrientationAngle;
|
||
|
FLOAT fFOVm1 = cmNm1.m_fFOV;
|
||
|
FLOAT fFOVp0 = cmNp0.m_fFOV;
|
||
|
FLOAT fFOVp1 = cmNp1.m_fFOV;
|
||
|
FLOAT fFOVp2 = cmNp2.m_fFOV;
|
||
|
m_colFade0 = cmNp0.m_colFade;
|
||
|
m_colFade1 = cmNp1.m_colFade;
|
||
|
|
||
|
// find quaternions for rotations
|
||
|
FLOATquat3D qPNm1; qPNm1.FromEuler(aPNm1);
|
||
|
FLOATquat3D qPNp0; qPNp0.FromEuler(aPNp0);
|
||
|
FLOATquat3D qPNp1; qPNp1.FromEuler(aPNp1);
|
||
|
FLOATquat3D qPNp2; qPNp2.FromEuler(aPNp2);
|
||
|
|
||
|
// make all angles between quaternion pairs acute
|
||
|
if( qPNm1%qPNp0<0 ) {
|
||
|
qPNp0 = -qPNp0;
|
||
|
}
|
||
|
if( qPNp0%qPNp1<0 ) {
|
||
|
qPNp1 = -qPNp1;
|
||
|
}
|
||
|
if( qPNp1%qPNp2<0 ) {
|
||
|
qPNp2 = -qPNp2;
|
||
|
}
|
||
|
|
||
|
// update time and position
|
||
|
m_tmAtMarker = m_tmAtMarker+m_tmDelta;
|
||
|
m_tmDelta = cmNp0.m_fDeltaTime;
|
||
|
m_vPNp0 = vPNp0;
|
||
|
m_vPNp1 = vPNp1;
|
||
|
m_fFOVp0 = fFOVp0;
|
||
|
m_fFOVp1 = fFOVp1;
|
||
|
m_qPNp0 = qPNp0;
|
||
|
m_qPNp1 = qPNp1;
|
||
|
|
||
|
// determine delta time multipliers
|
||
|
FLOAT tmDNm1 = cmNm1.m_fDeltaTime;
|
||
|
FLOAT tmDNp0 = cmNp0.m_fDeltaTime;
|
||
|
FLOAT tmDNp1 = cmNp1.m_fDeltaTime;
|
||
|
FLOAT fD0 = 2*tmDNp0 / (tmDNm1+tmDNp0);
|
||
|
FLOAT fD1 = 2*tmDNp0 / (tmDNp0+tmDNp1);
|
||
|
|
||
|
// determine biases, tensions and continuities
|
||
|
FLOAT fBNp0 = cmNp0.m_fBias;
|
||
|
FLOAT fTNp0 = cmNp0.m_fTension;
|
||
|
FLOAT fCNp0 = cmNp0.m_fContinuity;
|
||
|
FLOAT fBNp1 = cmNp1.m_fBias;
|
||
|
FLOAT fTNp1 = cmNp1.m_fTension;
|
||
|
FLOAT fCNp1 = cmNp1.m_fContinuity;
|
||
|
|
||
|
FLOAT fF00 = (1-fTNp0)*(1-fCNp0)*(1-fBNp0) / 2;
|
||
|
FLOAT fF01 = (1-fTNp0)*(1+fCNp0)*(1+fBNp0) / 2;
|
||
|
FLOAT fF10 = (1-fTNp1)*(1+fCNp1)*(1-fBNp1) / 2;
|
||
|
FLOAT fF11 = (1-fTNp1)*(1-fCNp1)*(1+fBNp1) / 2;
|
||
|
|
||
|
// find tangents for translation
|
||
|
m_vTNp0 = ( (vPNp1-vPNp0) * fF00 + (vPNp0-vPNm1) * fF01) * fD0;
|
||
|
m_vTNp1 = ( (vPNp2-vPNp1) * fF10 + (vPNp1-vPNp0) * fF11) * fD1;
|
||
|
|
||
|
// find tangents for FOV
|
||
|
m_fTFOVp0 = ( (fFOVp1-fFOVp0) * fF00 + (fFOVp0-fFOVm1) * fF01) * fD0;
|
||
|
m_fTFOVp1 = ( (fFOVp2-fFOVp1) * fF10 + (fFOVp1-fFOVp0) * fF11) * fD1;
|
||
|
|
||
|
// find tangents for rotation
|
||
|
FLOATquat3D qTNp0, qTNp1;
|
||
|
qTNp0 = ( Log(qPNp0.Inv()*qPNp1) * fF00 + Log(qPNm1.Inv()*qPNp0) * fF01) * fD0;
|
||
|
qTNp1 = ( Log(qPNp1.Inv()*qPNp2) * fF10 + Log(qPNp0.Inv()*qPNp1) * fF11) * fD1;
|
||
|
|
||
|
// find squad parameters
|
||
|
m_qANp0 = qPNp0*Exp( (qTNp0 - Log(qPNp0.Inv()*qPNp1))/2 );
|
||
|
m_qANp1 = qPNp1*Exp( (Log(qPNp0.Inv()*qPNp1) - qTNp1)/2 );
|
||
|
|
||
|
// check for stop moving
|
||
|
if( cmNp0.m_bStopMoving) {
|
||
|
m_bStopMoving = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// calculate the parameter value and hermit basis
|
||
|
FLOAT fT = (tmCurrent - m_tmAtMarker) / m_tmDelta;
|
||
|
FLOAT fH0 = 2*fT*fT*fT - 3*fT*fT + 1;
|
||
|
FLOAT fH1 = -2*fT*fT*fT + 3*fT*fT;
|
||
|
FLOAT fH2 = fT*fT*fT - 2*fT*fT + fT;
|
||
|
FLOAT fH3 = fT*fT*fT - fT*fT;
|
||
|
|
||
|
// interpolate position, rotation and fov
|
||
|
FLOAT3D vPos = m_vPNp0*fH0 + m_vPNp1*fH1 + m_vTNp0*fH2 + m_vTNp1*fH3;
|
||
|
FLOAT fFOV = m_fFOVp0*fH0 + m_fFOVp1*fH1 + m_fTFOVp0*fH2 + m_fTFOVp1*fH3;
|
||
|
FLOATquat3D qRot = Squad(fT, m_qPNp0, m_qPNp1, m_qANp0, m_qANp1);
|
||
|
FLOATmatrix3D mRot;
|
||
|
qRot.ToMatrix(mRot);
|
||
|
|
||
|
// just cache near polygons for various engine needs
|
||
|
en_vNextPosition = vPos;
|
||
|
en_mNextRotation = mRot;
|
||
|
CacheNearPolygons();
|
||
|
|
||
|
// set new placement
|
||
|
CPlacement3D plNew;
|
||
|
plNew.pl_PositionVector = vPos;
|
||
|
DecomposeRotationMatrixNoSnap(plNew.pl_OrientationAngle, mRot);
|
||
|
SetPlacement_internal(plNew, mRot, TRUE);
|
||
|
// if lerping is disabled
|
||
|
if (!bLerping) {
|
||
|
// make last placement same as this one
|
||
|
en_plLastPlacement = en_plPlacement;
|
||
|
}
|
||
|
// set new fov
|
||
|
m_fLastFOV = m_fFOV;
|
||
|
m_fFOV = fFOV;
|
||
|
}
|
||
|
|
||
|
|
||
|
void PostMoving()
|
||
|
{
|
||
|
if (!m_bMoving) {
|
||
|
return;
|
||
|
}
|
||
|
//
|
||
|
if( m_bStopMoving) {
|
||
|
m_bMoving = FALSE;
|
||
|
// mark for removing from list of movers
|
||
|
en_ulFlags |= ENF_INRENDERING;
|
||
|
SendEvent( EStop());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
procedures:
|
||
|
|
||
|
// routine for playing static camera
|
||
|
PlayStaticCamera()
|
||
|
{
|
||
|
m_bMoving = FALSE;
|
||
|
ECameraStart eStart;
|
||
|
eStart.penCamera = this;
|
||
|
m_penPlayer->SendEvent(eStart);
|
||
|
autowait(m_tmTime);
|
||
|
ECameraStop eStop;
|
||
|
eStop.penCamera=this;
|
||
|
m_penPlayer->SendEvent(eStop);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
// routine for playing movable camera
|
||
|
PlayMovingCamera()
|
||
|
{
|
||
|
// init camera
|
||
|
ECameraStart eStart;
|
||
|
eStart.penCamera = this;
|
||
|
m_penPlayer->SendEvent(eStart);
|
||
|
|
||
|
// check all markers for correct type and numbers
|
||
|
INDEX ctMarkers=1;
|
||
|
INDEX ctNonSkipped=0;
|
||
|
CCameraMarker *pcm0 = (CCameraMarker*)&*m_penTarget;
|
||
|
CCameraMarker *pcm = (CCameraMarker*)&*pcm0->m_penTarget;
|
||
|
// loop thru markers
|
||
|
while( pcm!=NULL && pcm->m_penTarget!=pcm0)
|
||
|
{
|
||
|
pcm = (CCameraMarker*)&*pcm->m_penTarget;
|
||
|
if (pcm==NULL) {
|
||
|
WarningMessage( "Movable camera - broken link!");
|
||
|
return;
|
||
|
}
|
||
|
if (!pcm->m_bSkipToNext) {
|
||
|
ctNonSkipped++;
|
||
|
}
|
||
|
ctMarkers++;
|
||
|
if (ctMarkers>500) {
|
||
|
WarningMessage( "Movable camera - invalid marker loop!");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
// check if we have enough markers to do smooth interpolation
|
||
|
if( ctMarkers<2) {
|
||
|
WarningMessage( "Movable camera requires at least 2 markers in order to work!");
|
||
|
return;
|
||
|
}
|
||
|
// check if we have enough markers to do smooth interpolation
|
||
|
if( ctNonSkipped<1) {
|
||
|
WarningMessage( "Movable camera requires at least 1 non-skipped marker!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// prepare internal variables
|
||
|
FLOAT tmCurrent = _pTimer->CurrentTick();
|
||
|
m_tmAtMarker = tmCurrent;
|
||
|
m_tmDelta = 0.0f;
|
||
|
m_bStopMoving = FALSE;
|
||
|
m_penLast = pcm; // keep last marker
|
||
|
ASSERT( pcm->m_penTarget == m_penTarget);
|
||
|
pcm = (CCameraMarker*)&*m_penTarget;
|
||
|
m_colFade0 = m_colFade1 = pcm->m_colFade;
|
||
|
|
||
|
// register camera as movable entity
|
||
|
AddToMovers();
|
||
|
m_bMoving = TRUE;
|
||
|
|
||
|
// roll, baby, roll ...
|
||
|
wait() {
|
||
|
on( EStop) : {
|
||
|
ECameraStop eStop;
|
||
|
eStop.penCamera=this;
|
||
|
m_penPlayer->SendEvent(eStop);
|
||
|
return;
|
||
|
}
|
||
|
otherwise() : {
|
||
|
resume;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// all done for now
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
// determine camera type and jump to corresponding routine
|
||
|
PlayCamera()
|
||
|
{
|
||
|
// eventually add to movers list
|
||
|
CCameraMarker &cm = (CCameraMarker&)*m_penTarget;
|
||
|
// if we have at least one marker
|
||
|
if( &cm!=NULL) {
|
||
|
// treat camera as movable
|
||
|
jump PlayMovingCamera();
|
||
|
// if there isn't any markers
|
||
|
} else {
|
||
|
// treat camera as fixed
|
||
|
jump PlayStaticCamera();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Main()
|
||
|
{
|
||
|
// init as model
|
||
|
InitAsEditorModel();
|
||
|
SetPhysicsFlags(EPF_MOVABLE);
|
||
|
SetCollisionFlags(ECF_CAMERA);
|
||
|
// set appearance
|
||
|
FLOAT fSize = 5.0f;
|
||
|
GetModelObject()->mo_Stretch = FLOAT3D(fSize, fSize, fSize);
|
||
|
SetModel(MODEL_CAMERA);
|
||
|
SetModelMainTexture(TEXTURE_CAMERA);
|
||
|
m_fLastFOV = m_fFOV;
|
||
|
|
||
|
if( m_penTarget!=NULL && !IsOfClass( m_penTarget, "Camera Marker")) {
|
||
|
WarningMessage( "Entity '%s' is not of Camera Marker class!",(const char*) m_penTarget->GetName());
|
||
|
m_penTarget = NULL;
|
||
|
}
|
||
|
|
||
|
while(TRUE)
|
||
|
{
|
||
|
wait() {
|
||
|
on (ETrigger eTrigger) : {
|
||
|
if( IsDerivedFromClass(eTrigger.penCaused, "Player")) {
|
||
|
m_penPlayer = eTrigger.penCaused;
|
||
|
call PlayCamera();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// cease to exist
|
||
|
Destroy();
|
||
|
return;
|
||
|
};
|
||
|
};
|
||
|
|