- New events: ObjectPhysicsDataEvent, ParcelPropertiesEvent, NewObjectEvent, ObjectUpdateEvent, ObjectKilledEvent - Added getXML function to Color4, Vector2, Vector3, Vector4, GameObject, Region, Quaternion, UUID for opensim-compatible XML export - Added TextureAnim and ParticleSystem decoding to the "full" ObjectStore - Object store will automatically request missing "parent" prims - "setPersist" - When persist is TRUE, the ObjectStore will not forget about "killed" prims - useful for region scanning - Support for Flexible params, Light params, LightImage params, Mesh data, Sculpt maps - Fixed object scale being incorrectly calculated - Add terrain decoding (this was a ballache) - Add parcel map decoding - Add support for region windlight settings (region.environment) - Add support for materials (normal / specular maps) - Add getBuffer, getLong and bitwiseOr to UUID - Added a circular-reference-safe JSONStringify to Utils - Add XferFile capability to Circuit PUBLIC API: AssetCommands: - Rework "downloadAsset" to detect failures - NEW: downloadInventoryAsset() - uses TransferRequest for prim inventory items - NEW: getMaterials() - resolves material UUIDs RegionCommands: - NEW: getTerrainTextures() - NEW: exportSettings() - OpenSim XML export of region settings - NEW: async getTerrain() - Get binary terrain heightmap, 256x256 float32 - resolveObjects() - now fetches task inventory contents too. - resolveObjects() - fix calculation of land impact - NEW: getObjectByLocalID(localID: number, timeout: number) - NEW: getObjectByUUID(uuid: UUID, timeout: number) - NEW: getParcels(); - NEW: pruneObjects - removes missing GameObjects from a list - NEW: setPersist - prevent objectstore from forgetting about killed gameobjects
365 lines
12 KiB
TypeScript
365 lines
12 KiB
TypeScript
import {TextureEntryFace} from './TextureEntryFace';
|
|
import {UUID} from './UUID';
|
|
import {Color4} from './Color4';
|
|
import {Utils} from './Utils';
|
|
|
|
export class TextureEntry
|
|
{
|
|
defaultTexture: TextureEntryFace | null;
|
|
faces: TextureEntryFace[] = [];
|
|
binary: Buffer;
|
|
|
|
static readFaceBitfield(buf: Buffer, pos: number): {
|
|
result: boolean,
|
|
pos: number,
|
|
faceBits: number,
|
|
bitfieldSize: number
|
|
}
|
|
{
|
|
const result = {
|
|
result: false,
|
|
pos: pos,
|
|
faceBits: 0,
|
|
bitfieldSize: 0
|
|
};
|
|
if (result.pos >= buf.length)
|
|
{
|
|
return result;
|
|
}
|
|
let b = 0;
|
|
do
|
|
{
|
|
b = buf.readUInt8(result.pos);
|
|
result.faceBits = (result.faceBits << 7) | (b & 0x7F);
|
|
result.bitfieldSize += 7;
|
|
result.pos++;
|
|
}
|
|
while ((b & 0x80) !== 0);
|
|
result.result = (result.faceBits !== 0);
|
|
return result;
|
|
}
|
|
|
|
constructor(buf: Buffer)
|
|
{
|
|
this.binary = buf;
|
|
if (buf.length < 16)
|
|
{
|
|
this.defaultTexture = null;
|
|
}
|
|
else
|
|
{
|
|
this.defaultTexture = new TextureEntryFace(null);
|
|
const pos = 0;
|
|
let i = pos;
|
|
|
|
// Texture
|
|
{
|
|
this.defaultTexture.textureID = new UUID(buf, i);
|
|
i += 16;
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const uuid = new UUID(buf, i);
|
|
i += 16;
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].textureID = uuid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Colour
|
|
{
|
|
this.defaultTexture.rgba = new Color4(buf, i, true);
|
|
i += 4;
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpColor = new Color4(buf, i, true);
|
|
i += 4;
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].rgba = tmpColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// RepeatU
|
|
{
|
|
this.defaultTexture.repeatU = buf.readFloatLE(i);
|
|
i += 4;
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpFloat = buf.readFloatLE(i);
|
|
i += 4;
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].repeatU = tmpFloat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// RepeatV
|
|
{
|
|
this.defaultTexture.repeatV = buf.readFloatLE(i);
|
|
i += 4;
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpFloat = buf.readFloatLE(i);
|
|
i += 4;
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].repeatV = tmpFloat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// OffsetU
|
|
{
|
|
this.defaultTexture.offsetU = Utils.ReadOffsetFloat(buf, i);
|
|
i += 2;
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpFloat = Utils.ReadOffsetFloat(buf, i);
|
|
i += 2;
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].offsetU = tmpFloat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// OffsetV
|
|
{
|
|
this.defaultTexture.offsetV = Utils.ReadOffsetFloat(buf, i);
|
|
i += 2;
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpFloat = Utils.ReadOffsetFloat(buf, i);
|
|
i += 2;
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].offsetV = tmpFloat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rotation
|
|
{
|
|
this.defaultTexture.rotation = Utils.ReadRotationFloat(buf, i);
|
|
i += 2;
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpFloat = Utils.ReadRotationFloat(buf, i);
|
|
i += 2;
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].rotation = tmpFloat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Material
|
|
{
|
|
this.defaultTexture.material = buf[i++];
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpByte = buf[i++];
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].material = tmpByte;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Media
|
|
{
|
|
this.defaultTexture.media = buf[i++];
|
|
|
|
let done = false;
|
|
while (i - pos < buf.length && !done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpByte = buf[i++];
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].media = tmpByte;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Glow
|
|
{
|
|
this.defaultTexture.glow = Utils.ReadGlowFloat(buf, i++);
|
|
|
|
let done = false;
|
|
while (!done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const tmpFloat = Utils.ReadGlowFloat(buf, i++);
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].glow = tmpFloat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MaterialID
|
|
{
|
|
const len = i - pos + 16;
|
|
if (i - pos + 16 <= buf.length)
|
|
{
|
|
this.defaultTexture.materialID = new UUID(buf, i);
|
|
i += 16;
|
|
|
|
let done = false;
|
|
while (i - pos + 16 <= buf.length && !done)
|
|
{
|
|
const result = TextureEntry.readFaceBitfield(buf, i);
|
|
done = !result.result;
|
|
i = result.pos;
|
|
if (!done)
|
|
{
|
|
const uuid = new UUID(buf, i);
|
|
i += 16;
|
|
for (let face = 0, bit = 1; face < result.bitfieldSize; face++, bit <<= 1)
|
|
{
|
|
if ((result.faceBits & bit) !== 0)
|
|
{
|
|
this.createFace(face);
|
|
this.faces[face].materialID = uuid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private createFace(face: number)
|
|
{
|
|
if (face > 32)
|
|
{
|
|
console.error('Warning: Face number exceeds maximum number of faces: 32');
|
|
}
|
|
while (this.faces.length <= face)
|
|
{
|
|
this.faces.push(new TextureEntryFace(this.defaultTexture));
|
|
}
|
|
}
|
|
}
|