Improved gltf support + tests

This commit is contained in:
Casper Warden
2023-11-16 02:03:08 +00:00
parent 0bea5c4a6e
commit 537253e411
9 changed files with 720 additions and 234 deletions

View File

@@ -60,6 +60,7 @@ export class Caps
req.push('FetchInventoryDescendents2');
req.push('IncrementCOFVersion');
req.push('InterestList');
req.push('InventoryThumbnailUpload');
req.push('GetDisplayNames');
req.push('GetExperiences');
req.push('AgentExperiences');
@@ -74,9 +75,12 @@ export class Caps
req.push('IsExperienceContributor');
req.push('RegionExperiences');
req.push('ExperienceQuery');
req.push('GetMesh');
req.push('GetMesh2');
req.push('GetMetadata');
req.push('GetObjectCost');
req.push('GetObjectPhysicsData');
req.push('GetTexture');
req.push('GroupAPIv1');
req.push('GroupMemberData');
req.push('GroupProposalBallot');
@@ -86,6 +90,7 @@ export class Caps
req.push('MapLayer');
req.push('MapLayerGod');
req.push('MeshUploadFlag');
req.push('ModifyMaterialParams');
req.push('NavMeshGenerationStatus');
req.push('NewFileAgentInventory');
req.push('ObjectAnimation');
@@ -128,6 +133,8 @@ export class Caps
req.push('UpdateSettingsAgentInventory');
req.push('UpdateSettingsTaskInventory');
req.push('UploadAgentProfileImage');
req.push('UpdateMaterialAgentInventory');
req.push('UpdateMaterialTaskInventory');
req.push('UploadBakedTexture');
req.push('UserInfo');
req.push('ViewerAsset');

View File

@@ -117,7 +117,6 @@ export class Circuit
};
pos.position += packetLength;
}
console.log('Sent packet ' + packetID + ', ' + packetLength + ' bytes');
this.sendMessage(sendXfer, PacketFlags.Reliable);
if (final)
{
@@ -148,7 +147,6 @@ export class Circuit
{
if (pos.position > -1)
{
console.log('Packet confirmed, sending next. Position: ' + pos.position);
packetID++;
this.sendXferPacket(xferID, packetID, data, pos);
}
@@ -160,7 +158,6 @@ export class Circuit
const msg = packet.message as AbortXferMessage;
if (msg.XferID.ID.equals(xferID))
{
console.log('Transfer aborted');
subs.unsubscribe();
reject(new Error('Transfer aborted'));
}

View File

@@ -14,6 +14,43 @@ export interface LLGLTFTexture
export type LLGLTFTextureInfo = LLGLTFTexture & LLGLTFExtensionsAndExtras;
export interface LLGLTFMaterialEntry
{
name?: string;
emissiveFactor?: number[];
alphaMode?: string;
alphaCutoff?: number;
doubleSided?: boolean;
pbrMetallicRoughness?: {
baseColorFactor?: number[];
baseColorTexture?: LLGLTFTextureInfo;
metallicRoughnessTexture?: LLGLTFTextureInfo;
metallicFactor?: number;
roughnessFactor?: number;
} & LLGLTFExtensionsAndExtras,
normalTexture?: {
index: number;
texCoord?: number;
scale?: number;
} & LLGLTFExtensionsAndExtras,
occlusionTexture?: {
index: number;
texCoord?: number;
strength?: number;
} & LLGLTFExtensionsAndExtras,
emissiveTexture?: {
extensions?: {
KHR_texture_transform?: {
offset: number[],
rotation: number,
scale: number[]
}
},
index: number
texCoord?: number;
}
}
export interface LLGLTFMaterialDataPart
{
asset?: {
@@ -95,41 +132,7 @@ export interface LLGLTFMaterialDataPart
nodes?: number[];
} & LLGLTFExtensionsAndExtras)[];
scene?: number;
materials?: ({
name?: string;
emissiveFactor?: number[];
alphaMode?: string;
alphaCutoff?: number;
doubleSided?: boolean;
pbrMetallicRoughness?: {
baseColorFactor: number[];
baseColorTexture?: LLGLTFTextureInfo;
metallicRoughnessTexture?: LLGLTFTextureInfo;
metallicFactor: number;
roughnessFactor: number;
} & LLGLTFExtensionsAndExtras,
normalTexture?: {
index: number;
texCoord?: number;
scale?: number;
} & LLGLTFExtensionsAndExtras,
occlusionTexture?: {
index: number;
texCoord?: number;
strength?: number;
} & LLGLTFExtensionsAndExtras,
emissiveTexture?: {
extensions?: {
KHR_texture_transform?: {
offset: number[],
rotation: number,
scale: number[]
}
},
index: number
texCoord?: number;
}
} & LLGLTFExtensionsAndExtras)[];
materials?: (LLGLTFMaterialEntry & LLGLTFExtensionsAndExtras)[];
images?: (({
bufferView: number;
mimeType: string;

View File

@@ -0,0 +1,239 @@
import { LLGLTFMaterialOverride } from './LLGLTFMaterialOverride';
import * as assert from 'assert';
const m = new LLGLTFMaterialOverride();
m.textures = [
'a6edc906-2f9f-5fb2-a373-efac406f0ef2',
'2f70a4f7-4ece-48d2-8963-32192608067d',
'45a45cc0-463c-49dd-9133-5202399a16d4',
'39bf5b2b-0619-4892-872c-024e2f601684'
];
m.doubleSided = true;
m.emissiveFactor = [
0.1,
0.69,
0.420
];
m.alphaCutoff = 0.42;
m.alphaMode = 1;
m.roughnessFactor = 0.23;
m.metallicFactor = 0.91;
m.baseColor = [
0.1,
0.2,
0.3,
0.4
];
m.textureTransforms = [
{
rotation: 0.43,
scale: [
0.2,
0.4
],
offset: [
1.0,
0.5
]
},
{
rotation: 0.52,
scale: [
0.9,
0.1
],
offset: [
0.8,
0.3
]
},
{
rotation: 0.43,
scale: [
0.1,
0.9
],
offset: [
1.3,
0.6
]
},
{
rotation: 0.43,
scale: [
0.0,
0.11
],
offset: [
0.5,
0.4
]
}
];
const m3 = new LLGLTFMaterialOverride();
m3.textures = [
null,
null,
'45a45cc0-463c-49dd-9133-5202399a16d4',
'39bf5b2b-0619-4892-872c-024e2f601684'
];
m3.doubleSided = false;
m3.emissiveFactor = [
0.1,
0.69,
0.420
];
m3.alphaCutoff = 0.42;
m3.alphaMode = 0;
m3.roughnessFactor = 0.23;
m3.metallicFactor = 0.91;
m3.baseColor = [
0.1,
0.2,
0.3,
0.4
];
m3.textureTransforms = [
{
rotation: 0.43,
scale: [
0.2,
0.4
],
offset: [
1.0,
0.5
]
},
null,
{
rotation: 0.43,
scale: [
0.1,
0.9
],
offset: [
1.3,
0.6
]
},
{
rotation: 0.43,
scale: [
0.0,
0.11
],
offset: [
0.5,
0.4
]
}
];
describe('LLGLTFMaterialOverride', () =>
{
it('outputs valid JSON', () =>
{
assert.equal(m.getFullMaterialJSON(), '{"asset":{"version":"2.0"},"images":[{"uri":"a6edc906-2f9f-5fb2-a373-efac406f0ef2"},{"uri":"45a45cc0-463c-49dd-9133-5202399a16d4"},{"uri":"39bf5b2b-0619-4892-872c-024e2f601684"},{"uri":"2f70a4f7-4ece-48d2-8963-32192608067d"}],"textures":[{"source":0,"extensions":{"KHR_texture_transform":{"offset":[1,0.5],"scale":[0.2,0.4],"rotation":0.43}}},{"source":1,"extensions":{"KHR_texture_transform":{"offset":[1.3,0.6],"scale":[0.1,0.9],"rotation":0.43}}},{"source":2,"extensions":{"KHR_texture_transform":{"offset":[0.5,0.4],"scale":[0,0.11],"rotation":0.43}}},{"source":3,"extensions":{"KHR_texture_transform":{"offset":[0.8,0.3],"scale":[0.9,0.1],"rotation":0.52}}}],"materials":[{"occlusionTexture":{"index":1},"pbrMetallicRoughness":{"baseColorFactor":[0.1,0.2,0.3,0.4],"metallicFactor":0.91,"roughnessFactor":0.23,"baseColorTexture":{"index":0},"metallicRoughnessTexture":{"index":1}},"alphaMode":"BLEND","alphaCutoff":0.42,"emissiveFactor":[0.1,0.69,0.42],"doubleSided":true,"emissiveTexture":{"index":2},"normalTexture":{"index":3}}]}');
})
it('parses json JSON correctly', () =>
{
let json = m.getFullMaterialJSON();
const m2 = LLGLTFMaterialOverride.fromFullMaterialJSON(json);
assert.equal(m2.roughnessFactor, m.roughnessFactor);
assert.equal(m2.doubleSided, m.doubleSided);
assert.equal(m2.alphaCutoff, m.alphaCutoff);
assert.equal(m2.alphaMode, m.alphaMode);
assert.equal(m2.metallicFactor, m.metallicFactor);
assert.equal(m2.baseColor?.length, m.baseColor?.length);
assert.equal(m2.baseColor?.[0], m.baseColor?.[0]);
assert.equal(m2.baseColor?.[1], m.baseColor?.[1]);
assert.equal(m2.baseColor?.[2], m.baseColor?.[2]);
assert.equal(m2.baseColor?.[3], m.baseColor?.[3]);
assert.equal(m2.emissiveFactor?.length, m.emissiveFactor?.length);
assert.equal(m2.emissiveFactor?.[0], m.emissiveFactor?.[0]);
assert.equal(m2.emissiveFactor?.[1], m.emissiveFactor?.[1]);
assert.equal(m2.emissiveFactor?.[2], m.emissiveFactor?.[2]);
assert.equal(m2.textures?.length, m.textures?.length);
assert.equal(m2.textures?.[0], m.textures?.[0]);
assert.equal(m2.textures?.[1], m.textures?.[1]);
assert.equal(m2.textures?.[2], m.textures?.[2]);
assert.equal(m2.textures?.[3], m.textures?.[3]);
assert.equal(m2.textureTransforms?.length, m.textureTransforms?.length);
assert.equal(m2.textureTransforms?.[0]?.offset?.[0], m.textureTransforms?.[0]?.offset?.[0]);
assert.equal(m2.textureTransforms?.[0]?.offset?.[1], m.textureTransforms?.[0]?.offset?.[1]);
assert.equal(m2.textureTransforms?.[0]?.scale?.[0], m.textureTransforms?.[0]?.scale?.[0]);
assert.equal(m2.textureTransforms?.[0]?.scale?.[1], m.textureTransforms?.[0]?.scale?.[1]);
assert.equal(m2.textureTransforms?.[0]?.rotation, m.textureTransforms?.[0]?.rotation);
assert.equal(m2.textureTransforms?.[1]?.offset?.[0], m.textureTransforms?.[1]?.offset?.[0]);
assert.equal(m2.textureTransforms?.[1]?.offset?.[1], m.textureTransforms?.[1]?.offset?.[1]);
assert.equal(m2.textureTransforms?.[1]?.scale?.[0], m.textureTransforms?.[1]?.scale?.[0]);
assert.equal(m2.textureTransforms?.[1]?.scale?.[1], m.textureTransforms?.[1]?.scale?.[1]);
assert.equal(m2.textureTransforms?.[1]?.rotation, m.textureTransforms?.[1]?.rotation);
assert.equal(m2.textureTransforms?.[2]?.offset?.[0], m.textureTransforms?.[2]?.offset?.[0]);
assert.equal(m2.textureTransforms?.[2]?.offset?.[1], m.textureTransforms?.[2]?.offset?.[1]);
assert.equal(m2.textureTransforms?.[2]?.scale?.[0], m.textureTransforms?.[2]?.scale?.[0]);
assert.equal(m2.textureTransforms?.[2]?.scale?.[1], m.textureTransforms?.[2]?.scale?.[1]);
assert.equal(m2.textureTransforms?.[2]?.rotation, m.textureTransforms?.[2]?.rotation);
assert.equal(m2.textureTransforms?.[3]?.offset?.[0], m.textureTransforms?.[3]?.offset?.[0]);
assert.equal(m2.textureTransforms?.[3]?.offset?.[1], m.textureTransforms?.[3]?.offset?.[1]);
assert.equal(m2.textureTransforms?.[3]?.scale?.[0], m.textureTransforms?.[3]?.scale?.[0]);
assert.equal(m2.textureTransforms?.[3]?.scale?.[1], m.textureTransforms?.[3]?.scale?.[1]);
assert.equal(m2.textureTransforms?.[3]?.rotation, m.textureTransforms?.[3]?.rotation);
json = m3.getFullMaterialJSON();
const m4 = LLGLTFMaterialOverride.fromFullMaterialJSON(json);
assert.equal(m4.roughnessFactor, m3.roughnessFactor);
assert.equal(m4.doubleSided, m3.doubleSided);
assert.equal(m4.alphaCutoff, m3.alphaCutoff);
assert.equal(m4.alphaMode, m3.alphaMode);
assert.equal(m4.metallicFactor, m3.metallicFactor);
assert.equal(m4.baseColor?.length, m3.baseColor?.length);
assert.equal(m4.baseColor?.[0], m3.baseColor?.[0]);
assert.equal(m4.baseColor?.[1], m3.baseColor?.[1]);
assert.equal(m4.baseColor?.[2], m3.baseColor?.[2]);
assert.equal(m4.baseColor?.[3], m3.baseColor?.[3]);
assert.equal(m4.emissiveFactor?.length, m3.emissiveFactor?.length);
assert.equal(m4.emissiveFactor?.[0], m3.emissiveFactor?.[0]);
assert.equal(m4.emissiveFactor?.[1], m3.emissiveFactor?.[1]);
assert.equal(m4.emissiveFactor?.[2], m3.emissiveFactor?.[2]);
assert.equal(m4.textures?.length, m3.textures?.length);
assert.equal(m4.textures?.[0], m3.textures?.[0]);
assert.equal(m4.textures?.[1], m3.textures?.[1]);
assert.equal(m4.textures?.[2], m3.textures?.[2]);
assert.equal(m4.textures?.[3], m3.textures?.[3]);
assert.equal(m4.textureTransforms?.length, m3.textureTransforms?.length);
assert.equal(m4.textureTransforms?.[0]?.offset?.[0], m3.textureTransforms?.[0]?.offset?.[0]);
assert.equal(m4.textureTransforms?.[0]?.offset?.[1], m3.textureTransforms?.[0]?.offset?.[1]);
assert.equal(m4.textureTransforms?.[0]?.scale?.[0], m3.textureTransforms?.[0]?.scale?.[0]);
assert.equal(m4.textureTransforms?.[0]?.scale?.[1], m3.textureTransforms?.[0]?.scale?.[1]);
assert.equal(m4.textureTransforms?.[0]?.rotation, m3.textureTransforms?.[0]?.rotation);
assert.equal(m4.textureTransforms?.[1]?.offset?.[0], m3.textureTransforms?.[1]?.offset?.[0]);
assert.equal(m4.textureTransforms?.[1]?.offset?.[1], m3.textureTransforms?.[1]?.offset?.[1]);
assert.equal(m4.textureTransforms?.[1]?.scale?.[0], m3.textureTransforms?.[1]?.scale?.[0]);
assert.equal(m4.textureTransforms?.[1]?.scale?.[1], m3.textureTransforms?.[1]?.scale?.[1]);
assert.equal(m4.textureTransforms?.[1]?.rotation, m3.textureTransforms?.[1]?.rotation);
assert.equal(m4.textureTransforms?.[2]?.offset?.[0], m3.textureTransforms?.[2]?.offset?.[0]);
assert.equal(m4.textureTransforms?.[2]?.offset?.[1], m3.textureTransforms?.[2]?.offset?.[1]);
assert.equal(m4.textureTransforms?.[2]?.scale?.[0], m3.textureTransforms?.[2]?.scale?.[0]);
assert.equal(m4.textureTransforms?.[2]?.scale?.[1], m3.textureTransforms?.[2]?.scale?.[1]);
assert.equal(m4.textureTransforms?.[2]?.rotation, m3.textureTransforms?.[2]?.rotation);
assert.equal(m4.textureTransforms?.[3]?.offset?.[0], m3.textureTransforms?.[3]?.offset?.[0]);
assert.equal(m4.textureTransforms?.[3]?.offset?.[1], m3.textureTransforms?.[3]?.offset?.[1]);
assert.equal(m4.textureTransforms?.[3]?.scale?.[0], m3.textureTransforms?.[3]?.scale?.[0]);
assert.equal(m4.textureTransforms?.[3]?.scale?.[1], m3.textureTransforms?.[3]?.scale?.[1]);
assert.equal(m4.textureTransforms?.[3]?.rotation, m3.textureTransforms?.[3]?.rotation);
});
});

View File

@@ -1,19 +1,397 @@
import {
LLGLTFExtensionsAndExtras,
LLGLTFMaterialData,
LLGLTFMaterialEntry,
LLGLTFTextureInfo,
} from './LLGLTFMaterialData';
import { UUID } from './UUID';
export interface LLGLTFTextureTransformOverride
{
offset: number[];
scale: number[];
rotation: number
offset?: number[];
scale?: number[];
rotation?: number
}
export class LLGLTFMaterialOverride
{
public textures?: string[];
public textures?: (string | null)[];
public baseColor?: number[];
public emissiveColor?: number[];
public emissiveFactor?: number[];
public metallicFactor?: number;
public roughnessFactor?: number;
public alphaMode?: number;
public alphaCutoff?: number;
public doubleSided?: boolean;
public textureTransforms?: LLGLTFTextureTransformOverride[];
public textureTransforms?: (LLGLTFTextureTransformOverride | null)[];
public getFullMaterialJSON(): string
{
const obj: LLGLTFMaterialData = {};
obj.asset = {
version: '2.0',
};
let texIndex = 0;
const material: LLGLTFMaterialEntry & LLGLTFExtensionsAndExtras = {};
const addTexture = (texNum: number): number | undefined =>
{
let idx: number | undefined = undefined;
if ((this.textures && this.textures.length > texNum && this.textures[texNum] !== null) || (this.textureTransforms && this.textureTransforms.length > texNum && this.textureTransforms[texNum] !== null))
{
idx = texIndex++;
if (idx === 0)
{
obj.images = [];
obj.textures = [];
}
const texture = this.textures?.[texNum];
if (texture)
{
obj.images!.push({
uri: texture
});
}
else
{
obj.images!.push({
uri: UUID.zero().toString()
});
}
const tex: {
source: number
} & LLGLTFExtensionsAndExtras = {
source: idx
};
if (this.textureTransforms && this.textureTransforms.length > texNum && this.textureTransforms[texNum] !== null)
{
const trans = this.textureTransforms[texNum];
tex.extensions = {
KHR_texture_transform: {
offset: trans?.offset ?? undefined,
scale: trans?.scale ?? undefined,
rotation: trans?.rotation ?? undefined
}
};
}
if (obj.textures === undefined)
{
obj.textures = [
tex
];
}
else
{
obj.textures.push(tex);
}
}
return idx;
}
if (
this.baseColor !== undefined
|| (this.textures !== undefined
&& ((this.textures.length > 0
&& this.textures[0] !== undefined
)
|| (this.textures.length > 2
&& this.textures[2] !== undefined
)))
|| this.metallicFactor !== undefined
|| this.roughnessFactor !== undefined
)
{
const pbrMetallicRoughness: {
baseColorFactor?: number[];
baseColorTexture?: LLGLTFTextureInfo;
metallicRoughnessTexture?: LLGLTFTextureInfo;
metallicFactor?: number;
roughnessFactor?: number;
} & LLGLTFExtensionsAndExtras = {
baseColorFactor: this.baseColor,
metallicFactor: this.metallicFactor,
roughnessFactor: this.roughnessFactor
};
let texIdx2 = addTexture(0);
if (texIdx2 !== undefined)
{
pbrMetallicRoughness.baseColorTexture = {
index: texIdx2
};
}
texIdx2 = addTexture(2);
if (texIdx2 !== undefined)
{
pbrMetallicRoughness.metallicRoughnessTexture = {
index: texIdx2
};
material.occlusionTexture = {
index: texIdx2
};
}
material.pbrMetallicRoughness = pbrMetallicRoughness;
}
if (this.alphaMode !== undefined)
{
if (this.alphaMode === 0) // OPAQUE
{
material.extras = {
override_alpha_mode: true
};
}
else if (this.alphaMode === 1)
{
material.alphaMode = 'BLEND';
}
else if (this.alphaMode === 2)
{
material.alphaMode = 'MASK';
}
}
if (this.alphaCutoff !== undefined)
{
material.alphaCutoff = this.alphaCutoff;
}
if (this.emissiveFactor !== undefined)
{
material.emissiveFactor = this.emissiveFactor;
}
if (this.doubleSided)
{
material.doubleSided = this.doubleSided;
}
else if (this.doubleSided === false)
{
if (!material.extras)
{
material.extras = {};
}
material.extras.override_double_sided = true;
}
// Emissive Texture
let texIdx = addTexture(3);
if (texIdx !== undefined)
{
material.emissiveTexture = {
index: texIdx
};
}
// Normal Map Texture
texIdx = addTexture(1);
if (texIdx !== undefined)
{
material.normalTexture = {
index: texIdx
};
}
if (Object.keys(material).length > 0)
{
obj.materials = [
material
];
}
return JSON.stringify(obj);
}
public setTexture(idx: number, uuid: string): void
{
if (!this.textures)
{
this.textures = [];
}
for (let x = this.textures.length; x < idx + 1; x++)
{
this.textures.push(null);
}
this.textures[idx] = uuid;
}
public setTransform(idx: number, trans: LLGLTFTextureTransformOverride): void
{
if (!this.textureTransforms)
{
this.textureTransforms = [];
}
for (let x = this.textureTransforms.length; x < idx + 1; x++)
{
this.textureTransforms.push(null);
}
this.textureTransforms[idx] = trans;
}
public static fromFullMaterialJSON(json: string): LLGLTFMaterialOverride
{
const obj = JSON.parse(json) as LLGLTFMaterialData;
const over = new LLGLTFMaterialOverride();
if (!obj.materials?.length)
{
return over;
}
const mat = obj.materials[0];
const getTexture = (idx: number): { uuid: string | null, transform: LLGLTFTextureTransformOverride | null } =>
{
const found: {
uuid: string | null,
transform: LLGLTFTextureTransformOverride | null
} = {
uuid: null,
transform: null
};
if (obj.textures && Array.isArray(obj.textures) && obj.textures.length > idx)
{
const source = obj.textures[idx].source;
if (source !== undefined && obj.images && Array.isArray(obj.images) && obj.images.length > source)
{
const img = obj.images[source];
if ('uri' in img)
{
found.uuid = img.uri ?? null;
if (found.uuid === UUID.zero().toString())
{
found.uuid = null;
}
}
}
const transform = obj.textures[idx].extensions?.KHR_texture_transform as {
offset?: number[],
scale?: number[],
rotation?: number
};
if (transform)
{
found.transform = transform ?? null;
}
}
return found;
};
if (mat.pbrMetallicRoughness)
{
const pbr = mat.pbrMetallicRoughness;
if (pbr.metallicFactor !== undefined)
{
over.metallicFactor = pbr.metallicFactor;
}
if (pbr.roughnessFactor !== undefined)
{
over.roughnessFactor = pbr.roughnessFactor;
}
if (pbr.baseColorFactor !== undefined && Array.isArray(pbr.baseColorFactor) && pbr.baseColorFactor.length === 4)
{
over.baseColor = pbr.baseColorFactor;
}
if (pbr.baseColorTexture?.index !== undefined)
{
const tex = getTexture(pbr.baseColorTexture.index);
if (tex && tex.uuid)
{
over.setTexture(0, tex.uuid);
}
if (tex && tex.transform)
{
over.setTransform(0, tex.transform);
}
}
if (pbr.metallicRoughnessTexture?.index !== undefined)
{
const tex = getTexture(pbr.metallicRoughnessTexture.index);
if (tex && tex.uuid)
{
over.setTexture(2, tex.uuid);
}
if (tex && tex.transform)
{
over.setTransform(2, tex.transform);
}
}
}
if (mat.alphaMode)
{
switch (mat.alphaMode)
{
case 'BLEND':
over.alphaMode = 1;
break;
case 'MASK':
over.alphaMode = 2;
break;
}
}
else if (mat.extras && mat.extras.override_alpha_mode)
{
over.alphaMode = 0;
}
if (mat.alphaCutoff !== undefined)
{
over.alphaCutoff = mat.alphaCutoff;
}
if (mat.emissiveFactor !== undefined)
{
over.emissiveFactor = mat.emissiveFactor;
}
if (mat.doubleSided === true)
{
over.doubleSided = true;
}
else if (mat.extras && mat.extras.override_double_sided)
{
over.doubleSided = false;
}
if (mat.normalTexture?.index !== undefined)
{
const tex = getTexture(mat.normalTexture?.index);
if (tex && tex.uuid)
{
over.setTexture(1, tex.uuid);
}
if (tex && tex.transform)
{
over.setTransform(1, tex.transform);
}
}
if (mat.emissiveTexture?.index !== undefined)
{
const tex = getTexture(mat.emissiveTexture?.index);
if (tex && tex.uuid)
{
over.setTexture(3, tex.uuid);
}
if (tex && tex.transform)
{
over.setTransform(3, tex.transform);
}
}
return over;
}
}

View File

@@ -192,7 +192,7 @@ export class ObjectStoreLite implements IObjectStore
if (emissiveColor !== undefined && Array.isArray(emissiveColor) && emissiveColor.length === 3 && isNumberArray(emissiveColor))
{
override.emissiveColor = emissiveColor;
override.emissiveFactor = emissiveColor;
}
if (metallicFactor !== undefined && typeof metallicFactor === 'number')
@@ -242,9 +242,17 @@ export class ObjectStoreLite implements IObjectStore
override.textureTransforms = [];
for (const transform of textureTransforms)
{
if (isLLGLTFTextureTransformOverride(transform))
if (transform instanceof LLSDMap)
{
override.textureTransforms.push(transform);
const tObj = {
offset: transform.get('o'),
scale: transform.get('s'),
rotation: transform.get('r')
}
if (isLLGLTFTextureTransformOverride(tObj))
{
override.textureTransforms.push(tObj);
}
}
}
}

View File

@@ -35,7 +35,7 @@ describe('GameObject', () =>
'1e079cce-eeca-4e05-9a4f-e58d8398ecf1',
'03573ae5-4c1a-44f7-9cd3-8b49028f9e48'
];
override.emissiveColor = [0.2, 0.5, 0.6];
override.emissiveFactor = [0.2, 0.5, 0.6];
override.baseColor = [0.1, 0.2, 0.3, 0.4];
override.textureTransforms = [
{
@@ -94,12 +94,12 @@ describe('GameObject', () =>
{
assert(false, 'Textures is not an array');
}
if (Array.isArray(entry.emissiveColor))
if (Array.isArray(entry.emissiveFactor))
{
assert.equal(entry.emissiveColor.length, 3);
assert.equal(entry.emissiveColor[0], 0.2);
assert.equal(entry.emissiveColor[1], 0.5);
assert.equal(entry.emissiveColor[2], 0.6);
assert.equal(entry.emissiveFactor.length, 3);
assert.equal(entry.emissiveFactor[0], 0.2);
assert.equal(entry.emissiveFactor[1], 0.5);
assert.equal(entry.emissiveFactor[2], 0.6);
}
else
{
@@ -123,7 +123,7 @@ describe('GameObject', () =>
for (let x = 0; x < 4; x++)
{
const transform = entry.textureTransforms[x];
if (Array.isArray(transform.scale))
if (transform?.scale && Array.isArray(transform.scale))
{
assert.equal(transform.scale.length, 2);
assert.equal(transform.scale[0], 0.5);
@@ -133,7 +133,7 @@ describe('GameObject', () =>
{
assert(false, 'Scale is not an array');
}
if (Array.isArray(transform.offset))
if (transform?.offset && Array.isArray(transform.offset))
{
assert.equal(transform.offset.length, 2);
assert.equal(transform.offset[0], 0.1);

View File

@@ -55,7 +55,7 @@ import { Subject } from 'rxjs';
import { RezScriptMessage } from '../messages/RezScript';
import { PermissionMask } from '../../enums/PermissionMask';
import { AssetType } from '../../enums/AssetType';
import { LLGLTFMaterialOverride, LLGLTFTextureTransformOverride } from '../LLGLTFMaterialOverride';
import { LLGLTFMaterialOverride } from '../LLGLTFMaterialOverride';
import * as uuid from 'uuid';
@@ -381,123 +381,19 @@ export class GameObject implements IGameObjectData
const buf = Buffer.from(prop, 'base64');
go.TextureEntry = TextureEntry.from(buf);
}
if (go.TextureEntry && shape['GLTFMaterialOverrides'] && Array.isArray(shape['GLTFMaterialOverrides']) && shape['GLTFMaterialOverrides'].length > 0)
if (go.TextureEntry && shape['MatOvrd'] && Array.isArray(shape['MatOvrd']) && shape['MatOvrd'].length > 0)
{
const te = go.TextureEntry;
te.gltfMaterialOverrides = new Map<number, LLGLTFMaterialOverride>();
const children = shape['GLTFMaterialOverrides'][0];
const childObj = children['GLTFMaterialOverride'];
if (childObj)
const tex = Buffer.from(shape['MatOvrd'][0], 'base64');
let pos = 0;
const entryCount = tex.readUInt8(pos++);
for (let x = 0; x < entryCount; x++)
{
for (const entry of childObj)
{
const override = new LLGLTFMaterialOverride();
let textureEntry = 0;
if ((prop = Utils.getFromXMLJS(entry, 'TextureEntry')) !== undefined)
{
textureEntry = prop;
}
else
{
continue;
}
if ((prop = Utils.getFromXMLJS(entry, 'DoubleSided')) !== undefined)
{
override.doubleSided = prop;
}
if ((prop = Utils.getFromXMLJS(entry, 'AlphaCutoff')) !== undefined)
{
override.alphaCutoff = parseFloat(prop);
}
if ((prop = Utils.getFromXMLJS(entry, 'RoughnessFactor')) !== undefined)
{
override.roughnessFactor = parseFloat(prop);
}
if ((prop = Utils.getFromXMLJS(entry, 'MetallicFactor')) !== undefined)
{
override.metallicFactor = parseFloat(prop);
}
if ((prop = Utils.getFromXMLJS(entry, 'AlphaMode')) !== undefined)
{
override.alphaMode = prop;
}
if (entry['Textures'] && Array.isArray(entry['Textures']) && entry['Textures'].length > 0)
{
override.textures = [];
const childArr = entry['Textures'][0];
for (const tex of childArr['UUID'])
{
override.textures.push(tex);
}
}
if (entry['EmissiveColor'] && Array.isArray(entry['EmissiveColor']) && entry['EmissiveColor'].length > 0)
{
const childArr = entry['EmissiveColor'][0];
const red = parseFloat(childArr['R'][0]);
const green = parseFloat(childArr['G'][0]);
const blue = parseFloat(childArr['B'][0]);
override.emissiveColor = [
red,
green,
blue
];
}
if (entry['BaseColor'] && Array.isArray(entry['BaseColor']) && entry['BaseColor'].length > 0)
{
const childArr = entry['BaseColor'][0];
const red = parseFloat(childArr['R'][0]);
const green = parseFloat(childArr['G'][0]);
const blue = parseFloat(childArr['B'][0]);
const alpha = parseFloat(childArr['A'][0]);
override.baseColor = [
red,
green,
blue,
alpha
];
}
if (entry['TextureTransforms'] && Array.isArray(entry['TextureTransforms']) && entry['TextureTransforms'].length > 0)
{
const childArr = entry['TextureTransforms'][0];
override.textureTransforms = [];
for (const tex of childArr['Transform'])
{
const t: LLGLTFTextureTransformOverride = {
offset: [],
scale: [],
rotation: 0,
};
if ((prop = Utils.getFromXMLJS(tex, 'Rotation')) !== undefined)
{
t.rotation = parseFloat(prop);
}
if (tex['Offsets'] && Array.isArray(tex['Offsets']) && tex['Offsets'].length > 0)
{
const offsetArr = tex['Offsets'][0];
const xOffset = parseFloat(offsetArr['X'][0]);
const yOffset = parseFloat(offsetArr['Y'][0]);
t.offset = [
xOffset,
yOffset
];
}
if (tex['Scale'] && Array.isArray(tex['Scale']) && tex['Scale'].length > 0)
{
const offsetArr = tex['Scale'][0];
const xOffset = parseFloat(offsetArr['X'][0]);
const yOffset = parseFloat(offsetArr['Y'][0]);
t.scale = [
xOffset,
yOffset
];
}
override.textureTransforms.push(t);
}
}
te.gltfMaterialOverrides.set(textureEntry, override);
}
const te_index = tex.readUInt8(pos++);
const len = tex.readUInt16LE(pos++);
pos++;
const json = tex.slice(pos, pos + len).toString('utf-8');
pos = pos + len;
go.TextureEntry.gltfMaterialOverrides.set(te_index, LLGLTFMaterialOverride.fromFullMaterialJSON(json));
}
}
if ((prop = Utils.getFromXMLJS(shape, 'PathBegin')) !== undefined)
@@ -1661,75 +1557,33 @@ export class GameObject implements IGameObjectData
if (this.TextureEntry.gltfMaterialOverrides)
{
const overrides = shape.ele('GLTFMaterialOverrides');
for (const te of this.TextureEntry.gltfMaterialOverrides.keys())
{
const entry = overrides.ele('GLTFMaterialOverride');
entry.ele('TextureEntry', te);
const override = this.TextureEntry.gltfMaterialOverrides.get(te);
if (override)
{
if (override.doubleSided !== undefined)
{
entry.ele('DoubleSided', override.doubleSided);
}
if (override.alphaCutoff !== undefined)
{
entry.ele('AlphaCutoff', override.alphaCutoff);
}
if (override.roughnessFactor !== undefined)
{
entry.ele('RoughnessFactor', override.roughnessFactor);
}
if (override.metallicFactor !== undefined)
{
entry.ele('MetallicFactor', override.metallicFactor);
}
if (override.alphaMode !== undefined)
{
entry.ele('AlphaMode', override.alphaMode);
}
if (override.textures !== undefined)
{
const texs = entry.ele('Textures');
for (const tex of override.textures)
{
texs.ele('UUID', tex);
}
}
if (override.emissiveColor !== undefined && override.emissiveColor.length === 3)
{
const emissive = entry.ele('EmissiveColor');
emissive.ele('R', override.emissiveColor[0]);
emissive.ele('G', override.emissiveColor[1]);
emissive.ele('B', override.emissiveColor[2]);
}
if (override.baseColor !== undefined && override.baseColor.length === 4)
{
const base = entry.ele('BaseColor');
base.ele('R', override.baseColor[0]);
base.ele('G', override.baseColor[1]);
base.ele('B', override.baseColor[2]);
base.ele('A', override.baseColor[3]);
}
if (override.textureTransforms && override.textureTransforms.length === 4)
{
const transforms = entry.ele('TextureTransforms');
for (const trans of override.textureTransforms)
{
const tfm = transforms.ele('Transform');
tfm.ele('Rotation', trans.rotation);
const overrideKeys = Array.from(this.TextureEntry.gltfMaterialOverrides.keys());
const numEntries = overrideKeys.length;
const offsets = tfm.ele('Offsets');
offsets.ele('X', trans.offset[0]);
offsets.ele('Y', trans.offset[1]);
if (numEntries > 0)
{
const buf: Buffer[] = [];
const scale = tfm.ele('Scale');
scale.ele('X', trans.scale[0]);
scale.ele('Y', trans.scale[1]);
}
const num = Buffer.allocUnsafe(1);
num.writeUInt8(numEntries, 0);
buf.push(num)
for (const overrideKey of overrideKeys)
{
const override = this.TextureEntry.gltfMaterialOverrides.get(overrideKey);
if (override === undefined)
{
continue;
}
const header = Buffer.allocUnsafe(3);
header.writeUInt8(overrideKey, 0);
const json = override.getFullMaterialJSON();
header.writeUInt16LE(json.length, 1);
buf.push(header);
buf.push(Buffer.from(json, 'utf-8'));
}
shape.ele('MatOvrd', Buffer.concat(buf).toString('base64'));
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@caspertech/node-metaverse",
"version": "0.6.6",
"version": "0.6.7",
"description": "A node.js interface for Second Life.",
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",