mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
876 lines
28 KiB
C++
876 lines
28 KiB
C++
/* Copyright 2003-2008 ROBLOX Corporation, All Rights Reserved */
|
|
|
|
#include "PhysicsReceiver.h"
|
|
|
|
#include "Compressor.h"
|
|
#include "RakPeerInterface.h"
|
|
#include "NetworkSettings.h"
|
|
|
|
#include "Streaming.h"
|
|
#include "Replicator.h"
|
|
#include "Util.h"
|
|
|
|
#include "V8DataModel/PartInstance.h"
|
|
#include "V8DataModel/Workspace.h"
|
|
#include "V8DataModel/PhysicsService.h"
|
|
|
|
#include "V8World/World.h"
|
|
#include "V8World/Primitive.h"
|
|
#include "V8World/Assembly.h"
|
|
#include "V8World/Joint.h"
|
|
#include "v8world/Mechanism.h"
|
|
|
|
#include "util/standardout.h"
|
|
|
|
#include "PhysicsSender.h"
|
|
#include "NetworkProfiler.h"
|
|
|
|
#include "AppDraw/DrawAdorn.h"
|
|
#include "GfxBase/Adorn.h"
|
|
#include <stack>
|
|
#include "util/MovementHistory.h"
|
|
#include "util/VarInt.h"
|
|
|
|
using namespace RBX;
|
|
using namespace RBX::Network;
|
|
|
|
DYNAMIC_FASTFLAG(HumanoidFloorPVUpdateSignal)
|
|
DYNAMIC_FASTINTVARIABLE(DebugMovementPathNumTotalWayPoint, 1000)
|
|
DYNAMIC_FASTFLAG(SimpleHermiteSplineInterpolate)
|
|
|
|
namespace RBX { namespace Network {
|
|
namespace PathBasedMovementDebug
|
|
{
|
|
NodeDebugInfo FirstNode = {Color3::blue(), 13, false};
|
|
NodeDebugInfo CompressedBaselineCFrame = {Color3::orange(), 12, true};
|
|
NodeDebugInfo PathNode = {Color3::yellow(), 6, true};
|
|
NodeDebugInfo XPacketCompressedPathNode = {Color3::green(), 6, true};
|
|
NodeDebugInfo InterpolateCFrameFromNode = {Color3::white(), 10, true};
|
|
NodeDebugInfo InterpolateCFrame = {Color3::white(), 11, true};
|
|
NodeDebugInfo VelocityNode = {Color3::red(), 5, false};
|
|
NodeDebugInfo OldNode = {Color3::red(), 6, true};
|
|
NodeDebugInfo CFrame = {Color3::white(), 5, true}; // regular cframe not from path based movement
|
|
}
|
|
}}
|
|
|
|
void DeserializedTouchItem::process(Replicator& replicator)
|
|
{
|
|
if (replicator.physicsReceiver)
|
|
{
|
|
for (auto& tp: touchPairs)
|
|
{
|
|
replicator.physicsReceiver->processTouchPair(tp);
|
|
}
|
|
}
|
|
}
|
|
|
|
PhysicsReceiver::PhysicsReceiver(Replicator* replicator, bool isServer)
|
|
: replicator(replicator), iAmServer(isServer), stats(NULL), movementWaypointList(DFInt::DebugMovementPathNumTotalWayPoint)
|
|
{
|
|
setTime(Time::nowFast());
|
|
}
|
|
|
|
void PhysicsReceiver::setTime(Time now_)
|
|
{
|
|
now = now_;
|
|
}
|
|
|
|
/*
|
|
Format for a mechanism:
|
|
|
|
>> MechanismAttributes
|
|
>> PrimaryAssembly
|
|
>> done
|
|
while (!done)
|
|
{
|
|
>> ChildAssembly
|
|
>> done
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void logItem(MechanismItem& item, RakNet::Time timeStamp)
|
|
{
|
|
float y = item.getAssemblyItem(0).pv.position.translation.y;
|
|
|
|
G3D::Log::common()->printf("%u\t", timeStamp);
|
|
G3D::Log::common()->printf("%f", y);
|
|
G3D::Log::common()->println("");
|
|
}
|
|
*/
|
|
|
|
void PhysicsReceiver::receiveMechanismCFrames(RakNet::BitStream& bitStream, RakNet::Time timeStamp, const RBX::RemoteTime& sendTime)
|
|
{
|
|
NETPROFILE_START("receiveMechanismCFrames", &bitStream);
|
|
int bitStart = bitStream.GetReadOffset();
|
|
shared_ptr<PartInstance> part;
|
|
while (receivePart(part, bitStream))
|
|
{
|
|
if (part)
|
|
{
|
|
if (part->raknetTime > timeStamp) {
|
|
if (replicator->settings().printPhysicsErrors) {
|
|
RBX::StandardOut::singleton()->print(RBX::MESSAGE_INFO, "Physics-in old packet");
|
|
}
|
|
part.reset();
|
|
}
|
|
}
|
|
CoordinateFrame cf;
|
|
Velocity vel;
|
|
readCoordinateFrame(bitStream, cf);
|
|
if(DFFlag::SimpleHermiteSplineInterpolate)
|
|
{
|
|
readVelocity(bitStream, vel);
|
|
}
|
|
|
|
if (part)
|
|
{
|
|
part->setPhysics(cf);
|
|
part->addInterpolationSample(cf, vel, sendTime, now, 0);
|
|
}
|
|
}
|
|
if (stats)
|
|
{
|
|
stats->details.mechanismCFrame.increment();
|
|
stats->details.mechanismCFrameSize.sample((bitStream.GetReadOffset() - bitStart)/8);
|
|
}
|
|
NETPROFILE_END("receiveMechanismCFrames", &bitStream);
|
|
}
|
|
|
|
void PhysicsReceiver::receiveMechanism(RakNet::BitStream& bitStream, PartInstance* rootPart, MechanismItem& item, RemoteTime remoteSendTime, int& numNodesInHistory)
|
|
{
|
|
if (NetworkSettings::singleton().getTotalNumMovementWayPoint() == 123456) // magic number to clear all after each packet
|
|
{
|
|
movementWaypointList.clear();
|
|
}
|
|
|
|
NETPROFILE_START("receiveMechanism", &bitStream);
|
|
int bitStart = bitStream.GetReadOffset();
|
|
item.reset();
|
|
|
|
readMechanismAttributes(bitStream, item);
|
|
readMovementHistory(bitStream, remoteSendTime, rootPart, item, numNodesInHistory);
|
|
if (iAmServer) // client always sends assembly only
|
|
{
|
|
readAssembly(bitStream, rootPart, item, false);
|
|
}
|
|
bool done;
|
|
|
|
bitStream >> done;
|
|
|
|
while (!done)
|
|
{
|
|
shared_ptr<PartInstance> part;
|
|
bool ok = receivePart(part, bitStream);
|
|
RBXASSERT(ok);
|
|
|
|
readAssembly(bitStream, part.get(), item, false);
|
|
|
|
bitStream >> done;
|
|
}
|
|
|
|
if (stats)
|
|
{
|
|
stats->details.mechanism.increment();
|
|
stats->details.mechanismSize.sample((bitStream.GetReadOffset() - bitStart)/8);
|
|
}
|
|
NETPROFILE_END("receiveMechanism", &bitStream);
|
|
}
|
|
|
|
void PhysicsReceiver::readMovementHistory(RakNet::BitStream& bitStream, RemoteTime remoteSendTime, PartInstance* rootPart, MechanismItem& mechanismItem, int& numNodesInHistory)
|
|
{
|
|
numNodesInHistory = 0;
|
|
|
|
if (iAmServer)
|
|
{
|
|
// client always sends assembly only
|
|
return;
|
|
}
|
|
NETPROFILE_START("readMovementHistory", &bitStream);
|
|
bool hasMovement;
|
|
unsigned int numNode;
|
|
uint8_t precisionLevel, timeInterval2Ms;
|
|
int8_t x, y, z;
|
|
float timeInterval, timeToEnd;
|
|
bitStream >> hasMovement;
|
|
if (hasMovement)
|
|
{
|
|
CoordinateFrame cf;
|
|
bool crossPacketCompression;
|
|
bitStream >> crossPacketCompression;
|
|
readAssembly(bitStream, rootPart, mechanismItem, crossPacketCompression);
|
|
if (rootPart)
|
|
{
|
|
if (crossPacketCompression)
|
|
{
|
|
cf = rootPart->getLastCFrame(rootPart->getCoordinateFrame());
|
|
cf.rotation = mechanismItem.getAssemblyItem(mechanismItem.numAssemblies()-1).pv.position.rotation;
|
|
}
|
|
else
|
|
{
|
|
cf = mechanismItem.getAssemblyItem(mechanismItem.numAssemblies()-1).pv.position;
|
|
}
|
|
rootPart->setLastCFrame(cf);
|
|
}
|
|
|
|
//bitStream >> numNode;
|
|
VarInt<>::decode<RakNet::BitStream>(bitStream, &numNode);
|
|
|
|
if (numNode > 0)
|
|
{
|
|
nodeStack.clear(); // Moved to header in order to avoid reallocation overhead
|
|
|
|
Vector3 nodeTrans = cf.translation;
|
|
AssemblyItem& assemblyItem = mechanismItem.getAssemblyItem(mechanismItem.numAssemblies()-1);
|
|
timeToEnd = 0.f;
|
|
for (uint8_t i=0; i<(uint8_t)numNode; i++)
|
|
{
|
|
bitStream >> precisionLevel;
|
|
bitStream >> x;
|
|
bitStream >> y;
|
|
bitStream >> z;
|
|
bitStream >> timeInterval2Ms;
|
|
|
|
// reconstruct the time
|
|
timeInterval = MovementHistory::getSecFrom2Ms(timeInterval2Ms);
|
|
// reconstruct the cframe
|
|
Vector3 delta;
|
|
delta.x = MovementHistory::decompress(x, precisionLevel);
|
|
delta.y = MovementHistory::decompress(y, precisionLevel);
|
|
delta.z = MovementHistory::decompress(z, precisionLevel);
|
|
nodeTrans = nodeTrans - delta;
|
|
CoordinateFrame nodeCFrame(cf.rotation, nodeTrans);
|
|
|
|
bool isBaselineNode = false;
|
|
if (crossPacketCompression)
|
|
{
|
|
if (i==0)
|
|
{
|
|
// this node is the current CFrame
|
|
assemblyItem.pv.position.translation = nodeCFrame.translation;
|
|
if (rootPart)
|
|
{
|
|
RBXASSERT(numNode > 1); // we should have at least two nodes: a baseline node and a path node
|
|
rootPart->setLastCFrame(nodeCFrame);
|
|
isBaselineNode = true;
|
|
//addVectorAdorn(cf.translation, nodeCFrame.translation, Color3::purple());
|
|
}
|
|
}
|
|
else if (i==1)
|
|
{
|
|
// Calculate the linear velocity based on last delta
|
|
assemblyItem.pv.velocity.linear = delta / timeInterval;
|
|
if (NetworkSettings::singleton().getTotalNumMovementWayPoint()%7 == 0) // magic number to print velocity
|
|
{
|
|
addWayPointAdorn(nodeCFrame.translation + assemblyItem.pv.velocity.linear, PathBasedMovementDebug::VelocityNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isBaselineNode)
|
|
{
|
|
TimedCF timedCf;
|
|
timedCf.cf = nodeCFrame;
|
|
timeToEnd += timeInterval;
|
|
timedCf.timeToEnd = timeToEnd;
|
|
nodeStack.push_back(timedCf);
|
|
}
|
|
else
|
|
{
|
|
addWayPointAdorn(nodeCFrame.translation, PathBasedMovementDebug::CompressedBaselineCFrame);
|
|
}
|
|
}
|
|
if (rootPart)
|
|
{
|
|
//StandardOut::singleton()->printf(MESSAGE_INFO, "-------------- %d", crossPacketCompression);
|
|
int numNodesAhead = nodeStack.size();
|
|
numNodesInHistory = numNodesAhead;
|
|
|
|
while (nodeStack.size()>0)
|
|
{
|
|
TimedCF timedCf = nodeStack.back();
|
|
RemoteTime nodeTime = remoteSendTime - Time::Interval(timedCf.timeToEnd);
|
|
if (replicator->remoteRaknetTimeToLocalRbxTime(nodeTime) >= rootPart->getLastUpdateTime()) // only process the new nodes
|
|
{
|
|
if(DFFlag::SimpleHermiteSplineInterpolate)
|
|
rootPart->addInterpolationSample(timedCf.cf, mechanismItem.getAssemblyItem(mechanismItem.numAssemblies()-1).pv.velocity , nodeTime, replicator->remoteRaknetTimeToLocalRbxTime(remoteSendTime), timedCf.timeToEnd, numNodesAhead);
|
|
else
|
|
rootPart->addInterpolationSample(timedCf.cf, Velocity() , nodeTime, replicator->remoteRaknetTimeToLocalRbxTime(remoteSendTime), timedCf.timeToEnd, numNodesAhead);
|
|
|
|
addWayPointAdorn(timedCf.cf.translation, crossPacketCompression ? PathBasedMovementDebug::XPacketCompressedPathNode : PathBasedMovementDebug::PathNode);
|
|
}
|
|
else
|
|
{
|
|
if (replicator->settings().printPhysicsErrors) {
|
|
RBX::StandardOut::singleton()->print(RBX::MESSAGE_INFO, "Discard old node *****");
|
|
|
|
addWayPointAdorn(timedCf.cf.translation, PathBasedMovementDebug::OldNode);
|
|
}
|
|
}
|
|
|
|
nodeStack.pop_back();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssemblyItem& assemblyItem = mechanismItem.appendAssembly();
|
|
assemblyItem.rootPart = shared_from<PartInstance>(rootPart);
|
|
if (rootPart)
|
|
{
|
|
assemblyItem.pv.position = rootPart->getCoordinateFrame();
|
|
assemblyItem.pv.velocity = rootPart->getVelocity();
|
|
}
|
|
readMotorAngles(bitStream, assemblyItem);
|
|
}
|
|
NETPROFILE_END("readMovementHistory", &bitStream);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void PhysicsReceiver::readMechanismAttributes(RakNet::BitStream& bitStream, MechanismItem& historyItem)
|
|
{
|
|
NETPROFILE_START("readMechanismAttributes", &bitStream);
|
|
historyItem.hasVelocity = replicator->settings().distributedPhysicsEnabled;
|
|
|
|
bool hasState;
|
|
bitStream >> hasState;
|
|
if (hasState) {
|
|
bitStream >> historyItem.networkHumanoidState;
|
|
}
|
|
else {
|
|
historyItem.networkHumanoidState = 0;
|
|
}
|
|
NETPROFILE_END("readMechanismAttributes", &bitStream);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void PhysicsReceiver::readAssembly(RakNet::BitStream& bitstream, PartInstance* rootPart, MechanismItem& mechanismItem, bool crossPacketCompression)
|
|
{
|
|
NETPROFILE_START("readAssembly", &bitstream);
|
|
AssemblyItem& assemblyItem = mechanismItem.appendAssembly();
|
|
assemblyItem.rootPart = shared_from<PartInstance>(rootPart);
|
|
|
|
readPV(bitstream, assemblyItem, crossPacketCompression);
|
|
if (crossPacketCompression && rootPart)
|
|
{
|
|
// reuse the last translation (for now)
|
|
assemblyItem.pv.position.translation = rootPart->getCoordinateFrame().translation;
|
|
}
|
|
|
|
readMotorAngles(bitstream, assemblyItem);
|
|
|
|
if (rootPart)
|
|
{
|
|
addWayPointAdorn(assemblyItem.pv.position.translation, PathBasedMovementDebug::FirstNode);
|
|
}
|
|
|
|
if (rootPart)
|
|
{
|
|
RBXASSERT(rootPart->getPartPrimitive()->getAssembly());
|
|
|
|
Primitive* primitive = rootPart->getPartPrimitive();
|
|
Joint* toParent = rbx_static_cast<Joint*>(primitive->getEdgeToParent());
|
|
|
|
// This joint could be null if the primitive still exists, but is no longer a child joint to parent
|
|
|
|
if (toParent) {
|
|
toParent->setPhysics(); // read on joint angles, etc.
|
|
}
|
|
}
|
|
NETPROFILE_END("readAssembly", &bitstream);
|
|
}
|
|
|
|
void PhysicsReceiver::deserializeTouches(RakNet::BitStream& bitstream, const RakNet::SystemAddress& from, std::vector<TouchPair>& touchPairs)
|
|
{
|
|
while (true)
|
|
{
|
|
TouchPair tp;
|
|
if (!deserializeTouch(bitstream, from, tp))
|
|
return;
|
|
|
|
if (tp.p1 && tp.p2)
|
|
touchPairs.push_back(tp);
|
|
}
|
|
}
|
|
|
|
bool PhysicsReceiver::deserializeTouch(RakNet::BitStream& bitstream, const RakNet::SystemAddress& from, TouchPair& touchPair)
|
|
{
|
|
shared_ptr<PartInstance> part1;
|
|
if (!receivePart(part1, bitstream))
|
|
return false; // this marks the end of the packet
|
|
|
|
shared_ptr<PartInstance> part2;
|
|
bool ok = receivePart(part2, bitstream);
|
|
RBXASSERT(ok);
|
|
|
|
bool touched;
|
|
bitstream >> touched;
|
|
|
|
if (!part1)
|
|
return true;
|
|
if (!part2)
|
|
return true;
|
|
|
|
if (replicator->settings().printTouches) {
|
|
if (touched) {
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_SENSITIVE,
|
|
"Replication: Touch:%s->%s << %s",
|
|
part1->getName().c_str(),
|
|
part2->getName().c_str(),
|
|
RakNetAddressToString(replicator->remotePlayerId).c_str()
|
|
);
|
|
} else {
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_SENSITIVE,
|
|
"Replication: Untouch:%s->%s << %s",
|
|
part1->getName().c_str(),
|
|
part2->getName().c_str(),
|
|
RakNetAddressToString(replicator->remotePlayerId).c_str()
|
|
);
|
|
}
|
|
}
|
|
|
|
touchPair = TouchPair(part1, part2, touched ? TouchPair::Touch : TouchPair::Untouch, RakNetToRbxAddress(from));
|
|
|
|
return true;
|
|
}
|
|
|
|
void PhysicsReceiver::readTouches(RakNet::BitStream& bitstream, const RakNet::SystemAddress& from)
|
|
{
|
|
while (true)
|
|
{
|
|
TouchPair tp;
|
|
if (!deserializeTouch(bitstream, from, tp))
|
|
return;
|
|
|
|
if (tp.p1 && tp.p2)
|
|
processTouchPair(tp);
|
|
}
|
|
}
|
|
|
|
void PhysicsReceiver::processTouchPair(const TouchPair& tp)
|
|
{
|
|
if (tp.type == TouchPair::Touch)
|
|
tp.p1->reportTouch(tp.p2);
|
|
else
|
|
tp.p1->reportUntouch(tp.p2);
|
|
|
|
// Send pair to other Replicators
|
|
if (!physicsService)
|
|
physicsService = shared_from(ServiceProvider::find<PhysicsService>(replicator));
|
|
|
|
physicsService->onTouchStep(tp);
|
|
}
|
|
|
|
void PhysicsReceiver::readPV(RakNet::BitStream& bitStream, AssemblyItem& item, bool crossPacketCompression)
|
|
{
|
|
NETPROFILE_START("readPV", &bitStream);
|
|
if (crossPacketCompression)
|
|
{
|
|
// readRotation only
|
|
Compressor::readRotation(bitStream, item.pv.position.rotation);
|
|
}
|
|
else
|
|
{
|
|
readCoordinateFrame(bitStream, item.pv.position);
|
|
readVelocity(bitStream, item.pv.velocity);
|
|
|
|
if (NetworkSettings::singleton().getTotalNumMovementWayPoint()%7 == 0) // magic number to print velocity
|
|
{
|
|
addWayPointAdorn(item.pv.position.translation + item.pv.velocity.linear, PathBasedMovementDebug::VelocityNode);
|
|
}
|
|
}
|
|
NETPROFILE_END("readPV", &bitStream);
|
|
}
|
|
|
|
void PhysicsReceiver::readCoordinateFrame(RakNet::BitStream& bitStream, CoordinateFrame& cFrame)
|
|
{
|
|
NETPROFILE_START("readCoordinateFrame", &bitStream);
|
|
|
|
if(stats == NULL)
|
|
{
|
|
Compressor::readTranslation(bitStream, cFrame.translation);
|
|
Compressor::readRotation(bitStream, cFrame.rotation);
|
|
NETPROFILE_END("readCoordinateFrame", &bitStream);
|
|
}
|
|
else
|
|
{
|
|
NETPROFILE_START("readTranslation", &bitStream);
|
|
int bitStart = bitStream.GetReadOffset();
|
|
Compressor::readTranslation(bitStream, cFrame.translation);
|
|
if (stats)
|
|
{
|
|
stats->details.translation.increment();
|
|
stats->details.translationSize.sample((bitStream.GetReadOffset() - bitStart)/8);
|
|
}
|
|
NETPROFILE_END("readTranslation", &bitStream);
|
|
NETPROFILE_START("readRotation", &bitStream);
|
|
bitStart = bitStream.GetReadOffset();
|
|
Compressor::readRotation(bitStream, cFrame.rotation);
|
|
if (stats)
|
|
{
|
|
stats->details.rotation.increment();
|
|
stats->details.rotationSize.sample((bitStream.GetReadOffset() - bitStart)/8);
|
|
}
|
|
NETPROFILE_END("readRotation", &bitStream);
|
|
NETPROFILE_END("readCoordinateFrame", &bitStream);
|
|
}
|
|
}
|
|
|
|
void PhysicsReceiver::readVelocity(RakNet::BitStream& bitStream, Velocity& velocity)
|
|
{
|
|
NETPROFILE_START("readVelocity", &bitStream);
|
|
int bitStart = bitStream.GetReadOffset();
|
|
if (replicator->settings().distributedPhysicsEnabled)
|
|
{
|
|
readVectorFast( bitStream, velocity.linear.x, velocity.linear.y, velocity.linear.z );
|
|
readVectorFast( bitStream, velocity.rotational.x, velocity.rotational.y, velocity.rotational.z );
|
|
}
|
|
else
|
|
{
|
|
velocity = Velocity::zero();
|
|
}
|
|
if (stats)
|
|
{
|
|
stats->details.velocity.increment();
|
|
stats->details.velocitySize.sample((bitStream.GetReadOffset() - bitStart)/8);
|
|
}
|
|
NETPROFILE_END("readVelocity", &bitStream);
|
|
}
|
|
|
|
void PhysicsReceiver::readMotorAngles(RakNet::BitStream& bitStream, AssemblyItem& item)
|
|
{
|
|
NETPROFILE_START("readMotorAngles", &bitStream);
|
|
unsigned char compactNum;
|
|
bitStream >> compactNum;
|
|
|
|
if (compactNum>50 && replicator->settings().printPhysicsErrors)
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_WARNING, "Physics-in has %d motors", compactNum);
|
|
|
|
const int numMotors = compactNum;
|
|
item.motorAngles.resize(numMotors); // i.e. - fast clear, no allocation
|
|
|
|
for (int i = 0; i < numMotors; ++i)
|
|
{
|
|
readCompactCFrame(bitStream, item.motorAngles[i]);
|
|
}
|
|
NETPROFILE_END("readMotorAngles", &bitStream);
|
|
}
|
|
|
|
void PhysicsReceiver::readCompactCFrame(RakNet::BitStream& bitStream, CompactCFrame& cFrame)
|
|
{
|
|
NETPROFILE_START("readCompactCFrame", &bitStream);
|
|
bool isSimpleZAngle = bitStream.ReadBit();
|
|
|
|
if(isSimpleZAngle)
|
|
{
|
|
unsigned char byteAngle;
|
|
bitStream >> byteAngle;
|
|
cFrame = CompactCFrame(Vector3::zero(), Vector3::unitZ(), Math::rotationFromByte(byteAngle));
|
|
RBXASSERT(!Math::isNanInfVector3(cFrame.getAxis()));
|
|
RBXASSERT(!Math::isNanInf(cFrame.getAngle()));
|
|
RBXASSERT(!Math::isNanInfVector3(cFrame.translation));
|
|
}
|
|
else
|
|
{
|
|
bool hasTranslation = bitStream.ReadBit();
|
|
bool hasRotation = bitStream.ReadBit();
|
|
if(hasTranslation)
|
|
{
|
|
Compressor::readTranslation(bitStream, cFrame.translation);
|
|
}
|
|
else
|
|
{
|
|
cFrame.translation = Vector3::zero();
|
|
}
|
|
|
|
if(hasRotation)
|
|
{
|
|
Vector3 axis;
|
|
readVectorFast( bitStream, axis.x, axis.y, axis.z );
|
|
unsigned char byteAngle;
|
|
bitStream >> byteAngle;
|
|
float angle = Math::rotationFromByte(byteAngle);
|
|
cFrame.setAxisAngle(axis, angle);
|
|
}
|
|
else
|
|
{
|
|
cFrame.setAxisAngle(RBX::Vector3::unitX(), 0);
|
|
}
|
|
|
|
RBXASSERT(!Math::isNanInfVector3(cFrame.getAxis()));
|
|
RBXASSERT(!Math::isNanInf(cFrame.getAngle()));
|
|
RBXASSERT(!Math::isNanInfVector3(cFrame.translation));
|
|
}
|
|
NETPROFILE_END("readCompactCFrame", &bitStream);
|
|
}
|
|
|
|
void PhysicsReceiver::setPhysics(const MechanismItem& item, const RBX::RemoteTime& remoteSendTime, const RakNet::TimeMS lagInMs, int numNodesInHistory)
|
|
{
|
|
bool hasVelocity = item.hasVelocity;
|
|
Time::Interval lag(static_cast<double>(lagInMs) / 1000.0f);
|
|
|
|
for (int i = 0; i < item.numAssemblies(); ++i)
|
|
{
|
|
AssemblyItem& assemblyItem = item.getAssemblyItem(i);
|
|
|
|
// assemblyItem.primitive will be NULL if the Part was destroyed while the physics packet was outstanding
|
|
|
|
if (PartInstance* part = assemblyItem.rootPart.get())
|
|
{
|
|
if (this->replicator->filterPhysics(part) == Reject)
|
|
{
|
|
if (replicator->settings().printPhysicsFilters)
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "filterPhysics %s", part->getName().c_str());
|
|
continue;
|
|
}
|
|
|
|
Primitive* primitive = part->getPartPrimitive();
|
|
|
|
// Show david this assert and then destroy - validating the case where we received this history item in the past, and the primitive is no longer in world
|
|
RBXASSERT(primitive->getWorld());
|
|
|
|
if (!Assembly::isAssemblyRootPrimitive(primitive))
|
|
{
|
|
if (replicator->settings().printPhysicsFilters)
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "!isAssemblyRootPrimitive %s", part->getName().c_str());
|
|
continue;
|
|
}
|
|
|
|
Assembly* a = primitive->getAssembly();
|
|
if (a->computeIsGrounded())
|
|
{
|
|
if (replicator->settings().printPhysicsFilters)
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "computeIsGrounded %s", part->getName().c_str());
|
|
continue;
|
|
}
|
|
|
|
a->setPhysics(assemblyItem.motorAngles, assemblyItem.pv); // motor angles
|
|
a->setNetworkHumanoidState((i == 0) ? item.networkHumanoidState : 0);
|
|
|
|
if (hasVelocity)
|
|
{
|
|
if (iAmServer)
|
|
{
|
|
part->addInterpolationSample(assemblyItem.pv.position, assemblyItem.pv.velocity, remoteSendTime, now, 0.f, numNodesInHistory);
|
|
part->setPhysics(assemblyItem.pv);
|
|
|
|
Time localTime = replicator->remoteRaknetTimeToLocalRbxTime(remoteSendTime);
|
|
part->addMovementNode(assemblyItem.pv.position, assemblyItem.pv.velocity, localTime); // force update the movement node
|
|
}
|
|
else
|
|
{
|
|
bool isLagCompenstated = false;
|
|
|
|
if (isLagCompenstated)
|
|
{
|
|
// Extrapolate with velocity
|
|
if (assemblyItem.pv.velocity.rotational.squaredLength() >= 0.01)
|
|
{
|
|
Quaternion qOrientation(assemblyItem.pv.position.rotation);
|
|
qOrientation.normalize();
|
|
Quaternion qDot(Quaternion(assemblyItem.pv.velocity.rotational) * qOrientation * 0.5f);
|
|
qOrientation += qDot * lag.seconds();
|
|
qOrientation.normalize();
|
|
qOrientation.toRotationMatrix(assemblyItem.pv.position.rotation);
|
|
}
|
|
if (assemblyItem.pv.velocity.linear.squaredLength() >= 1)
|
|
assemblyItem.pv.position.translation += assemblyItem.pv.velocity.linear * lag.seconds();
|
|
|
|
part->addInterpolationSample(assemblyItem.pv.position, assemblyItem.pv.velocity, remoteSendTime + lag, now, 0.f, numNodesInHistory);
|
|
}
|
|
else
|
|
{
|
|
part->addInterpolationSample(assemblyItem.pv.position, assemblyItem.pv.velocity, remoteSendTime, now, 0.f, numNodesInHistory);
|
|
}
|
|
|
|
CoordinateFrame oldPosition;
|
|
RBX::Velocity previousLinearVelocity;
|
|
RBX::Time lastUpdateTime;
|
|
|
|
if (DFFlag::HumanoidFloorPVUpdateSignal)
|
|
{
|
|
oldPosition = part->getCoordinateFrame();
|
|
previousLinearVelocity = part->getVelocity();
|
|
lastUpdateTime = part->getLastUpdateTime();
|
|
|
|
}
|
|
part->setPhysics(assemblyItem.pv);
|
|
|
|
if (DFFlag::HumanoidFloorPVUpdateSignal)
|
|
{
|
|
float deltaTime = (replicator->remoteRaknetTimeToLocalRbxTime(remoteSendTime) - lastUpdateTime).seconds();
|
|
//shared_ptr<PartInstance> partPointer = shared_from(part);
|
|
if (part->onDemandRead())
|
|
part->onDemandWrite()->onPositionUpdatedByNetworkSignal(boost::ref(part), boost::ref(oldPosition), boost::ref(previousLinearVelocity), boost::ref(deltaTime));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
part->addInterpolationSample(assemblyItem.pv.position, assemblyItem.pv.velocity, remoteSendTime, now, 0.f, numNodesInHistory);
|
|
CoordinateFrame oldPosition;
|
|
RBX::Velocity previousLinearVelocity;
|
|
RBX::Time lastUpdateTime;
|
|
if (DFFlag::HumanoidFloorPVUpdateSignal)
|
|
{
|
|
oldPosition = part->getCoordinateFrame();
|
|
previousLinearVelocity = part->getVelocity();
|
|
lastUpdateTime = part->getLastUpdateTime();
|
|
}
|
|
part->setPhysics(assemblyItem.pv.position);
|
|
|
|
if (DFFlag::HumanoidFloorPVUpdateSignal)
|
|
{
|
|
float deltaTime = (replicator->remoteRaknetTimeToLocalRbxTime(remoteSendTime) - lastUpdateTime).seconds();
|
|
//shared_ptr<PartInstance> partPointer = shared_from(part);
|
|
if (part->onDemandRead())
|
|
part->onDemandWrite()->onPositionUpdatedByNetworkSignal(boost::ref(part), boost::ref(oldPosition), boost::ref(previousLinearVelocity), boost::ref(deltaTime));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void PhysicsReceiver::addWayPointAdorn(const Vector3& p, const PathBasedMovementDebug::NodeDebugInfo& info, const std::string& debugText)
|
|
{
|
|
if (Workspace::showPartMovementPath && info.show)
|
|
{
|
|
if (movementWaypointList.capacity() != NetworkSettings::singleton().getTotalNumMovementWayPoint())
|
|
{
|
|
movementWaypointList.rset_capacity(NetworkSettings::singleton().getTotalNumMovementWayPoint());
|
|
}
|
|
if (movementWaypointList.capacity() > 0)
|
|
{
|
|
movementWaypointList.push_back(MovementWaypointAdorn(p, info.color, info.size, debugText));
|
|
}
|
|
}
|
|
}
|
|
|
|
void PhysicsReceiver::addVectorAdorn(const Vector3& start, const Vector3& end, const RBX::Color4& c)
|
|
{
|
|
if (Workspace::showPartMovementPath)
|
|
{
|
|
if (movementVectorList.capacity() != NetworkSettings::singleton().getTotalNumMovementWayPoint())
|
|
{
|
|
movementVectorList.rset_capacity(NetworkSettings::singleton().getTotalNumMovementWayPoint());
|
|
}
|
|
if (movementVectorList.capacity() > 0)
|
|
{
|
|
movementVectorList.push_back(MovementVectorAdorn(start, end, c));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool PhysicsReceiver::okDistributedReceivePart(const shared_ptr<PartInstance>& part)
|
|
{
|
|
return ( !replicator->settings().distributedPhysicsEnabled
|
|
|| replicator->checkDistributedReceive(part.get()) );
|
|
}
|
|
|
|
|
|
bool PhysicsReceiver::receiveRootPart(shared_ptr<PartInstance>& part, RakNet::BitStream& inBitstream)
|
|
{
|
|
NETPROFILE_START("receiveRootPart", &inBitstream);
|
|
bool answer = receivePart(part, inBitstream);
|
|
|
|
if (part)
|
|
{
|
|
if (part->getPartPrimitive()->getAssembly()->computeIsGrounded())
|
|
{
|
|
part.reset();
|
|
}
|
|
else if (!okDistributedReceivePart(part))
|
|
{
|
|
part.reset();
|
|
}
|
|
}
|
|
NETPROFILE_END("receiveRootPart", &inBitstream);
|
|
return answer;
|
|
}
|
|
|
|
|
|
bool PhysicsReceiver::receivePart(shared_ptr<PartInstance>& part, RakNet::BitStream& inBitstream)
|
|
{
|
|
shared_ptr<Instance> instance;
|
|
RBX::Guid::Data id;
|
|
if (replicator->deserializeInstanceRef(inBitstream, instance, id))
|
|
{
|
|
if (instance==NULL) {
|
|
return false; // packet end tag
|
|
}
|
|
part = Instance::fastSharedDynamicCast<PartInstance>(instance);
|
|
}
|
|
else
|
|
{
|
|
if (replicator->settings().printPhysicsErrors) {
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_WARNING,
|
|
"Physics-in of unidentified %s", id.readableString().c_str());
|
|
}
|
|
}
|
|
|
|
if (part)
|
|
{
|
|
if (!PartInstance::nonNullInWorkspace(part))
|
|
{
|
|
if (replicator->settings().printPhysicsErrors) {
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO,
|
|
"Physics-in of part not in workspace %s", id.readableString().c_str());
|
|
}
|
|
part.reset();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void PhysicsReceiver::renderPartMovementPath(Adorn* adorn)
|
|
{
|
|
|
|
for (boost::circular_buffer<MovementWaypointAdorn>::iterator iter = movementWaypointList.begin(); iter != movementWaypointList.end(); iter++)
|
|
{
|
|
MovementWaypointAdorn wp = *iter;
|
|
DrawAdorn::star(adorn, wp.position, wp.size, wp.color, wp.color, wp.color);
|
|
|
|
if (wp.text.length() > 0)
|
|
{
|
|
Vector3 aboveStar = wp.position;
|
|
aboveStar.y += 5.0f;
|
|
|
|
const Camera& camera = *adorn->getCamera();
|
|
Vector3 screenLoc = camera.project(aboveStar);
|
|
if(screenLoc.z == std::numeric_limits<float>::infinity())
|
|
continue;
|
|
|
|
adorn->drawFont2D(
|
|
wp.text,
|
|
screenLoc.xy(),
|
|
10.0f,
|
|
false,
|
|
Color4(Color3::white(), 1.0f),
|
|
Color4(Color3::black(), 1.0f),
|
|
Text::FONT_ARIALBOLD,
|
|
Text::XALIGN_CENTER,
|
|
Text::YALIGN_BOTTOM );
|
|
}
|
|
}
|
|
|
|
for (boost::circular_buffer<MovementVectorAdorn>::iterator iter = movementVectorList.begin(); iter != movementVectorList.end(); iter++)
|
|
{
|
|
MovementVectorAdorn wp = *iter;
|
|
adorn->line3d(wp.startPos, wp.endPos, wp.color);
|
|
}
|
|
}
|
|
|