- Mesh upload support
- LLMesh asset decoding and encoding (inc. LLPhysicsConvex, LLSkin, LLSubMesh) - Query inventory folder by type - onSelectedObject event - fetchInventoryItem command - Fix packing/unpacking of object shape - Time sync with SimulatorViewerTimeMessage - Changed several classes to a .from style rather than setting up in the constructor (exception friendly) - Whole bunch of other improvements - Object building
This commit is contained in:
@@ -34,4 +34,10 @@ export class FlexibleData
|
||||
buf[pos++] = (this.Wind) * 10;
|
||||
this.Force.writeToBuffer(buf, pos, false);
|
||||
}
|
||||
getBuffer(): Buffer
|
||||
{
|
||||
const buf = Buffer.allocUnsafe(16);
|
||||
this.writeToBuffer(buf, 0);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@ import * as Long from 'long';
|
||||
import {IGameObjectData} from '../interfaces/IGameObjectData';
|
||||
import {
|
||||
HoleType,
|
||||
HTTPAssets,
|
||||
HTTPAssets, PacketFlags,
|
||||
PCode,
|
||||
PhysicsShapeType,
|
||||
PrimFlags,
|
||||
ProfileShape, SculptType,
|
||||
ProfileShape,
|
||||
SculptType,
|
||||
SoundFlags,
|
||||
Utils
|
||||
} from '../..';
|
||||
@@ -29,6 +30,16 @@ import {InventoryType} from '../../enums/InventoryType';
|
||||
import {LLWearable} from '../LLWearable';
|
||||
import {TextureAnim} from './TextureAnim';
|
||||
import {ExtraParams} from './ExtraParams';
|
||||
import {ObjectExtraParamsMessage} from '../messages/ObjectExtraParams';
|
||||
import {ExtraParamType} from '../../enums/ExtraParamType';
|
||||
import {ObjectImageMessage} from '../messages/ObjectImage';
|
||||
import {ObjectNameMessage} from '../messages/ObjectName';
|
||||
import {ObjectDescriptionMessage} from '../messages/ObjectDescription';
|
||||
import {ObjectPositionMessage} from '../messages/ObjectPosition';
|
||||
import {MultipleObjectUpdateMessage} from '../messages/MultipleObjectUpdate';
|
||||
import {UpdateType} from '../../enums/UpdateType';
|
||||
import {ObjectLinkMessage} from '../messages/ObjectLink';
|
||||
import {ObjectShapeMessage} from '../messages/ObjectShape';
|
||||
|
||||
export class GameObject implements IGameObjectData
|
||||
{
|
||||
@@ -139,11 +150,13 @@ export class GameObject implements IGameObjectData
|
||||
|
||||
resolveAttempts = 0;
|
||||
|
||||
claimedForBuild = false;
|
||||
|
||||
private static getFromXMLJS(obj: any, param: string): any
|
||||
{
|
||||
if (obj[param] === undefined)
|
||||
{
|
||||
return false;
|
||||
return undefined;
|
||||
}
|
||||
let retParam;
|
||||
if (Array.isArray(obj[param]))
|
||||
@@ -178,149 +191,149 @@ export class GameObject implements IGameObjectData
|
||||
const go = new GameObject();
|
||||
go.Flags = 0;
|
||||
let prop: any;
|
||||
if (this.getFromXMLJS(obj, 'AllowedDrop'))
|
||||
if (this.getFromXMLJS(obj, 'AllowedDrop') !== undefined)
|
||||
{
|
||||
go.Flags = go.Flags | PrimFlags.AllowInventoryDrop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'CreatorID'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'CreatorID')) !== undefined)
|
||||
{
|
||||
go.creatorID = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'FolderID'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'FolderID')) !== undefined)
|
||||
{
|
||||
go.folderID = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'InventorySerial'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'InventorySerial')) !== undefined)
|
||||
{
|
||||
go.inventorySerial = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'UUID'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'UUID')) !== undefined)
|
||||
{
|
||||
go.FullID = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'LocalId'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'LocalId')) !== undefined)
|
||||
{
|
||||
go.ID = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'Name'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'Name')) !== undefined)
|
||||
{
|
||||
go.name = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'Material'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'Material')) !== undefined)
|
||||
{
|
||||
go.Material = prop;
|
||||
}
|
||||
if (prop = Vector3.fromXMLJS(obj, 'GroupPosition'))
|
||||
if ((prop = Vector3.fromXMLJS(obj, 'GroupPosition')) !== undefined)
|
||||
{
|
||||
if (root)
|
||||
{
|
||||
go.Position = prop;
|
||||
}
|
||||
}
|
||||
if (prop = Vector3.fromXMLJS(obj, 'OffsetPosition'))
|
||||
if ((prop = Vector3.fromXMLJS(obj, 'OffsetPosition')) !== undefined)
|
||||
{
|
||||
if (!root)
|
||||
{
|
||||
go.Position = prop;
|
||||
}
|
||||
}
|
||||
if (prop = Quaternion.fromXMLJS(obj, 'RotationOffset'))
|
||||
if ((prop = Quaternion.fromXMLJS(obj, 'RotationOffset')) !== undefined)
|
||||
{
|
||||
go.Rotation = prop;
|
||||
}
|
||||
if (prop = Vector3.fromXMLJS(obj, 'Velocity'))
|
||||
if ((prop = Vector3.fromXMLJS(obj, 'Velocity')) !== undefined)
|
||||
{
|
||||
go.Velocity = prop;
|
||||
}
|
||||
if (prop = Vector3.fromXMLJS(obj, 'AngularVelocity'))
|
||||
if ((prop = Vector3.fromXMLJS(obj, 'AngularVelocity')) !== undefined)
|
||||
{
|
||||
go.AngularVelocity = prop;
|
||||
}
|
||||
if (prop = Vector3.fromXMLJS(obj, 'Acceleration'))
|
||||
if ((prop = Vector3.fromXMLJS(obj, 'Acceleration')) !== undefined)
|
||||
{
|
||||
go.Acceleration = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'Description'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'Description')) !== undefined)
|
||||
{
|
||||
go.description = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'Text'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'Text')) !== undefined)
|
||||
{
|
||||
go.Text = prop;
|
||||
}
|
||||
if (prop = Color4.fromXMLJS(obj, 'Color'))
|
||||
if ((prop = Color4.fromXMLJS(obj, 'Color')) !== undefined)
|
||||
{
|
||||
go.TextColor = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'SitName'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'SitName')) !== undefined)
|
||||
{
|
||||
go.sitName = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'TouchName'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'TouchName')) !== undefined)
|
||||
{
|
||||
go.touchName = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'ClickAction'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'ClickAction')) !== undefined)
|
||||
{
|
||||
go.ClickAction = prop;
|
||||
}
|
||||
if (prop = Vector3.fromXMLJS(obj, 'Scale'))
|
||||
if ((prop = Vector3.fromXMLJS(obj, 'Scale')) !== undefined)
|
||||
{
|
||||
go.Scale = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'ParentID'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'ParentID')) !== undefined)
|
||||
{
|
||||
go.ParentID = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'Category'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'Category')) !== undefined)
|
||||
{
|
||||
go.category = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'SalePrice'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'SalePrice')) !== undefined)
|
||||
{
|
||||
go.salePrice = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'ObjectSaleType'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'ObjectSaleType')) !== undefined)
|
||||
{
|
||||
go.saleType = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'OwnershipCost'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'OwnershipCost')) !== undefined)
|
||||
{
|
||||
go.ownershipCost = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'GroupID'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'GroupID')) !== undefined)
|
||||
{
|
||||
go.groupID = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'OwnerID'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'OwnerID')) !== undefined)
|
||||
{
|
||||
go.OwnerID = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'LastOwnerID'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'LastOwnerID')) !== undefined)
|
||||
{
|
||||
go.lastOwnerID = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'BaseMask'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'BaseMask')) !== undefined)
|
||||
{
|
||||
go.baseMask = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'OwnerMask'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'OwnerMask')) !== undefined)
|
||||
{
|
||||
go.ownerMask = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'GroupMask'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'GroupMask')) !== undefined)
|
||||
{
|
||||
go.groupMask = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'EveryoneMask'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'EveryoneMask')) !== undefined)
|
||||
{
|
||||
go.everyoneMask = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'NextOwnerMask'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'NextOwnerMask')) !== undefined)
|
||||
{
|
||||
go.nextOwnerMask = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'Flags'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'Flags')) !== undefined)
|
||||
{
|
||||
let flags = 0;
|
||||
if (typeof prop === 'string')
|
||||
@@ -337,121 +350,125 @@ export class GameObject implements IGameObjectData
|
||||
}
|
||||
go.Flags = flags;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'TextureAnimation'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'TextureAnimation')) !== undefined)
|
||||
{
|
||||
const buf = Buffer.from(prop, 'base64');
|
||||
go.textureAnim = TextureAnim.from(buf);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'ParticleSystem'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'ParticleSystem')) !== undefined)
|
||||
{
|
||||
const buf = Buffer.from(prop, 'base64');
|
||||
go.Particles = ParticleSystem.from(buf);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'PhysicsShapeType'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'PhysicsShapeType')) !== undefined)
|
||||
{
|
||||
go.physicsShapeType = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'SoundID'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'SoundID')) !== undefined)
|
||||
{
|
||||
go.Sound = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'SoundGain'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'SoundGain')) !== undefined)
|
||||
{
|
||||
go.SoundGain = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'SoundFlags'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'SoundFlags')) !== undefined)
|
||||
{
|
||||
go.SoundFlags = prop;
|
||||
}
|
||||
if (prop = UUID.fromXMLJS(obj, 'SoundRadius'))
|
||||
if ((prop = UUID.fromXMLJS(obj, 'SoundRadius')) !== undefined)
|
||||
{
|
||||
go.SoundRadius = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(obj, 'Shape'))
|
||||
if ((prop = this.getFromXMLJS(obj, 'Shape')) !== undefined)
|
||||
{
|
||||
const shape = prop;
|
||||
if (prop = this.getFromXMLJS(shape, 'ProfileCurve'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'ProfileCurve')) !== undefined)
|
||||
{
|
||||
go.ProfileCurve = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'TextureEntry'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'TextureEntry')) !== undefined)
|
||||
{
|
||||
const buf = Buffer.from(prop, 'base64');
|
||||
go.TextureEntry = new TextureEntry(buf);
|
||||
go.TextureEntry = TextureEntry.from(buf);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathBegin'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathBegin')) !== undefined)
|
||||
{
|
||||
go.PathBegin = prop;
|
||||
go.PathBegin = Utils.unpackBeginCut(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathCurve'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathCurve')) !== undefined)
|
||||
{
|
||||
go.PathCurve = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathEnd'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathEnd')) !== undefined)
|
||||
{
|
||||
go.PathEnd = prop;
|
||||
go.PathEnd = Utils.unpackEndCut(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathRadiusOffset'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathRadiusOffset')) !== undefined)
|
||||
{
|
||||
go.PathRadiusOffset = prop;
|
||||
go.PathRadiusOffset = Utils.unpackPathTwist(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathRevolutions'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathRevolutions')) !== undefined)
|
||||
{
|
||||
go.PathRevolutions = prop;
|
||||
go.PathRevolutions = Utils.unpackPathRevolutions(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathScaleX'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathScaleX')) !== undefined)
|
||||
{
|
||||
go.PathScaleX = prop;
|
||||
go.PathScaleX = Utils.unpackPathScale(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathScaleY'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathScaleY')) !== undefined)
|
||||
{
|
||||
go.PathScaleY = prop;
|
||||
go.PathScaleY = Utils.unpackPathScale(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathShearX'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathShearX')) !== undefined)
|
||||
{
|
||||
go.PathShearX = prop;
|
||||
go.PathShearX = Utils.unpackPathShear(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathSkew'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathShearY')) !== undefined)
|
||||
{
|
||||
go.PathSkew = prop;
|
||||
go.PathShearY = Utils.unpackPathShear(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathTaperX'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathSkew')) !== undefined)
|
||||
{
|
||||
go.PathTaperX = prop;
|
||||
go.PathSkew = Utils.unpackPathShear(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathTaperY'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathTaperX')) !== undefined)
|
||||
{
|
||||
go.PathTaperY = prop;
|
||||
go.PathTaperX = Utils.unpackPathTaper(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathTwist'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathTaperY')) !== undefined)
|
||||
{
|
||||
go.PathTwist = prop;
|
||||
go.PathTaperY = Utils.unpackPathTaper(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PathTwistBegin'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathTwist')) !== undefined)
|
||||
{
|
||||
go.PathTwistBegin = prop;
|
||||
go.PathTwist = Utils.unpackPathTwist(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'PCode'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'PathTwistBegin')) !== undefined)
|
||||
{
|
||||
go.PathTwistBegin = Utils.unpackPathTwist(prop);
|
||||
}
|
||||
if ((prop = this.getFromXMLJS(shape, 'PCode')) !== undefined)
|
||||
{
|
||||
go.PCode = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'ProfileBegin'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'ProfileBegin')) !== undefined)
|
||||
{
|
||||
go.ProfileBegin = prop;
|
||||
go.ProfileBegin = Utils.unpackBeginCut(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'ProfileEnd'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'ProfileEnd')) !== undefined)
|
||||
{
|
||||
go.ProfileEnd = prop;
|
||||
go.ProfileEnd = Utils.unpackEndCut(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'ProfileHollow'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'ProfileHollow')) !== undefined)
|
||||
{
|
||||
go.ProfileHollow = prop;
|
||||
go.ProfileHollow = Utils.unpackProfileHollow(prop);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'State'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'State')) !== undefined)
|
||||
{
|
||||
go.State = prop;
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'ProfileShape'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'ProfileShape')) !== undefined)
|
||||
{
|
||||
if (!go.ProfileCurve)
|
||||
{
|
||||
@@ -459,7 +476,7 @@ export class GameObject implements IGameObjectData
|
||||
}
|
||||
go.ProfileCurve = go.ProfileCurve | parseInt(ProfileShape[prop], 10);
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'HollowShape'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'HollowShape')) !== undefined)
|
||||
{
|
||||
if (!go.ProfileCurve)
|
||||
{
|
||||
@@ -467,7 +484,7 @@ export class GameObject implements IGameObjectData
|
||||
}
|
||||
go.ProfileCurve = go.ProfileCurve | parseInt(HoleType[prop], 10);
|
||||
}
|
||||
if (this.getFromXMLJS(shape, 'SculptEntry'))
|
||||
if (this.getFromXMLJS(shape, 'SculptEntry') !== undefined)
|
||||
{
|
||||
const type = this.getFromXMLJS(shape, 'SculptType');
|
||||
if (type !== false && type !== undefined)
|
||||
@@ -475,6 +492,10 @@ export class GameObject implements IGameObjectData
|
||||
const id = UUID.fromXMLJS(shape, 'SculptTexture');
|
||||
if (id instanceof UUID)
|
||||
{
|
||||
if (!go.extraParams)
|
||||
{
|
||||
go.extraParams = new ExtraParams();
|
||||
}
|
||||
if (type & SculptType.Mesh)
|
||||
{
|
||||
go.extraParams.setMeshData(type, id);
|
||||
@@ -486,7 +507,7 @@ export class GameObject implements IGameObjectData
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.getFromXMLJS(shape, 'FlexiEntry'))
|
||||
if (this.getFromXMLJS(shape, 'FlexiEntry') !== undefined)
|
||||
{
|
||||
const flexiSoftness = this.getFromXMLJS(shape, 'FlexiSoftness');
|
||||
const flexiTension = this.getFromXMLJS(shape, 'FlexiTension');
|
||||
@@ -505,10 +526,14 @@ export class GameObject implements IGameObjectData
|
||||
flexiForceY !== false &&
|
||||
flexiForceZ !== false)
|
||||
{
|
||||
if (!go.extraParams)
|
||||
{
|
||||
go.extraParams = new ExtraParams();
|
||||
}
|
||||
go.extraParams.setFlexiData(flexiSoftness, flexiTension, flexiDrag, flexiGravity, flexiWind, new Vector3([flexiForceX, flexiForceY, flexiForceZ]));
|
||||
}
|
||||
}
|
||||
if (this.getFromXMLJS(shape, 'LightEntry'))
|
||||
if (this.getFromXMLJS(shape, 'LightEntry') !== undefined)
|
||||
{
|
||||
const lightColorR = this.getFromXMLJS(shape, 'LightColorR');
|
||||
const lightColorG = this.getFromXMLJS(shape, 'LightColorG');
|
||||
@@ -527,6 +552,10 @@ export class GameObject implements IGameObjectData
|
||||
lightFalloff !== false &&
|
||||
lightIntensity !== false)
|
||||
{
|
||||
if (!go.extraParams)
|
||||
{
|
||||
go.extraParams = new ExtraParams();
|
||||
}
|
||||
go.extraParams.setLightData(
|
||||
new Color4(lightColorR, lightColorG, lightColorB, lightColorA),
|
||||
lightRadius,
|
||||
@@ -536,18 +565,14 @@ export class GameObject implements IGameObjectData
|
||||
);
|
||||
}
|
||||
}
|
||||
if (prop = this.getFromXMLJS(shape, 'ExtraParams'))
|
||||
if ((prop = this.getFromXMLJS(shape, 'ExtraParams')) !== undefined)
|
||||
{
|
||||
const buf = Buffer.from(prop, 'base64');
|
||||
go.extraParams = ExtraParams.from(buf);
|
||||
}
|
||||
}
|
||||
// TODO: TaskInventory
|
||||
|
||||
|
||||
|
||||
console.log('BURP');
|
||||
process.exit(0);
|
||||
return go;
|
||||
}
|
||||
|
||||
static fromXML(xml: string)
|
||||
@@ -572,7 +597,21 @@ export class GameObject implements IGameObjectData
|
||||
throw new Error('Root part not found');
|
||||
}
|
||||
const rootPart = GameObject.partFromXMLJS(result['SceneObjectPart'][0], true);
|
||||
|
||||
rootPart.children = [];
|
||||
rootPart.totalChildren = 0;
|
||||
if (result['OtherParts'] && Array.isArray(result['OtherParts']) && result['OtherParts'].length > 0)
|
||||
{
|
||||
const obj = result['OtherParts'][0];
|
||||
if (obj['SceneObjectPart'])
|
||||
{
|
||||
for (const part of obj['SceneObjectPart'])
|
||||
{
|
||||
rootPart.children.push(GameObject.partFromXMLJS(part, false));
|
||||
rootPart.totalChildren++;
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve(rootPart);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -603,6 +642,359 @@ export class GameObject implements IGameObjectData
|
||||
return '';
|
||||
}
|
||||
|
||||
setIfDefined(def?: number, v?: number)
|
||||
{
|
||||
if (def === undefined)
|
||||
{
|
||||
def = 0;
|
||||
}
|
||||
if (v === undefined)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
async setShape(PathCurve?: number,
|
||||
ProfileCurve?: number,
|
||||
PathBegin?: number,
|
||||
PathEnd?: number,
|
||||
PathScaleX?: number,
|
||||
PathScaleY?: number,
|
||||
PathShearX?: number,
|
||||
PathShearY?: number,
|
||||
PathTwist?: number,
|
||||
PathTwistBegin?: number,
|
||||
PathRadiusOffset?: number,
|
||||
PathTaperX?: number,
|
||||
PathTaperY?: number,
|
||||
PathRevolutions?: number,
|
||||
PathSkew?: number,
|
||||
ProfileBegin?: number,
|
||||
ProfileEnd?: number,
|
||||
ProfileHollow?: number)
|
||||
{
|
||||
this.PathCurve = this.setIfDefined(this.PathCurve, PathCurve);
|
||||
this.ProfileCurve = this.setIfDefined(this.ProfileCurve, ProfileCurve);
|
||||
this.PathBegin = this.setIfDefined(this.PathBegin, PathBegin);
|
||||
this.PathEnd = this.setIfDefined(this.PathEnd, PathEnd);
|
||||
this.PathScaleX = this.setIfDefined(this.PathScaleX, PathScaleX);
|
||||
this.PathScaleY = this.setIfDefined(this.PathScaleY, PathScaleY);
|
||||
this.PathShearX = this.setIfDefined(this.PathShearX, PathShearX);
|
||||
this.PathShearY = this.setIfDefined(this.PathShearY, PathShearY);
|
||||
this.PathTwist = this.setIfDefined(this.PathTwist, PathTwist);
|
||||
this.PathTwistBegin = this.setIfDefined(this.PathTwistBegin, PathTwistBegin);
|
||||
this.PathRadiusOffset = this.setIfDefined(this.PathRadiusOffset, PathRadiusOffset);
|
||||
this.PathTaperX = this.setIfDefined(this.PathTaperX, PathTaperX);
|
||||
this.PathTaperY = this.setIfDefined(this.PathTaperY, PathTaperY);
|
||||
this.PathRevolutions = this.setIfDefined(this.PathRevolutions, PathRevolutions);
|
||||
this.PathSkew = this.setIfDefined(this.PathSkew, PathSkew);
|
||||
this.ProfileBegin = this.setIfDefined(this.ProfileBegin, ProfileBegin);
|
||||
this.ProfileEnd = this.setIfDefined(this.ProfileEnd, ProfileEnd);
|
||||
this.ProfileHollow = this.setIfDefined(this.ProfileHollow, ProfileHollow);
|
||||
if (!this.region)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const msg = new ObjectShapeMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.region.agent.agentID,
|
||||
SessionID: this.region.circuit.sessionID
|
||||
};
|
||||
msg.ObjectData = [
|
||||
{
|
||||
ObjectLocalID: this.ID,
|
||||
PathCurve: this.PathCurve,
|
||||
ProfileCurve: this.ProfileCurve,
|
||||
PathBegin: Utils.packBeginCut(this.PathBegin),
|
||||
PathEnd: Utils.packEndCut(this.PathEnd),
|
||||
PathScaleX: Utils.packPathScale(this.PathScaleX),
|
||||
PathScaleY: Utils.packPathScale(this.PathScaleY),
|
||||
PathShearX: Utils.packPathShear(this.PathShearX),
|
||||
PathShearY: Utils.packPathShear(this.PathShearY),
|
||||
PathTwist: Utils.packPathTwist(this.PathTwist),
|
||||
PathTwistBegin: Utils.packPathTwist(this.PathTwistBegin),
|
||||
PathRadiusOffset: Utils.packPathTwist(this.PathRadiusOffset),
|
||||
PathTaperX: Utils.packPathTaper(this.PathTaperX),
|
||||
PathTaperY: Utils.packPathTaper(this.PathTaperY),
|
||||
PathRevolutions: Utils.packPathRevolutions(this.PathRevolutions),
|
||||
PathSkew: Utils.packPathTwist(this.PathSkew),
|
||||
ProfileBegin: Utils.packBeginCut(this.ProfileBegin),
|
||||
ProfileEnd: Utils.packEndCut(this.ProfileEnd),
|
||||
ProfileHollow: Utils.packProfileHollow(this.ProfileHollow)
|
||||
}
|
||||
];
|
||||
await this.region.circuit.waitForAck(this.region.circuit.sendMessage(msg, PacketFlags.Reliable), 10000);
|
||||
}
|
||||
|
||||
async setName(name: string)
|
||||
{
|
||||
this.name = name;
|
||||
if (!this.region)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const msg = new ObjectNameMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.region.agent.agentID,
|
||||
SessionID: this.region.circuit.sessionID
|
||||
};
|
||||
msg.ObjectData = [
|
||||
{
|
||||
LocalID: this.ID,
|
||||
Name: Utils.StringToBuffer(name)
|
||||
}
|
||||
];
|
||||
await this.region.circuit.waitForAck(this.region.circuit.sendMessage(msg, PacketFlags.Reliable), 10000);
|
||||
}
|
||||
|
||||
private compareParam(name: string, param1: number | undefined, param2: number | undefined): boolean
|
||||
{
|
||||
if (param1 === undefined)
|
||||
{
|
||||
param1 = 0;
|
||||
}
|
||||
if (param2 === undefined)
|
||||
{
|
||||
param2 = 0;
|
||||
}
|
||||
if (Math.abs(param1 - param2) < 0.0001)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Failed ' + name + ' - ' + param1 + ' vs ' + param2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
compareShape(obj: GameObject): boolean
|
||||
{
|
||||
return this.compareParam('PathCurve', this.PathCurve, obj.PathCurve) &&
|
||||
this.compareParam('ProfileCurve', this.ProfileCurve, obj.ProfileCurve) &&
|
||||
this.compareParam('PathBegin', this.PathBegin, obj.PathBegin) &&
|
||||
this.compareParam('PathEnd', this.PathEnd, obj.PathEnd) &&
|
||||
this.compareParam('PathScaleX', this.PathScaleX, obj.PathScaleX) &&
|
||||
this.compareParam('PathScaleY', this.PathScaleY, obj.PathScaleY) &&
|
||||
this.compareParam('PathShearX', this.PathShearX, obj.PathShearX) &&
|
||||
this.compareParam('PathShearY', this.PathShearY, obj.PathShearY) &&
|
||||
this.compareParam('PathTwist', this.PathTwist, obj.PathTwist) &&
|
||||
this.compareParam('PathTwistBegin', this.PathTwistBegin, obj.PathTwistBegin) &&
|
||||
this.compareParam('PathRadiusOffset', this.PathRadiusOffset, obj.PathRadiusOffset) &&
|
||||
this.compareParam('PathTaperX', this.PathTaperX, obj.PathTaperX) &&
|
||||
this.compareParam('PathTaperY', this.PathTaperY, obj.PathTaperY) &&
|
||||
this.compareParam('PathRevolutions', this.PathRevolutions, obj.PathRevolutions) &&
|
||||
this.compareParam('PathSkew', this.PathSkew, obj.PathSkew) &&
|
||||
this.compareParam('ProfileBegin', this.ProfileBegin, obj.ProfileBegin) &&
|
||||
this.compareParam('ProfileEnd', this.ProfileEnd, obj.ProfileEnd) &&
|
||||
this.compareParam('PRofileHollow', this.ProfileHollow, obj.ProfileHollow);
|
||||
}
|
||||
|
||||
async setGeometry(pos?: Vector3, rot?: Quaternion, scale?: Vector3)
|
||||
{
|
||||
const data = [];
|
||||
if (pos !== undefined)
|
||||
{
|
||||
this.Position = pos;
|
||||
data.push({
|
||||
ObjectLocalID: this.ID,
|
||||
Type: UpdateType.Position,
|
||||
Data: pos.getBuffer()
|
||||
});
|
||||
}
|
||||
if (rot !== undefined)
|
||||
{
|
||||
this.Rotation = rot;
|
||||
data.push({
|
||||
ObjectLocalID: this.ID,
|
||||
Type: UpdateType.Rotation,
|
||||
Data: rot.getBuffer()
|
||||
})
|
||||
}
|
||||
if (scale !== undefined)
|
||||
{
|
||||
this.Scale = scale;
|
||||
data.push({
|
||||
ObjectLocalID: this.ID,
|
||||
Type: UpdateType.Scale,
|
||||
Data: scale.getBuffer()
|
||||
})
|
||||
}
|
||||
if (!this.region || data.length === 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const msg = new MultipleObjectUpdateMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.region.agent.agentID,
|
||||
SessionID: this.region.circuit.sessionID
|
||||
};
|
||||
msg.ObjectData = data;
|
||||
await this.region.circuit.waitForAck(this.region.circuit.sendMessage(msg, PacketFlags.Reliable), 30000);
|
||||
}
|
||||
|
||||
async linkTo(root: GameObject)
|
||||
{
|
||||
const msg = new ObjectLinkMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.region.agent.agentID,
|
||||
SessionID: this.region.circuit.sessionID
|
||||
};
|
||||
msg.ObjectData = [
|
||||
{
|
||||
ObjectLocalID: root.ID
|
||||
},
|
||||
{
|
||||
ObjectLocalID: this.ID
|
||||
}
|
||||
];
|
||||
await this.region.circuit.waitForAck(this.region.circuit.sendMessage(msg, PacketFlags.Reliable), 30000);
|
||||
}
|
||||
|
||||
async setDescription(desc: string)
|
||||
{
|
||||
this.description = desc;
|
||||
if (!this.region)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const msg = new ObjectDescriptionMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.region.agent.agentID,
|
||||
SessionID: this.region.circuit.sessionID
|
||||
};
|
||||
msg.ObjectData = [
|
||||
{
|
||||
LocalID: this.ID,
|
||||
Description: Utils.StringToBuffer(desc)
|
||||
}
|
||||
];
|
||||
await this.region.circuit.waitForAck(this.region.circuit.sendMessage(msg, PacketFlags.Reliable), 10000);
|
||||
}
|
||||
|
||||
async setTextureEntry(e: TextureEntry)
|
||||
{
|
||||
this.TextureEntry = e;
|
||||
if (!this.region)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await this.setTextureAndMediaURL();
|
||||
}
|
||||
|
||||
private async setTextureAndMediaURL()
|
||||
{
|
||||
const msg = new ObjectImageMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.region.agent.agentID,
|
||||
SessionID: this.region.circuit.sessionID
|
||||
};
|
||||
if (this.MediaURL === undefined)
|
||||
{
|
||||
this.MediaURL = '';
|
||||
}
|
||||
if (this.TextureEntry === undefined)
|
||||
{
|
||||
this.TextureEntry = new TextureEntry();
|
||||
}
|
||||
msg.ObjectData = [
|
||||
{
|
||||
ObjectLocalID: this.ID,
|
||||
TextureEntry: this.TextureEntry.toBuffer(),
|
||||
MediaURL: Utils.StringToBuffer(this.MediaURL)
|
||||
}
|
||||
];
|
||||
await this.region.circuit.waitForAck(this.region.circuit.sendMessage(msg, PacketFlags.Reliable), 10000);
|
||||
}
|
||||
|
||||
async setExtraParams(ex: ExtraParams)
|
||||
{
|
||||
this.extraParams = ex;
|
||||
if (!this.region)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set ExtraParams
|
||||
const msg = new ObjectExtraParamsMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.region.agent.agentID,
|
||||
SessionID: this.region.circuit.sessionID
|
||||
};
|
||||
msg.ObjectData = [];
|
||||
let params = 0;
|
||||
if (ex.lightData !== null)
|
||||
{
|
||||
params++;
|
||||
const data = ex.lightData.getBuffer();
|
||||
msg.ObjectData.push({
|
||||
ObjectLocalID: this.ID,
|
||||
ParamType: ExtraParamType.Light,
|
||||
ParamInUse: (ex.lightData.Intensity !== 0.0),
|
||||
ParamData: data,
|
||||
ParamSize: data.length
|
||||
});
|
||||
}
|
||||
if (ex.flexibleData !== null)
|
||||
{
|
||||
params++;
|
||||
const data = ex.flexibleData.getBuffer();
|
||||
msg.ObjectData.push({
|
||||
ObjectLocalID: this.ID,
|
||||
ParamType: ExtraParamType.Flexible,
|
||||
ParamInUse: true,
|
||||
ParamData: data,
|
||||
ParamSize: data.length
|
||||
});
|
||||
}
|
||||
if (ex.lightImageData !== null)
|
||||
{
|
||||
params++;
|
||||
const data = ex.lightImageData.getBuffer();
|
||||
msg.ObjectData.push({
|
||||
ObjectLocalID: this.ID,
|
||||
ParamType: ExtraParamType.LightImage,
|
||||
ParamInUse: true,
|
||||
ParamData: data,
|
||||
ParamSize: data.length
|
||||
});
|
||||
}
|
||||
if (ex.sculptData !== null)
|
||||
{
|
||||
params++;
|
||||
const data = ex.sculptData.getBuffer();
|
||||
msg.ObjectData.push({
|
||||
ObjectLocalID: this.ID,
|
||||
ParamType: ExtraParamType.Sculpt,
|
||||
ParamInUse: true,
|
||||
ParamData: data,
|
||||
ParamSize: data.length
|
||||
});
|
||||
}
|
||||
if (ex.meshData !== null)
|
||||
{
|
||||
params++;
|
||||
const data = ex.meshData.getBuffer();
|
||||
msg.ObjectData.push({
|
||||
ObjectLocalID: this.ID,
|
||||
ParamType: ExtraParamType.Mesh,
|
||||
ParamInUse: true,
|
||||
ParamData: data,
|
||||
ParamSize: data.length
|
||||
});
|
||||
}
|
||||
if (params > 0)
|
||||
{
|
||||
const ack = this.region.circuit.sendMessage(msg, PacketFlags.Reliable);
|
||||
await this.region.circuit.waitForAck(ack, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
private async getInventoryXML(xml: XMLElementOrXMLNode, inv: InventoryItem)
|
||||
{
|
||||
if (!inv.assetID.equals(UUID.zero()))
|
||||
@@ -695,29 +1087,30 @@ export class GameObject implements IGameObjectData
|
||||
shape.ele('ProfileCurve', this.ProfileCurve);
|
||||
if (this.TextureEntry)
|
||||
{
|
||||
shape.ele('TextureEntry', this.TextureEntry.binary.toString('base64'));
|
||||
shape.ele('TextureEntry', this.TextureEntry.toBase64());
|
||||
}
|
||||
if (this.extraParams)
|
||||
{
|
||||
shape.ele('ExtraParams', this.extraParams.toBase64());
|
||||
}
|
||||
shape.ele('PathBegin', this.PathBegin);
|
||||
shape.ele('PathBegin', Utils.packBeginCut(Utils.numberOrZero(this.PathBegin)));
|
||||
shape.ele('PathCurve', this.PathCurve);
|
||||
shape.ele('PathEnd', this.PathEnd);
|
||||
shape.ele('PathRadiusOffset', this.PathRadiusOffset);
|
||||
shape.ele('PathRevolutions', this.PathRevolutions);
|
||||
shape.ele('PathScaleX', this.PathScaleX);
|
||||
shape.ele('PathScaleY', this.PathScaleY);
|
||||
shape.ele('PathShearX', this.PathShearX);
|
||||
shape.ele('PathSkew', this.PathSkew);
|
||||
shape.ele('PathTaperX', this.PathTaperX);
|
||||
shape.ele('PathTaperY', this.PathTaperY);
|
||||
shape.ele('PathTwist', this.PathTwist);
|
||||
shape.ele('PathTwistBegin', this.PathTwistBegin);
|
||||
shape.ele('PathEnd', Utils.packEndCut(Utils.numberOrZero(this.PathEnd)));
|
||||
shape.ele('PathRadiusOffset', Utils.packPathTwist(Utils.numberOrZero(this.PathRadiusOffset)));
|
||||
shape.ele('PathRevolutions', Utils.packPathRevolutions(Utils.numberOrZero(this.PathRevolutions)));
|
||||
shape.ele('PathScaleX', Utils.packPathScale(Utils.numberOrZero(this.PathScaleX)));
|
||||
shape.ele('PathScaleY', Utils.packPathScale(Utils.numberOrZero(this.PathScaleY)));
|
||||
shape.ele('PathShearX', Utils.packPathShear(Utils.numberOrZero(this.PathShearX)));
|
||||
shape.ele('PathShearY', Utils.packPathShear(Utils.numberOrZero(this.PathShearY)));
|
||||
shape.ele('PathSkew', Utils.packPathTwist(Utils.numberOrZero(this.PathSkew)));
|
||||
shape.ele('PathTaperX', Utils.packPathTaper(Utils.numberOrZero(this.PathTaperX)));
|
||||
shape.ele('PathTaperY', Utils.packPathTaper(Utils.numberOrZero(this.PathTaperY)));
|
||||
shape.ele('PathTwist', Utils.packPathTwist(Utils.numberOrZero(this.PathTwist)));
|
||||
shape.ele('PathTwistBegin', Utils.packPathTwist(Utils.numberOrZero(this.PathTwistBegin)));
|
||||
shape.ele('PCode', this.PCode);
|
||||
shape.ele('ProfileBegin', this.ProfileBegin);
|
||||
shape.ele('ProfileEnd', this.ProfileEnd);
|
||||
shape.ele('ProfileHollow', this.ProfileHollow);
|
||||
shape.ele('ProfileBegin', Utils.packBeginCut(Utils.numberOrZero(this.ProfileBegin)));
|
||||
shape.ele('ProfileEnd', Utils.packEndCut(Utils.numberOrZero(this.ProfileEnd)));
|
||||
shape.ele('ProfileHollow', Utils.packProfileHollow(Utils.numberOrZero(this.ProfileHollow)));
|
||||
shape.ele('State', this.State);
|
||||
|
||||
if (this.ProfileCurve)
|
||||
|
||||
630
lib/classes/public/LLMesh.ts
Normal file
630
lib/classes/public/LLMesh.ts
Normal file
@@ -0,0 +1,630 @@
|
||||
import {Utils} from '../Utils';
|
||||
import * as zlib from 'zlib';
|
||||
import * as LLSD from '@caspertech/llsd';
|
||||
import {UUID} from '../UUID';
|
||||
import {LLSubMesh} from './interfaces/LLSubMesh';
|
||||
import {Vector3} from '../Vector3';
|
||||
import {Vector2} from '../Vector2';
|
||||
import {LLSkin} from './interfaces/LLSkin';
|
||||
import {mat4} from '../../tsm/mat4';
|
||||
import {LLPhysicsConvex} from './interfaces/LLPhysicsConvex';
|
||||
|
||||
export class LLMesh
|
||||
{
|
||||
version: number;
|
||||
lodLevels: {[key: string]: LLSubMesh[]} = {};
|
||||
physicsConvex: LLPhysicsConvex;
|
||||
skin?: LLSkin;
|
||||
creatorID: UUID;
|
||||
date: Date;
|
||||
|
||||
static async from(buf: Buffer): Promise<LLMesh>
|
||||
{
|
||||
const llmesh = new LLMesh();
|
||||
const binData = new LLSD.Binary(Array.from(buf), 'BASE64');
|
||||
let obj = LLSD.LLSD.parseBinary(binData);
|
||||
if (obj['result'] === undefined)
|
||||
{
|
||||
throw new Error('Failed to decode header');
|
||||
}
|
||||
if (obj['position'] === undefined)
|
||||
{
|
||||
throw new Error('Position not reported');
|
||||
}
|
||||
const startPos = parseInt(obj['position'], 10);
|
||||
obj = obj['result'];
|
||||
if (!obj['version'])
|
||||
{
|
||||
throw new Error('No version found');
|
||||
}
|
||||
if (!obj['creator'])
|
||||
{
|
||||
throw new Error('Creator UUID not found');
|
||||
}
|
||||
if (obj['date'] === undefined)
|
||||
{
|
||||
throw new Error('Date not found');
|
||||
}
|
||||
llmesh.creatorID = new UUID(obj['creator'].toString());
|
||||
llmesh.date = obj['date'];
|
||||
llmesh.version = parseInt(obj['version'], 10);
|
||||
for (const key of Object.keys(obj))
|
||||
{
|
||||
const o = obj[key];
|
||||
if (typeof o === 'object' && o !== null && o['offset'] !== undefined)
|
||||
{
|
||||
const bufFrom = startPos + parseInt(o['offset'], 10);
|
||||
const bufTo = startPos + parseInt(o['offset'], 10) + parseInt(o['size'], 10);
|
||||
const partBuf = buf.slice(bufFrom, bufTo);
|
||||
const deflated = await this.inflate(partBuf);
|
||||
|
||||
const mesh = LLSD.LLSD.parseBinary(new LLSD.Binary(Array.from(deflated), 'BASE64'));
|
||||
if (mesh['result'] === undefined)
|
||||
{
|
||||
throw new Error('Failed to parse compressed submesh data');
|
||||
}
|
||||
if (key === 'physics_convex')
|
||||
{
|
||||
llmesh.physicsConvex = this.parsePhysicsConvex(mesh['result']);
|
||||
}
|
||||
else if (key === 'skin')
|
||||
{
|
||||
llmesh.skin = this.parseSkin(mesh['result']);
|
||||
}
|
||||
else if (key === 'physics_havok' || key === 'physics_cost_data')
|
||||
{
|
||||
// Used by the simulator
|
||||
}
|
||||
else
|
||||
{
|
||||
llmesh.lodLevels[key] = this.parseLODLevel(mesh['result']);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return llmesh;
|
||||
}
|
||||
static parseSkin(mesh: any): LLSkin
|
||||
{
|
||||
if (!mesh['joint_names'])
|
||||
{
|
||||
throw new Error('Joint names missing from skin');
|
||||
}
|
||||
if (!mesh['bind_shape_matrix'])
|
||||
{
|
||||
throw new Error('Bind shape matrix missing from skin');
|
||||
}
|
||||
if (!mesh['inverse_bind_matrix'])
|
||||
{
|
||||
throw new Error('Inverse bind matrix missing from skin');
|
||||
}
|
||||
const skin: LLSkin = {
|
||||
jointNames: mesh['joint_names'],
|
||||
bindShapeMatrix: new mat4(mesh['bind_shape_matrix']),
|
||||
inverseBindMatrix: []
|
||||
};
|
||||
if (mesh['inverse_bind_matrix'])
|
||||
{
|
||||
skin.inverseBindMatrix = [];
|
||||
for (const inv of mesh['inverse_bind_matrix'])
|
||||
{
|
||||
skin.inverseBindMatrix.push(new mat4(inv));
|
||||
}
|
||||
}
|
||||
if (mesh['alt_inverse_bind_matrix'])
|
||||
{
|
||||
skin.altInverseBindMatrix = [];
|
||||
for (const inv of mesh['alt_inverse_bind_matrix'])
|
||||
{
|
||||
skin.altInverseBindMatrix.push(new mat4(inv));
|
||||
}
|
||||
}
|
||||
if (mesh['pelvis_offset'])
|
||||
{
|
||||
skin.pelvisOffset = new mat4(mesh['pelvis_offset']);
|
||||
}
|
||||
return skin;
|
||||
}
|
||||
|
||||
static fixReal(arr: number[]): number[]
|
||||
{
|
||||
const newArr = [];
|
||||
for (let num of arr)
|
||||
{
|
||||
if ((num >> 0) === num && !((num === 0) && ((1 / num) === -Infinity)))
|
||||
{
|
||||
num += 0.0000000001;
|
||||
}
|
||||
newArr.push(num);
|
||||
}
|
||||
return newArr;
|
||||
}
|
||||
static parsePhysicsConvex(mesh: any): LLPhysicsConvex
|
||||
{
|
||||
const conv: LLPhysicsConvex = {
|
||||
boundingVerts: [],
|
||||
domain: {
|
||||
min: new Vector3([-0.5, -0.5, -0.5]),
|
||||
max: new Vector3([0.5, 0.5, 0.5])
|
||||
}
|
||||
};
|
||||
if (mesh['Min'])
|
||||
{
|
||||
conv.domain.min.x = mesh['Min'][0];
|
||||
conv.domain.min.y = mesh['Min'][1];
|
||||
conv.domain.min.z = mesh['Min'][2];
|
||||
}
|
||||
if (mesh['Max'])
|
||||
{
|
||||
conv.domain.max.x = mesh['Max'][0];
|
||||
conv.domain.max.y = mesh['Max'][1];
|
||||
conv.domain.max.z = mesh['Max'][2];
|
||||
}
|
||||
if (mesh['HullList'])
|
||||
{
|
||||
if (!mesh['Positions'])
|
||||
{
|
||||
throw new Error('Positions must be supplied if hull list is present');
|
||||
}
|
||||
conv.positions = this.decodeByteDomain3(mesh['Positions'].toArray(), conv.domain.min, conv.domain.max);
|
||||
conv.hullList = mesh['HullList'].toArray();
|
||||
if (conv.hullList === undefined)
|
||||
{
|
||||
throw new Error('HullList undefined');
|
||||
}
|
||||
else
|
||||
{
|
||||
let totalPoints = 0;
|
||||
for (const hull of conv.hullList)
|
||||
{
|
||||
totalPoints += hull;
|
||||
}
|
||||
if (conv.positions.length !== totalPoints)
|
||||
{
|
||||
throw new Error('Hull list expected number of points does not match number of positions: ' + totalPoints + ' vs ' + conv.positions.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mesh['BoundingVerts'])
|
||||
{
|
||||
throw new Error('BoundingVerts is required');
|
||||
}
|
||||
conv.boundingVerts = this.decodeByteDomain3(mesh['BoundingVerts'].toArray(), conv.domain.min, conv.domain.max);
|
||||
return conv;
|
||||
}
|
||||
static parseLODLevel(mesh: any): LLSubMesh[]
|
||||
{
|
||||
const list: LLSubMesh[] = [];
|
||||
for (const submesh of mesh)
|
||||
{
|
||||
const decoded: LLSubMesh = {
|
||||
positionDomain: {
|
||||
min: new Vector3([-0.5, -0.5, -0.5]),
|
||||
max: new Vector3([0.5, 0.5, 0.5])
|
||||
}
|
||||
};
|
||||
if (submesh['NoGeometry'])
|
||||
{
|
||||
decoded.noGeometry = true;
|
||||
list.push(decoded);
|
||||
}
|
||||
else
|
||||
{
|
||||
decoded.position = [];
|
||||
if (!submesh['Position'])
|
||||
{
|
||||
throw new Error('Submesh does not contain position data');
|
||||
}
|
||||
if (decoded.positionDomain !== undefined)
|
||||
{
|
||||
if (submesh['PositionDomain'])
|
||||
{
|
||||
if (submesh['PositionDomain']['Max'] !== undefined)
|
||||
{
|
||||
const dom = submesh['PositionDomain']['Max'];
|
||||
decoded.positionDomain.max.x = dom[0];
|
||||
decoded.positionDomain.max.y = dom[1];
|
||||
decoded.positionDomain.max.z = dom[2];
|
||||
}
|
||||
if (submesh['PositionDomain']['Min'] !== undefined)
|
||||
{
|
||||
const dom = submesh['PositionDomain']['Min'];
|
||||
decoded.positionDomain.min.x = dom[0];
|
||||
decoded.positionDomain.min.y = dom[1];
|
||||
decoded.positionDomain.min.z = dom[2];
|
||||
}
|
||||
}
|
||||
decoded.position = this.decodeByteDomain3(submesh['Position'].toArray(), decoded.positionDomain.min, decoded.positionDomain.max);
|
||||
}
|
||||
if (submesh['Normal'])
|
||||
{
|
||||
decoded.normal = this.decodeByteDomain3(submesh['Normal'].toArray(), new Vector3([-1.0, -1.0, -1.0]), new Vector3([1.0, 1.0, 1.0]));
|
||||
if (decoded.normal.length !== decoded.position.length)
|
||||
{
|
||||
throw new Error('Normal length does not match vertex position length');
|
||||
}
|
||||
}
|
||||
if (submesh['TexCoord0'])
|
||||
{
|
||||
decoded.texCoord0Domain = {
|
||||
min: new Vector2([-0.5, -0.5]),
|
||||
max: new Vector2([0.5, 0.5])
|
||||
};
|
||||
if (submesh['TexCoord0Domain'])
|
||||
{
|
||||
if (submesh['TexCoord0Domain']['Max'] !== undefined)
|
||||
{
|
||||
const dom = submesh['TexCoord0Domain']['Max'];
|
||||
decoded.texCoord0Domain.max.x = dom[0];
|
||||
decoded.texCoord0Domain.max.y = dom[1];
|
||||
}
|
||||
if (submesh['TexCoord0Domain']['Min'] !== undefined)
|
||||
{
|
||||
const dom = submesh['TexCoord0Domain']['Min'];
|
||||
decoded.texCoord0Domain.min.x = dom[0];
|
||||
decoded.texCoord0Domain.min.y = dom[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Error('TexCoord0Domain is required if Texcoord0 is present');
|
||||
}
|
||||
decoded.texCoord0 = this.decodeByteDomain2(submesh['TexCoord0'].toArray(), decoded.texCoord0Domain.min, decoded.texCoord0Domain.max);
|
||||
}
|
||||
if (!submesh['TriangleList'])
|
||||
{
|
||||
throw new Error('TriangleList is required');
|
||||
}
|
||||
const indexBuf = new Buffer(submesh['TriangleList'].toArray());
|
||||
decoded.triangleList = [];
|
||||
for (let pos = 0; pos < indexBuf.length; pos = pos + 2)
|
||||
{
|
||||
const vertIndex = indexBuf.readUInt16LE(pos);
|
||||
if (vertIndex >= decoded.position.length)
|
||||
{
|
||||
throw new Error('Vertex index out of range: ' + vertIndex)
|
||||
}
|
||||
decoded.triangleList.push(vertIndex);
|
||||
}
|
||||
if (submesh['Weights'])
|
||||
{
|
||||
const skinBuf = new Buffer(submesh['Weights'].toArray());
|
||||
decoded.weights = [];
|
||||
let pos = 0;
|
||||
while (pos < skinBuf.length)
|
||||
{
|
||||
const entry: {[key: number]: number} = {};
|
||||
for (let x = 0; x < 4; x++)
|
||||
{
|
||||
const jointNum = skinBuf.readUInt8(pos++);
|
||||
if (jointNum === 0xFF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const value = skinBuf.readUInt16LE(pos); pos = pos + 2;
|
||||
entry[jointNum] = value;
|
||||
}
|
||||
decoded.weights.push(entry);
|
||||
}
|
||||
if (decoded.weights.length !== decoded.position.length)
|
||||
{
|
||||
throw new Error('Weight list differs in length from position list');
|
||||
}
|
||||
}
|
||||
list.push(decoded);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
static decodeByteDomain3(posArray: number[], minDomain: Vector3, maxDomain: Vector3): Vector3[]
|
||||
{
|
||||
const result: Vector3[] = [];
|
||||
const buf = new Buffer(posArray);
|
||||
for (let idx = 0; idx < posArray.length; idx = idx + 6)
|
||||
{
|
||||
const posX = this.normalizeDomain(buf.readUInt16LE(idx), minDomain.x, maxDomain.x);
|
||||
const posY = this.normalizeDomain(buf.readUInt16LE(idx + 2), minDomain.y, maxDomain.y);
|
||||
const posZ = this.normalizeDomain(buf.readUInt16LE(idx + 4), minDomain.z, maxDomain.z);
|
||||
result.push(new Vector3([posX, posY, posZ]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static decodeByteDomain2(posArray: number[], minDomain: Vector2, maxDomain: Vector2): Vector2[]
|
||||
{
|
||||
const result: Vector2[] = [];
|
||||
const buf = new Buffer(posArray);
|
||||
for (let idx = 0; idx < posArray.length; idx = idx + 4)
|
||||
{
|
||||
const posX = this.normalizeDomain(buf.readUInt16LE(idx), minDomain.x, maxDomain.x);
|
||||
const posY = this.normalizeDomain(buf.readUInt16LE(idx + 2), minDomain.y, maxDomain.y);
|
||||
result.push(new Vector2([posX, posY]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static normalizeDomain(value: number, min: number, max: number)
|
||||
{
|
||||
return ((value / 65535) * (max - min)) + min;
|
||||
}
|
||||
static inflate(buf: Buffer): Promise<Buffer>
|
||||
{
|
||||
return new Promise<Buffer>((resolve, reject) =>
|
||||
{
|
||||
zlib.inflate(buf, (error: (Error| null), result: Buffer) =>
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
reject(error)
|
||||
}
|
||||
else
|
||||
{
|
||||
resolve(result);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
static deflate(buf: Buffer): Promise<Buffer>
|
||||
{
|
||||
return new Promise<Buffer>((resolve, reject) =>
|
||||
{
|
||||
zlib.deflate(buf, { level: 9}, (error: (Error| null), result: Buffer) =>
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
reject(error)
|
||||
}
|
||||
else
|
||||
{
|
||||
resolve(result);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
private encodeSubMesh(mesh: LLSubMesh)
|
||||
{
|
||||
const data: {
|
||||
NoGeometry?: true,
|
||||
Position?: any, // LLSD.Binary
|
||||
PositionDomain?: {
|
||||
Min: number[],
|
||||
Max: number[]
|
||||
},
|
||||
Normal?: any, // LLSD.Binary
|
||||
TexCoord0?: any, // LLSD.Binary
|
||||
TexCoord0Domain?: {
|
||||
Min: number[],
|
||||
Max: number[]
|
||||
},
|
||||
TriangleList?: any, // LLSD.Binary
|
||||
Weights?: any // LLSD.Binary
|
||||
} = {};
|
||||
if (mesh.noGeometry === true)
|
||||
{
|
||||
data.NoGeometry = true;
|
||||
return data;
|
||||
}
|
||||
if (!mesh.position)
|
||||
{
|
||||
throw new Error('No position data when encoding submesh');
|
||||
}
|
||||
if (mesh.positionDomain !== undefined)
|
||||
{
|
||||
data.Position = new LLSD.Binary(Array.from(this.expandFromDomain(mesh.position, mesh.positionDomain.min, mesh.positionDomain.max)));
|
||||
data.PositionDomain = {
|
||||
Min: LLMesh.fixReal(mesh.positionDomain.min.toArray()),
|
||||
Max: LLMesh.fixReal(mesh.positionDomain.max.toArray())
|
||||
};
|
||||
}
|
||||
if (mesh.texCoord0 && mesh.texCoord0Domain !== undefined)
|
||||
{
|
||||
data.TexCoord0 = new LLSD.Binary(Array.from(this.expandFromDomain(mesh.texCoord0, mesh.texCoord0Domain.min, mesh.texCoord0Domain.max)));
|
||||
data.TexCoord0Domain = {
|
||||
Min: LLMesh.fixReal(mesh.texCoord0Domain.min.toArray()),
|
||||
Max: LLMesh.fixReal(mesh.texCoord0Domain.max.toArray())
|
||||
};
|
||||
}
|
||||
if (mesh.normal)
|
||||
{
|
||||
data.Normal = new LLSD.Binary(Array.from(this.expandFromDomain(mesh.normal, new Vector3([-1.0, -1.0, -1.0]), new Vector3([1.0, 1.0, 1.0]))));
|
||||
}
|
||||
if (mesh.triangleList)
|
||||
{
|
||||
const triangles = Buffer.allocUnsafe(mesh.triangleList.length * 2);
|
||||
let pos = 0;
|
||||
for (let x = 0; x < mesh.triangleList.length; x++)
|
||||
{
|
||||
triangles.writeUInt16LE(mesh.triangleList[x], pos); pos = pos + 2;
|
||||
}
|
||||
data.TriangleList = new LLSD.Binary(Array.from(triangles));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Error('Triangle list is required');
|
||||
}
|
||||
if (mesh.weights)
|
||||
{
|
||||
// Calculate how much space we need
|
||||
let spaceNeeded = 0;
|
||||
for (const weight of mesh.weights)
|
||||
{
|
||||
const keys = Object.keys(weight);
|
||||
spaceNeeded = spaceNeeded + keys.length * 3;
|
||||
if (keys.length < 4)
|
||||
{
|
||||
spaceNeeded = spaceNeeded + 1;
|
||||
}
|
||||
}
|
||||
const weightBuff = Buffer.allocUnsafe(spaceNeeded);
|
||||
let pos = 0;
|
||||
for (const weight of mesh.weights)
|
||||
{
|
||||
const keys = Object.keys(weight);
|
||||
for (const jointID of keys)
|
||||
{
|
||||
weightBuff.writeUInt8(parseInt(jointID, 10), pos++);
|
||||
weightBuff.writeUInt16LE(weight[parseInt(jointID, 10)], pos); pos = pos + 2;
|
||||
}
|
||||
if (keys.length < 4)
|
||||
{
|
||||
weightBuff.writeUInt8(0xFF, pos++);
|
||||
}
|
||||
}
|
||||
data.Weights = new LLSD.Binary(Array.from(weightBuff));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
private expandFromDomain(data: Vector3[] | Vector2[], domainMin: Vector3 | Vector2, domainMax: Vector3 | Vector2)
|
||||
{
|
||||
let length = 4;
|
||||
if (data.length > 0 && data[0] instanceof Vector3)
|
||||
{
|
||||
length = 6;
|
||||
}
|
||||
const buf = Buffer.allocUnsafe(data.length * length);
|
||||
let pos = 0;
|
||||
for (const c of data)
|
||||
{
|
||||
const coord: Vector3 | Vector2 = c;
|
||||
const sizeX = domainMax.x - domainMin.x;
|
||||
const newX = Math.round(((coord.x - domainMin.x) / sizeX) * 65535);
|
||||
|
||||
const sizeY = domainMax.y - domainMin.y;
|
||||
const newY = Math.round(((coord.y - domainMin.y) / sizeY) * 65535);
|
||||
buf.writeUInt16LE(newX, pos); pos = pos + 2;
|
||||
buf.writeUInt16LE(newY, pos); pos = pos + 2;
|
||||
if (coord instanceof Vector3 && domainMin instanceof Vector3 && domainMax instanceof Vector3)
|
||||
{
|
||||
const sizeZ = domainMax.z - domainMin.z;
|
||||
const newZ = Math.round(((coord.z - domainMin.z) / sizeZ) * 65535);
|
||||
buf.writeUInt16LE(newZ, pos); pos = pos + 2;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
private async encodeLODLevel(name: string, submeshes: LLSubMesh[]): Promise<Buffer>
|
||||
{
|
||||
const smList = [];
|
||||
for (const sub of submeshes)
|
||||
{
|
||||
smList.push(this.encodeSubMesh(sub))
|
||||
}
|
||||
const mesh = LLSD.LLSD.formatBinary(smList);
|
||||
return await LLMesh.deflate(Buffer.from(mesh.toArray()));
|
||||
}
|
||||
private async encodePhysicsConvex(conv: LLPhysicsConvex): Promise<Buffer>
|
||||
{
|
||||
const llsd: {
|
||||
'HullList'?: any,
|
||||
'Positions'?: any,
|
||||
'BoundingVerts'?: any,
|
||||
'Min': number[],
|
||||
'Max': number[];
|
||||
} = {
|
||||
Min: LLMesh.fixReal(conv.domain.min.toArray()),
|
||||
Max: LLMesh.fixReal(conv.domain.max.toArray())
|
||||
};
|
||||
const sizeX = conv.domain.max.x - conv.domain.min.x;
|
||||
const sizeY = conv.domain.max.y - conv.domain.min.y;
|
||||
const sizeZ = conv.domain.max.z - conv.domain.min.z;
|
||||
if (conv.hullList)
|
||||
{
|
||||
if (!conv.positions)
|
||||
{
|
||||
throw new Error('Positions must be present if hullList is set.')
|
||||
}
|
||||
llsd.HullList = new LLSD.Binary(conv.hullList);
|
||||
const buf = Buffer.allocUnsafe(conv.positions.length * 6);
|
||||
let pos = 0;
|
||||
for (const vec of conv.positions)
|
||||
{
|
||||
buf.writeUInt16LE(Math.round(((vec.x - conv.domain.min.x) / sizeX) * 65535), pos); pos = pos + 2;
|
||||
buf.writeUInt16LE(Math.round(((vec.y - conv.domain.min.y) / sizeY) * 65535), pos); pos = pos + 2;
|
||||
buf.writeUInt16LE(Math.round(((vec.z - conv.domain.min.z) / sizeZ) * 65535), pos); pos = pos + 2;
|
||||
}
|
||||
llsd.Positions = new LLSD.Binary(Array.from(buf));
|
||||
}
|
||||
{
|
||||
const buf = Buffer.allocUnsafe(conv.boundingVerts.length * 6);
|
||||
let pos = 0;
|
||||
for (const vec of conv.boundingVerts)
|
||||
{
|
||||
buf.writeUInt16LE(Math.round(((vec.x - conv.domain.min.x) / sizeX)) * 65535, pos);
|
||||
pos = pos + 2;
|
||||
buf.writeUInt16LE(Math.round(((vec.y - conv.domain.min.y) / sizeY)) * 65535, pos);
|
||||
pos = pos + 2;
|
||||
buf.writeUInt16LE(Math.round(((vec.z - conv.domain.min.z) / sizeZ)) * 65535, pos);
|
||||
pos = pos + 2;
|
||||
}
|
||||
llsd.BoundingVerts = new LLSD.Binary(Array.from(buf));
|
||||
}
|
||||
const mesh = LLSD.LLSD.formatBinary(llsd);
|
||||
return await LLMesh.deflate(Buffer.from(mesh.toArray()));
|
||||
}
|
||||
private async encodeSkin(skin: LLSkin): Promise<Buffer>
|
||||
{
|
||||
const llsd: {[key: string]: any} = {};
|
||||
llsd['joint_names'] = skin.jointNames;
|
||||
llsd['bind_shape_matrix'] = skin.bindShapeMatrix.toArray();
|
||||
llsd['inverse_bind_matrix'] = [];
|
||||
for (const matrix of skin.inverseBindMatrix)
|
||||
{
|
||||
llsd['inverse_bind_matrix'].push(matrix.toArray())
|
||||
}
|
||||
if (skin.altInverseBindMatrix)
|
||||
{
|
||||
llsd['alt_inverse_bind_matrix'] = [];
|
||||
for (const matrix of skin.altInverseBindMatrix)
|
||||
{
|
||||
llsd['alt_inverse_bind_matrix'].push(matrix.toArray())
|
||||
}
|
||||
}
|
||||
if (skin.pelvisOffset)
|
||||
{
|
||||
llsd['pelvis_offset'] = skin.pelvisOffset.toArray();
|
||||
}
|
||||
const mesh = LLSD.LLSD.formatBinary(llsd);
|
||||
return await LLMesh.deflate(Buffer.from(mesh.toArray()));
|
||||
}
|
||||
async toAsset(): Promise<Buffer>
|
||||
{
|
||||
const llsd: {[key: string]: any} = {
|
||||
'creator': new LLSD.UUID(this.creatorID.toString()),
|
||||
'version': this.version,
|
||||
'date': null
|
||||
};
|
||||
let offset = 0;
|
||||
const bufs = [];
|
||||
for (const lod of Object.keys(this.lodLevels))
|
||||
{
|
||||
const lodBlob = await this.encodeLODLevel(lod, this.lodLevels[lod]);
|
||||
llsd[lod] = {
|
||||
'offset': offset,
|
||||
'size': lodBlob.length
|
||||
};
|
||||
offset += lodBlob.length;
|
||||
bufs.push(lodBlob);
|
||||
}
|
||||
if (this.physicsConvex)
|
||||
{
|
||||
const physBlob = await this.encodePhysicsConvex(this.physicsConvex);
|
||||
llsd['physics_convex'] = {
|
||||
'offset': offset,
|
||||
'size': physBlob.length
|
||||
};
|
||||
offset += physBlob.length;
|
||||
bufs.push(physBlob);
|
||||
|
||||
}
|
||||
if (this.skin)
|
||||
{
|
||||
const skinBlob = await this.encodeSkin(this.skin);
|
||||
llsd['skin'] = {
|
||||
'offset': offset,
|
||||
'size': skinBlob.length
|
||||
};
|
||||
offset += skinBlob.length;
|
||||
bufs.push(skinBlob);
|
||||
}
|
||||
bufs.unshift(Buffer.from(LLSD.LLSD.formatBinary(llsd).toArray()));
|
||||
return Buffer.concat(bufs);
|
||||
}
|
||||
}
|
||||
@@ -39,4 +39,10 @@ export class LightData
|
||||
buf.writeFloatLE(this.Cutoff, pos); pos = pos + 4;
|
||||
buf.writeFloatLE(this.Falloff, pos);
|
||||
}
|
||||
getBuffer(): Buffer
|
||||
{
|
||||
const buf = Buffer.allocUnsafe(16);
|
||||
this.writeToBuffer(buf, 0);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,10 @@ export class LightImageData
|
||||
this.texture.writeToBuffer(buf, pos); pos = pos + 16;
|
||||
this.params.writeToBuffer(buf, pos, false);
|
||||
}
|
||||
getBuffer(): Buffer
|
||||
{
|
||||
const buf = Buffer.allocUnsafe(28);
|
||||
this.writeToBuffer(buf, 0);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,10 @@ export class MeshData
|
||||
this.meshData.writeToBuffer(buf, pos); pos = pos + 16;
|
||||
buf.writeUInt8(this.type, pos);
|
||||
}
|
||||
getBuffer(): Buffer
|
||||
{
|
||||
const buf = Buffer.allocUnsafe(17);
|
||||
this.writeToBuffer(buf, 0);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,10 @@ export class SculptData
|
||||
this.texture.writeToBuffer(buf, pos); pos = pos + 16;
|
||||
buf.writeUInt8(this.type, pos);
|
||||
}
|
||||
getBuffer(): Buffer
|
||||
{
|
||||
const buf = Buffer.allocUnsafe(17);
|
||||
this.writeToBuffer(buf, 0);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
12
lib/classes/public/interfaces/LLPhysicsConvex.ts
Normal file
12
lib/classes/public/interfaces/LLPhysicsConvex.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {Vector3} from '../../Vector3';
|
||||
|
||||
export interface LLPhysicsConvex
|
||||
{
|
||||
hullList?: number[];
|
||||
positions?: Vector3[];
|
||||
boundingVerts: Vector3[];
|
||||
domain: {
|
||||
min: Vector3,
|
||||
max: Vector3
|
||||
}
|
||||
}
|
||||
10
lib/classes/public/interfaces/LLSkin.ts
Normal file
10
lib/classes/public/interfaces/LLSkin.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {mat4} from '../../../tsm/mat4';
|
||||
|
||||
export interface LLSkin
|
||||
{
|
||||
jointNames: string[];
|
||||
bindShapeMatrix: mat4;
|
||||
inverseBindMatrix: mat4[];
|
||||
altInverseBindMatrix?: mat4[];
|
||||
pelvisOffset?: mat4;
|
||||
}
|
||||
21
lib/classes/public/interfaces/LLSubMesh.ts
Normal file
21
lib/classes/public/interfaces/LLSubMesh.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {Vector3} from '../../Vector3';
|
||||
import {Vector2} from '../../Vector2';
|
||||
|
||||
export interface LLSubMesh
|
||||
{
|
||||
noGeometry?: true,
|
||||
position?: Vector3[],
|
||||
positionDomain?: {
|
||||
min: Vector3,
|
||||
max: Vector3
|
||||
},
|
||||
normal?: Vector3[],
|
||||
texCoord0?: Vector2[],
|
||||
texCoord0Domain?: {
|
||||
min: Vector2,
|
||||
max: Vector2
|
||||
}
|
||||
triangleList?: number[],
|
||||
weights?: {[key: number]: number}[],
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user