1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "nel/misc/matrix.h"
22 #include "nel/3d/u_scene.h"
23 #include "nel/3d/u_transform.h"
24 #include "nel/3d/u_skeleton.h"
25 #include "nel/3d/u_bone.h"
26 #include "attached_fx.h"
27 #include "character_cl.h"
28 #include "user_entity.h"
30 #include "time_client.h"
31 #include "fx_manager.h"
37 extern NL3D::UScene
*Scene
;
40 using namespace NLMISC
;
43 extern CUserEntity
*UserEntity
;
45 // *************************************************************************************
46 CAttachedFX::CAttachedFX()
51 // *************************************************************************************
52 CAttachedFX::~CAttachedFX()
57 // ***********************************************************************************************************************
58 void CAttachedFX::clear()
62 if (StickMode
== CFXStickMode::SpawnPermanent
)
64 FXMngr
.addFX(FX
, 100.f
);
68 Scene
->deleteInstance(FX
);
73 TimeOutDate
= FX_MANAGER_DEFAULT_TIMEOUT
;
74 StickMode
= CFXStickMode::Follow
;
77 TargeterUserBoneID
= 0xff;
80 // ***********************************************************************************************************************
81 void CAttachedFX::create(CCharacterCL
&parent
,
82 const CBuildInfo
&buildInfo
,
83 const CTargeterInfo
&targeterInfo
87 if (!buildInfo
.Sheet
) return;
88 UParticleSystemInstance instance
= buildInfo
.Sheet
->createMatchingInstance();
89 // TODO nico : user params are not in the buildInfo, but are set by createMatchingInstance then by the caller (they could be set directly by createMatchingInstance)
90 if (instance
.empty()) return;
91 create(parent
, instance
, buildInfo
, targeterInfo
);
94 // ***********************************************************************************************************************
95 void CAttachedFX::create(CCharacterCL
&parent
,
96 NL3D::UParticleSystemInstance instance
,
97 const CBuildInfo
&buildInfo
,
98 const CTargeterInfo
&targeterInfo
101 nlassert(buildInfo
.Sheet
);
102 TimeOutDate
= buildInfo
.TimeOut
;
103 const CFXStickMode
*stickMode
= buildInfo
.StickMode
? buildInfo
.StickMode
: &buildInfo
.Sheet
->Sheet
->StickMode
;
104 if (!instance
.empty())
106 instance
.setClusterSystem(parent
.getClusterSystem());
107 instance
.setTransformMode(NL3D::UTransformable::DirectMatrix
);
111 AniFX
= buildInfo
.Sheet
;
112 StickMode
= stickMode
->Mode
;
113 SpawnTime
= TimeInSec
;
114 MaxAnimCount
= buildInfo
.MaxNumAnimCount
;
115 TargeterInfo
= targeterInfo
;
116 switch(stickMode
->Mode
)
118 case CFXStickMode::StaticMatrix
:
120 const CMatrix
&staticMatrix
= buildInfo
.StaticMatrix
? *buildInfo
.StaticMatrix
: NLMISC::CMatrix::Identity
;
121 instance
.setMatrix(staticMatrix
);
122 SpawnPos
= staticMatrix
.getPos();
125 case CFXStickMode::Spawn
:
126 case CFXStickMode::SpawnPermanent
:
128 parent
.alignFX(instance
, parent
.getScalePos());
129 SpawnPos
= parent
.pos() + buildInfo
.StickOffset
;
130 if (stickMode
->Mode
== CFXStickMode::SpawnPermanent
)
132 instance
.forceInstanciate();
136 case CFXStickMode::UserBone
:
138 if (parent
.skeleton())
140 sint boneID
= parent
.skeleton()->getBoneIdByName(NLMISC::CStringMapper::unmap(stickMode
->UserBoneName
));
144 parent
.skeleton()->stickObjectEx(instance
, boneID
, true);
145 NLMISC::CMatrix mat
= instance
.getMatrix();
146 mat
.scale(buildInfo
.Sheet
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
);
147 mat
.setPos(buildInfo
.StickOffset
);
148 if (!instance
.empty()) instance
.setMatrix(mat
);
149 SpawnPos
= buildInfo
.StickOffset
;
150 // if parent is hidden, then force to compute the bone at least once
151 if (parent
.skeleton()->getVisibility() == NL3D::UTransform::Hide
)
153 parent
.forceEvalAnim();
154 // force to compute fx at least once
155 parent
.skeleton()->forceComputeBone(UserBoneID
);
160 // bone not found or no skeleton
161 if (!parent
.instance().empty())
163 instance
.parent(parent
.instance());
164 SpawnPos
= buildInfo
.StickOffset
;
168 // just spawn at position of entity
169 SpawnPos
= parent
.pos() + buildInfo
.StickOffset
;
173 case CFXStickMode::UserBoneOrientedTowardTargeter
:
174 if (parent
.skeleton())
176 UserBoneID
= parent
.skeleton()->getBoneIdByName(NLMISC::CStringMapper::unmap(stickMode
->UserBoneName
));
177 if (UserBoneID
== 0xff)
179 nlwarning("Bad bone name : %s", NLMISC::CStringMapper::unmap(stickMode
->UserBoneName
).c_str());
186 SpawnPos
= buildInfo
.StickOffset
;
187 update(parent
, CMatrix::Identity
);
189 case CFXStickMode::UserBoneRay
:
190 if (parent
.skeleton())
192 UserBoneID
= parent
.skeleton()->getBoneIdByName(NLMISC::CStringMapper::unmap(stickMode
->UserBoneName
));
193 if (UserBoneID
== 0xff)
195 nlwarning("Bad bone name : %s", NLMISC::CStringMapper::unmap(stickMode
->UserBoneName
).c_str());
202 SpawnPos
= buildInfo
.StickOffset
;
203 TargeterUserBoneID
=0xff;
204 if (targeterInfo
.StickMode
.UserBoneName
!= 0)
206 CEntityCL
*targeter
= EntitiesMngr
.entity(TargeterInfo
.Slot
);
207 if (parent
.skeleton() && targeter
&& targeter
->skeleton())
209 TargeterUserBoneID
= parent
.skeleton()->getBoneIdByName(NLMISC::CStringMapper::unmap(TargeterInfo
.StickMode
.UserBoneName
));
212 update(parent
, CMatrix::Identity
);
214 case CFXStickMode::OrientedTowardTargeter
:
215 SpawnPos
= buildInfo
.StickOffset
;
216 update(parent
, CMatrix::Identity
);
218 default: // -> stick fx in 'Follow' mode
219 parent
.alignFX(instance
, parent
.getScalePos());
220 SpawnPos
= buildInfo
.StickOffset
;
226 // ***********************************************************************************************************************
227 void CAttachedFX::evalTargeterStickPos(NLMISC::CVector
&dest
) const
229 CEntityCL
*targeter
= EntitiesMngr
.entity(TargeterInfo
.Slot
);
232 dest
= TargeterInfo
.DefaultPos
;
235 switch(TargeterInfo
.StickMode
.Mode
)
237 case CFXStickMode::Spawn
:
238 case CFXStickMode::SpawnPermanent
:
239 case CFXStickMode::Follow
:
240 case CFXStickMode::FollowNoRotation
:
241 case CFXStickMode::OrientedTowardTargeter
:
242 dest
= targeter
->pos();
244 case CFXStickMode::StaticMatrix
:
245 nlwarning("Not implemented"); // this case is not used for now
246 dest
= targeter
->pos();
248 case CFXStickMode::UserBoneOrientedTowardTargeter
:
249 case CFXStickMode::UserBoneRay
:
250 case CFXStickMode::UserBone
:
251 if (targeter
->skeleton() && TargeterUserBoneID
!= 0xff)
253 const UBone bone
= targeter
->skeleton()->getBone(TargeterUserBoneID
);
254 targeter
->forceEvalAnim();
255 targeter
->skeleton()->forceComputeBone(TargeterUserBoneID
);
256 dest
= bone
.getLastWorldMatrixComputed() * TargeterInfo
.StickOffset
;
260 dest
= TargeterInfo
.DefaultPos
;
266 // ***********************************************************************************************************************
267 void CAttachedFX::update(CCharacterCL
&parent
, const NLMISC::CMatrix
&alignMatrix
)
269 if (AniFX
&& !FX
.empty())
271 NLMISC::CVector trackPos
;
272 // see if fx has a track applied on it
276 AniFX
->PosTrack
->interpolate((float) (TimeInSec
- SpawnTime
), trackPos
);
280 trackPos
.set(0.f
, 0.f
, 0.f
);
282 // apply pos depending on mode
285 case CFXStickMode::UserBone
:
287 FX
.setClusterSystem(parent
.getClusterSystem());
288 if (!AniFX
->PosTrack
)
291 if (parent
.skeleton() && UserBoneID
!= 0xff)
293 if (parent
.skeleton()->getVisibility() == UTransform::Hide
)
295 // if user no visible, force to compute the bone.
296 parent
.forceEvalAnim();
297 parent
.skeleton()->forceComputeBone(UserBoneID
);
302 if (parent
.skeleton())
304 if (UserBoneID
!= 0xff)
306 CMatrix mat
= FX
.getMatrix();
307 mat
.setPos(trackPos
+ SpawnPos
);
308 mat
.setScale(AniFX
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
);
310 if (&parent
== UserEntity
&& UserEntity
->skeleton())
312 if (UserEntity
->skeleton()->getVisibility() == UTransform::Hide
)
314 // if user no visible, force to compute the bone.
315 parent
.forceEvalAnim();
316 UserEntity
->skeleton()->forceComputeBone(UserBoneID
);
321 // no skeleton or bone not found
322 if (!parent
.instance().empty())
324 CMatrix mat
= FX
.getMatrix();
325 mat
.setPos(trackPos
+ SpawnPos
);
326 mat
.setScale(AniFX
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
);
331 // no skeleton, no instance
332 CMatrix mat
= FX
.getMatrix();
333 mat
.setPos(trackPos
+ SpawnPos
+ parent
.pos());
334 mat
.setScale(AniFX
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
);
339 case CFXStickMode::Follow
:
340 // just change local pos
341 parent
.alignFX(FX
, alignMatrix
, AniFX
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
, trackPos
+ SpawnPos
);
343 case CFXStickMode::FollowNoRotation
:
345 // just update the pos
346 CMatrix mat
= FX
.getMatrix();
347 mat
.setScale(AniFX
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
);
348 mat
.setPos(trackPos
+ parent
.pos().asVector());
350 FX
.setClusterSystem(parent
.getClusterSystem());
353 case CFXStickMode::StaticObjectCastRay
:
355 // if not animated need no updates
356 if (!AniFX
->PosTrack
) return;
357 nlwarning("Not implemented");
360 case CFXStickMode::Spawn
:
361 case CFXStickMode::SpawnPermanent
:
363 if (!AniFX
->PosTrack
) return;
364 // put in local basis and offset spawn pos
365 CMatrix mat
= FX
.getMatrix();
366 mat
.setScale(AniFX
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
);
367 mat
.setPos(SpawnPos
+ FX
.getMatrix().mulVector(trackPos
));
369 // Do not update the cluster system because fx stays in place
372 case CFXStickMode::OrientedTowardTargeter
:
374 CEntityCL
*targeter
= EntitiesMngr
.entity(TargeterInfo
.Slot
);
377 CVectorD orientD
= parent
.pos() - targeter
->pos();
378 CVector
J((float) orientD
.x
, (float) orientD
.y
, 0.f
); // project on XY plane
380 CVector I
= J
^ CVector::K
;
382 mat
.setRot(I
, J
, CVector::K
);
383 mat
.setScale(AniFX
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
);
384 mat
.setPos(trackPos
+ parent
.pos().asVector());
386 FX
.setClusterSystem(parent
.getClusterSystem());
390 case CFXStickMode::UserBoneOrientedTowardTargeter
:
392 CEntityCL
*targeter
= EntitiesMngr
.entity(TargeterInfo
.Slot
);
397 if (UserBoneID
== 0xff || !parent
.skeleton())
399 // bone not found -> use parent position instead
400 orientD
= parent
.pos() - targeter
->pos();
401 orientMat
.setPos(trackPos
+ parent
.pos());
405 const UBone bone
= parent
.skeleton()->getBone(UserBoneID
);
406 parent
.forceEvalAnim();
407 parent
.skeleton()->forceComputeBone(UserBoneID
);
408 const CMatrix
&wm
= bone
.getLastWorldMatrixComputed();
409 // compute orientation toward targeter, and build a matrix from it
410 orientD
= wm
.getPos() - targeter
->pos().asVector();
411 orientMat
.setPos(trackPos
+ wm
.getPos());
413 CVector
J(orientD
.x
, orientD
.y
, 0.f
); // project on XY plane
415 CVector I
= J
^ CVector::K
;
416 float scale
= AniFX
->Sheet
->ScaleFX
? parent
.getScaleRef() : 1.f
;
417 orientMat
.setRot(scale
* I
, scale
* J
, scale
* CVector::K
, true);
418 FX
.setMatrix(orientMat
);
419 FX
.setClusterSystem(parent
.getClusterSystem());
423 case CFXStickMode::UserBoneRay
:
426 evalTargeterStickPos(aimingPoint
);
427 CVector startPoint
= CVector::Null
;
428 if (UserBoneID
!= 0xff && parent
.skeleton())
430 const UBone bone
= parent
.skeleton()->getBone(UserBoneID
);
431 parent
.forceEvalAnim();
432 parent
.skeleton()->forceComputeBone(UserBoneID
);
433 startPoint
= bone
.getLastWorldMatrixComputed().getPos();
436 CVector ray
= aimingPoint
- startPoint
;
437 CVector I
= ray
.normed();
438 CVector K
= (CVector::K
- (I
* CVector::K
) * I
).normed();
442 I
*= ray
.norm() / AniFX
->Sheet
->RayRefLength
;
444 rayMat
.setRot(I
, J
, K
);
445 rayMat
.setPos(startPoint
+ 0.5f
* AniFX
->Sheet
->RayRefLength
* I
);
446 FX
.setMatrix(rayMat
);
447 // don't clusterize ray, because it can be quite large
448 FX
.setForceClipRoot(true);