-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathCObjectJoint.cpp
404 lines (305 loc) · 8.8 KB
/
CObjectJoint.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
// CObjectJoint.cpp
//
// CObjectJoint class
// Copyright (c) 2017 by Kronosaur Productions, LLC. All Rights Reserved.
#include "PreComp.h"
#define FIELD_LENGTH CONSTLIT("length")
#define FIELD_LIMIT1 CONSTLIT("limit1")
#define FIELD_LIMIT2 CONSTLIT("limit2")
#define FIELD_POS1 CONSTLIT("pos1")
#define FIELD_POS2 CONSTLIT("pos2")
#define TYPE_HINGE CONSTLIT("hinge")
#define TYPE_ROD CONSTLIT("rod")
#define TYPE_SPINE CONSTLIT("spine")
const Metric SPINE_MAX_DIST2 = (0.0 * g_KlicksPerPixel * g_KlicksPerPixel);
const Metric SPINE_SPRING_K = 0.05;
const Metric SPINE_DAMPING = 0.05;
const Metric SPINE_GAMMA = 0.5 * sqrt(4.0 * SPINE_SPRING_K - SPINE_DAMPING * SPINE_DAMPING);
CObjectJoint::CObjectJoint (ETypes iType, CSpaceObject *pFrom, CSpaceObject *pTo) :
m_iType(iType),
m_dwID(g_pUniverse->CreateGlobalID()),
m_iTick(0),
m_iLifetime(-1),
m_iMaxLength(0),
m_fDestroyed(false),
m_fPaintNeeded(false),
m_fShipCompartment(false)
// CObjectJoint constructor
{
m_P1.pObj = pFrom;
m_P2.pObj = pTo;
// If no type, then we don't initialize anything else. This happens when
// loading from a stream.
if (iType == jointNone)
return;
// Set some flags
ASSERT(pFrom && pTo);
m_fShipCompartment = (pFrom->IsAttached() || pTo->IsAttached());
// Some defaults
switch (m_iType)
{
case jointRod:
case jointSpine:
m_iMaxLength = mathRound(pFrom->GetDistance(pTo) / g_KlicksPerPixel);
break;
}
}
void CObjectJoint::AddContacts (CPhysicsContactResolver &Resolver)
// AddContacts
//
// Adds contacts
{
switch (m_iType)
{
case jointHinge:
Resolver.AddRod(CPhysicsContact::contactCollision, m_P1.pObj, m_P2.pObj, 0.0);
break;
case jointRod:
case jointSpine:
if (m_P1.pObj->GetAttachedRoot() == m_P2.pObj->GetAttachedRoot())
Resolver.AddRod(CPhysicsContact::contactSelfContact, m_P1.pObj, m_P2.pObj, m_iMaxLength * g_KlicksPerPixel);
else
Resolver.AddRod(CPhysicsContact::contactCollision, m_P1.pObj, m_P2.pObj, m_iMaxLength * g_KlicksPerPixel);
break;
}
}
void CObjectJoint::AddForces (void)
// AddForces
//
// Adds forces on the objects caused by the joint.
{
switch (m_iType)
{
case jointSpine:
{
// For spine joints we try to align P2's rotation with P1's.
int iRotation = m_P1.pObj->GetRotation();
// For now we assume that we want P2 to be behind P1 (with respect
// to P1's rotation). This can be configurable later.
int iJointRotation = iRotation + 180;
// Figure out the desired position of P2's center, based on
// the rod length and the rotation.
CVector vDesiredPos = m_P1.pObj->GetPos() + PolarToVector(iJointRotation, m_iMaxLength * g_KlicksPerPixel);
// If we've close to the desired position, then we don't have any forces
CVector vDiff = m_P2.pObj->GetPos() - vDesiredPos;
Metric rDist2 = vDiff.Length2();
if (rDist2 < SPINE_MAX_DIST2)
break;
// Get velocity relative to our anchor.
CSpaceObject *pRoot = m_P2.pObj->GetAttachedRoot();
CVector vCurVel = (pRoot ? m_P2.pObj->GetVel() - pRoot->GetVel() : m_P2.pObj->GetVel());
// Compute where we will be next tick based on equations for a damped-harmonic
// oscillator.
//
// See: Game Physics Engine Development, Ian Millington, pg.107.
CVector vC = (vDiff * (SPINE_DAMPING / (2.0 * SPINE_GAMMA))) + (vCurVel * (1.0 / SPINE_GAMMA));
CVector vTarget = (vDiff * cos(SPINE_GAMMA * g_SecondsPerUpdate)) + (vC * sin(SPINE_GAMMA * g_SecondsPerUpdate));
vTarget = vTarget * exp(-0.5 * g_SecondsPerUpdate * SPINE_DAMPING);
// Calculate the acceleration
CVector vAccel = (vTarget - vDiff) * (1.0 / g_SecondsPerUpdate * g_SecondsPerUpdate) - (vCurVel * g_SecondsPerUpdate);
m_P2.pObj->DeltaV(vAccel);
break;
}
}
}
void CObjectJoint::ApplyOptions (ICCItem *pOptions)
// ApplyOptions
//
// We accept the following options:
//
// length: length in pixels
// pos1: vector or { angle:n radius:n z:n }
// pos2: vector or { angle:n radius:n z:n }
// limit1: { min:n max:n }
// limit2: { min:n max:n }
{
CCodeChainCtx Ctx;
if (pOptions == NULL)
return;
// Length
ICCItem *pLength = pOptions->GetElement(FIELD_LENGTH);
if (pLength)
m_iMaxLength = Max(0, pLength->GetIntegerValue());
// Position
ICCItem *pPos = pOptions->GetElement(FIELD_POS1);
if (pPos)
m_P1.Pos = Ctx.As3DObjectPos(m_P1.pObj, pPos);
pPos = pOptions->GetElement(FIELD_POS2);
if (pPos)
m_P2.Pos = Ctx.As3DObjectPos(m_P2.pObj, pPos);
// Limits
ICCItem *pLimits = pOptions->GetElement(FIELD_LIMIT1);
if (pLimits && !pLimits->IsNil())
{
int iMinArc, iMaxArc;
Ctx.AsArc(pLimits, &iMinArc, &iMaxArc);
m_P1.iMinArc = iMinArc;
m_P1.iMaxArc = iMaxArc;
}
pLimits = pOptions->GetElement(FIELD_LIMIT2);
if (pLimits && !pLimits->IsNil())
{
int iMinArc, iMaxArc;
Ctx.AsArc(pLimits, &iMinArc, &iMaxArc);
m_P2.iMinArc = iMinArc;
m_P2.iMaxArc = iMaxArc;
}
}
void CObjectJoint::CreateFromStream (SLoadCtx &Ctx, CObjectJoint **retpJoint)
// CreateFromStream
//
// Creates a new joint from a stream.
{
CObjectJoint *pJoint = new CObjectJoint(jointNone, NULL, NULL);
int iType;
Ctx.pStream->Read(iType);
pJoint->m_iType = (ETypes)iType;
Ctx.pStream->Read(pJoint->m_dwID);
Ctx.pStream->Read(pJoint->m_iTick);
Ctx.pStream->Read(pJoint->m_iLifetime);
// Flags
DWORD dwFlags;
if (Ctx.dwVersion >= 148)
Ctx.pStream->Read(dwFlags);
else
dwFlags = 0;
pJoint->m_fShipCompartment = (dwFlags & 0x00000001 ? true : false);
// Max length
Ctx.pStream->Read(pJoint->m_iMaxLength);
// Attack points
ReadFromStream(Ctx, pJoint->m_P1);
ReadFromStream(Ctx, pJoint->m_P2);
*retpJoint = pJoint;
}
void CObjectJoint::Paint (CG32bitImage &Dest, SViewportPaintCtx &Ctx) const
// Paint
//
// Paint the joint
{
// Ship compartments do their own painting
if (m_fShipCompartment)
return;
// Paint based on type
switch (m_iType)
{
case jointRod:
case jointSpine:
{
int xFrom, yFrom;
Ctx.XForm.Transform(m_P1.pObj->GetPos(), &xFrom, &yFrom);
int xTo, yTo;
Ctx.XForm.Transform(m_P2.pObj->GetPos(), &xTo, &yTo);
Dest.DrawLine(xFrom, yFrom, xTo, yTo, 2, CG32bitPixel(255, 255, 128));
break;
}
}
}
CObjectJoint::ETypes CObjectJoint::ParseType (const CString &sValue)
// ParseType
//
// Returns a type
{
if (strEquals(sValue, TYPE_HINGE))
return jointHinge;
else if (strEquals(sValue, TYPE_ROD))
return jointRod;
else if (strEquals(sValue, TYPE_SPINE))
return jointSpine;
else
return jointNone;
}
void CObjectJoint::ReadFromStream (SLoadCtx &Ctx, SAttachPoint &Point)
// ReadFromStream
//
// Reads the attach point
{
DWORD dwLoad;
Ctx.pSystem->ReadObjRefFromStream(Ctx, &Point.pObj);
Point.Pos.ReadFromStream(Ctx);
Ctx.pStream->Read(dwLoad);
Point.iMinArc = (int)LOWORD(dwLoad);
Point.iMaxArc = (int)HIWORD(dwLoad);
}
void CObjectJoint::SetNextJoint (CSpaceObject *pObj, CObjectJoint *pNext)
// SetNextJoint
//
// Sets the next joint for the given object.
{
if (m_P1.pObj == pObj)
m_P1.pNext = pNext;
else if (m_P2.pObj == pObj)
m_P2.pNext = pNext;
}
void CObjectJoint::SetObjListPaintNeeded (CSpaceObject *pObj, bool bValue)
// SetObjListPaintNeeded
//
// Sets paint needed flag for all joints for the given object.
{
CObjectJoint *pNext = this;
while (pNext)
{
pNext->SetPaintNeeded(bValue);
pNext = pNext->GetNextJoint(pObj);
}
}
void CObjectJoint::Update (SUpdateCtx &Ctx)
// Update
//
// Updates the joint every tick
{
// If this is a ship compartment, we rotation the ship to face the attach
// point.
//
// NOTE: We assume that Obj2 always faces Obj1.
if (m_fShipCompartment)
{
CShip *pAttachedShip = m_P2.pObj->AsShip();
if (pAttachedShip)
{
CVector vToAttachPoint = m_P1.pObj->GetPos() - m_P2.pObj->GetPos();
pAttachedShip->SetRotation(VectorToPolar(vToAttachPoint));
}
}
}
void CObjectJoint::WriteToStream (CSystem *pSystem, IWriteStream &Stream)
// WriteToStream
//
// Writes out the joint to the stream.
//
// DWORD m_iType
// DWORD m_dwID
// DWORD m_iTick
// DWORD m_iLifetime
// DWORD m_dwFlags
//
// DWORD m_iMaxLength
//
// DWORD m_P1.pObj
// C3DObjectPos m_P1.Pos
// DWORD m_P1.iMinArc and m_P1.iMaxArc
//
// DWORD m_P2.pObj
// C3DObjectPos m_P2.Pos
// DWORD m_P2.iMinArc and m_P1.iMaxArc
{
Stream.Write((int)m_iType);
Stream.Write(m_dwID);
Stream.Write(m_iTick);
Stream.Write(m_iLifetime);
DWORD dwFlags = 0;
dwFlags |= (m_fShipCompartment ? 0x00000001 : 0);
Stream.Write(dwFlags);
Stream.Write(m_iMaxLength);
WriteToStream(pSystem, Stream, m_P1);
WriteToStream(pSystem, Stream, m_P2);
}
void CObjectJoint::WriteToStream (CSystem *pSystem, IWriteStream &Stream, const SAttachPoint &Point)
// WriteToStream
//
// Writes an attach point
{
pSystem->WriteObjRefToStream(Point.pObj, &Stream);
Point.Pos.WriteToStream(Stream);
DWORD dwSave = MAKELONG(Point.iMinArc, Point.iMaxArc);
Stream.Write(dwSave);
}