Networking floats
by Gary "ChunkyKs" Briggs · in Torque Game Engine · 05/03/2007 (3:07 pm) · 5 replies
According to bitStream.h, BitStream::readFloat, writeFloat, /et al/, are for floats in the range 0..1.
I have code thus:
The problem is, that my floats typically aren't in the range 0..1. The mass is thus:
The fourth entry in each row of I is deliberately ignored.
How should I be sending this structure across a network? It only needs to be done once. Entries in it are typically zero, 10, 100.
According to my debugger, mass.* in packUpdate has sensible values.
When that unpack runs, I get nothing but NANs.
How should I go about doing this properly? Eventually, this stuff will go in a datablock, but I'll still need to transmit it across the network.
Thanks,
Gary (-;
I have code thus:
#define _I(i,j) I[(i)*4+(j)]
U32 tdMass::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
{
U32 ret = Parent::packUpdate(conn, mask, stream);
if(stream->writeFlag(mask&NewMass)) {
int i,j;
// Total mass of rigid body
stream->writeFloat(mass.mass,32);
// center of gravity position in body frame (x,y,z)
for(i=0;i<=3;i++) {
stream->writeFloat(mass.c[i],32);
}
// 3x3 inertia tensor in body frame, about POR
for(i=0;i<3;i++) {
for(j=0;j<3;j++) {
stream->writeFloat(mass._I(i,j),32);
}
}
}
return ret;
}
void tdMass::unpackUpdate(NetConnection *conn, BitStream *stream)
{
Parent::unpackUpdate(conn, stream);
if(stream->readFlag()) {
int i,j;
mass.mass = stream->readFloat(32);
for(i=0;i<=3;i++) {
mass.c[i] = stream->readFloat(32);
}
for(i=0;i<3;i++) {
for(j=0;j<3;j++) {
mass._I(i,j) = stream->readFloat(32);
}
}
}
}The problem is, that my floats typically aren't in the range 0..1. The mass is thus:
// dReal is typed to either a float or a double
typedef dReal dVector4[4];
typedef dReal dMatrix3[4*3];
typedef struct dMass {
dReal mass; // total mass of the rigid body
dVector4 c; // center of gravity position in body frame (x,y,z)
dMatrix3 I; // 3x3 inertia tensor in body frame, about POR
} dMass;The fourth entry in each row of I is deliberately ignored.
How should I be sending this structure across a network? It only needs to be done once. Entries in it are typically zero, 10, 100.
According to my debugger, mass.* in packUpdate has sensible values.
When that unpack runs, I get nothing but NANs.
How should I go about doing this properly? Eventually, this stuff will go in a datablock, but I'll still need to transmit it across the network.
Thanks,
Gary (-;
#2
And from bitstream:
Gary (-;
05/03/2007 (4:31 pm)
That feels somewhat hacky; what if my mass accidentally ends up bigger than my divisor? Choosing a better divisor is hard...And from bitstream:
// read and write floats... floats are 0 to 1 inclusive, signed floats are -1 to 1 inclusive F32 readFloat(S32 bitCount); F32 readSignedFloat(S32 bitCount); void writeFloat(F32 f, S32 bitCount); void writeSignedFloat(F32 f, S32 bitCount);
Gary (-;
#3
And this appears in the console:
I don't really understand what's going on here. Thanks for the suggestion, though. It's what I was kinda hoping to avoid, but if that's the blessed way of doing it, then so be it :-)
Gary (-;
05/03/2007 (4:45 pm)
Hm. So I tried that:#define _I(i,j) I[(i)*4+(j)]
// Constant divisor. If your masses are bigger than this, you have a problem
#define MASS_DIVISOR 256.0F
U32 tdMass::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
{
U32 ret = Parent::packUpdate(conn, mask, stream);
if(stream->writeFlag(mask&NewMass)) {
int i,j;
// Total mass of rigid body
stream->writeFloat(mass.mass/MASS_DIVISOR,32);
Con::printf("Wrote mass.mass: %f", i, mass.mass/MASS_DIVISOR);
// center of gravity position in body frame (x,y,z)
for(i=0;i<=3;i++) {
stream->writeFloat(mass.c[i]/MASS_DIVISOR,32);
Con::printf("Wrote mass.c[%i]: %f", i, mass.c[i]/MASS_DIVISOR);
}
// 3x3 inertia tensor in body frame, about POR
for(i=0;i<3;i++) {
for(j=0;j<3;j++) {
stream->writeFloat(mass._I(i,j)/MASS_DIVISOR,32);
Con::printf("Wrote mass._I(%i,%i): %f", i, j, mass._I(i,j)/MASS_DIVISOR);
}
}
}
return ret;
}
void tdMass::unpackUpdate(NetConnection *conn, BitStream *stream)
{
Parent::unpackUpdate(conn, stream);
if(stream->readFlag()) {
int i,j;
mass.mass = stream->readFloat(32) * MASS_DIVISOR;
Con::printf("Read mass.mass: %f", mass.mass);
for(i=0;i<=3;i++) {
mass.c[i] = stream->readFloat(32) * MASS_DIVISOR;
Con::printf("Read mass.c[%i]: %f", i, mass.c[i]);
}
for(i=0;i<3;i++) {
for(j=0;j<3;j++) {
mass._I(i,j) = stream->readFloat(32) * MASS_DIVISOR;
Con::printf("Read mass._I(%i,%i): %f", i, j, mass._I(i,j));
}
}
}
}And this appears in the console:
Quote:Wrote mass.mass: 0.011719
Wrote mass.c[0]: 0.000000
Wrote mass.c[1]: 0.000000
Wrote mass.c[2]: 0.000000
Wrote mass.c[3]: 0.000000
Wrote mass._I(0,0): 0.042188
Wrote mass._I(0,1): 0.000000
Wrote mass._I(0,2): 0.000000
Wrote mass._I(1,0): 0.000000
Wrote mass._I(1,1): 0.042188
Wrote mass._I(1,2): 0.000000
Wrote mass._I(2,0): 0.000000
Wrote mass._I(2,1): 0.000000
Wrote mass._I(2,2): 0.042188
Read mass.mass: nan
Read mass.c[0]: nan
Read mass.c[1]: nan
Read mass.c[2]: nan
Read mass.c[3]: nan
Read mass._I(0,0): nan
Read mass._I(0,1): nan
Read mass._I(0,2): nan
Read mass._I(1,0): nan
Read mass._I(1,1): nan
Read mass._I(1,2): nan
Read mass._I(2,0): nan
Read mass._I(2,1): nan
Read mass._I(2,2): nan
==>quit();
I don't really understand what's going on here. Thanks for the suggestion, though. It's what I was kinda hoping to avoid, but if that's the blessed way of doing it, then so be it :-)
Gary (-;
#4
BitStream derives from Stream, Stream implements a write(F32) method. Thus, you can just do stream->write(myF32); / stream->read(&myF32);
The method in BitStream you mention is just a specialised optimization for normalized floats.
T.
05/03/2007 (5:00 pm)
I told you this on IRC, but if anyone else is wondering ...BitStream derives from Stream, Stream implements a write(F32) method. Thus, you can just do stream->write(myF32); / stream->read(&myF32);
The method in BitStream you mention is just a specialised optimization for normalized floats.
T.
#5
Gives this:
Thanks Tom & Stephen!
Gary (-;
05/03/2007 (5:05 pm)
Woo! Thank-you :-)// Next line ripped from ode's mass.cpp
#define _I(i,j) I[(i)*4+(j)]
U32 tdMass::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
{
U32 ret = Parent::packUpdate(conn, mask, stream);
if(stream->writeFlag(mask&NewMass)) {
int i,j;
// Total mass of rigid body
stream->write((F32)mass.mass);
Con::printf("Wrote mass.mass: %f", mass.mass);
// center of gravity position in body frame (x,y,z)
for(i=0;i<=3;i++) {
stream->write((F32)mass.c[i]);
Con::printf("Wrote mass.c[%i]: %f", i, mass.c[i]);
}
// 3x3 inertia tensor in body frame, about POR
for(i=0;i<3;i++) {
for(j=0;j<3;j++) {
stream->write((F32)mass._I(i,j));
Con::printf("Wrote mass._I(%i,%i): %f", i, j, mass._I(i,j));
}
}
}
return ret;
}
void tdMass::unpackUpdate(NetConnection *conn, BitStream *stream)
{
Parent::unpackUpdate(conn, stream);
if(stream->readFlag()) {
int i,j;
stream->read((F32 *)&mass.mass);
Con::printf("Read mass.mass: %f", mass.mass);
for(i=0;i<=3;i++) {
stream->read((F32 *)&mass.c[i]);
Con::printf("Read mass.c[%i]: %f", i, mass.c[i]);
}
for(i=0;i<3;i++) {
for(j=0;j<3;j++) {
stream->read((F32 *)&mass._I(i,j));
Con::printf("Read mass._I(%i,%i): %f", i, j, mass._I(i,j));
}
}
}
}Gives this:
Quote:Wrote mass.mass: 3.000000
Wrote mass.c[0]: 0.000000
Wrote mass.c[1]: 0.000000
Wrote mass.c[2]: 0.000000
Wrote mass.c[3]: 0.000000
Wrote mass._I(0,0): 10.800000
Wrote mass._I(0,1): 0.000000
Wrote mass._I(0,2): 0.000000
Wrote mass._I(1,0): 0.000000
Wrote mass._I(1,1): 10.800000
Wrote mass._I(1,2): 0.000000
Wrote mass._I(2,0): 0.000000
Wrote mass._I(2,1): 0.000000
Wrote mass._I(2,2): 10.800000
Read mass.mass: 3.000000
Read mass.c[0]: 0.000000
Read mass.c[1]: 0.000000
Read mass.c[2]: 0.000000
Read mass.c[3]: 0.000000
Read mass._I(0,0): 10.800000
Read mass._I(0,1): 0.000000
Read mass._I(0,2): 0.000000
Read mass._I(1,0): 0.000000
Read mass._I(1,1): 10.800000
Read mass._I(1,2): 0.000000
Read mass._I(2,0): 0.000000
Read mass._I(2,1): 0.000000
Read mass._I(2,2): 10.800000
==>quit();
Thanks Tom & Stephen!
Gary (-;
Torque 3D Owner Stephen Zepp
In unpackUpdate, simply "un-normalize" the value back to it's original state by multiplying by the constant.
IIRC, you also do not give a number after the float value like you do with ints...that second parameter is used with ints for saying how many bits should be used in the bitstream, which is not applicable for write/readFloat.