From 9de705b53c56fd865f4041bdc29c072d68d53709 Mon Sep 17 00:00:00 2001 From: MatsErdkamp <62242064+MatsErdkamp@users.noreply.github.com> Date: Sun, 26 May 2024 16:05:44 +0200 Subject: [PATCH 1/8] Added decodeMeshoptBuffer() --- src/gltf/gltf-parser.ts | 490 +++++++++++++++++++++++++--------------- 1 file changed, 307 insertions(+), 183 deletions(-) diff --git a/src/gltf/gltf-parser.ts b/src/gltf/gltf-parser.ts index b3b6c0a..78bd5a0 100644 --- a/src/gltf/gltf-parser.ts +++ b/src/gltf/gltf-parser.ts @@ -1,30 +1,31 @@ -import { Texture, BaseTexture } from "@pixi/core" -import { ALPHA_MODES, WRAP_MODES } from "@pixi/constants" +import { Texture, BaseTexture } from "@pixi/core"; +import { ALPHA_MODES, WRAP_MODES } from "@pixi/constants"; -import { glTFChannel } from "./animation/gltf-channel" -import { glTFAsset } from "./gltf-asset" -import { glTFAnimation } from "./animation/gltf-animation" -import { glTFAttribute } from "./gltf-attribute" -import { glTFMaterial } from "./gltf-material" -import { Mesh3D } from "../mesh/mesh" -import { Container3D } from "../container" -import { Material } from "../material/material" -import { MaterialFactory } from "../material/material-factory" -import { MeshGeometry3D } from "../mesh/geometry/mesh-geometry" -import { Model } from "../model" -import { Matrix4x4 } from "../transform/matrix" -import { Skin } from "../skinning/skin" -import { Joint } from "../skinning/joint" -import { StandardMaterialFactory } from "../material/standard/standard-material-factory" -import { glTFChannelFactory } from "./animation/gltf-channel-factory" +import { glTFChannel } from "./animation/gltf-channel"; +import { glTFAsset } from "./gltf-asset"; +import { glTFAnimation } from "./animation/gltf-animation"; +import { glTFAttribute } from "./gltf-attribute"; +import { glTFMaterial } from "./gltf-material"; +import { Mesh3D } from "../mesh/mesh"; +import { Container3D } from "../container"; +import { Material } from "../material/material"; +import { MaterialFactory } from "../material/material-factory"; +import { MeshGeometry3D } from "../mesh/geometry/mesh-geometry"; +import { Model } from "../model"; +import { Matrix4x4 } from "../transform/matrix"; +import { Skin } from "../skinning/skin"; +import { Joint } from "../skinning/joint"; +import { StandardMaterialFactory } from "../material/standard/standard-material-factory"; +import { glTFChannelFactory } from "./animation/gltf-channel-factory"; +import { MeshoptDecoder } from "meshoptimizer"; /** * Parses glTF assets and creates models and meshes. */ export class glTFParser { - private _asset: glTFAsset - private _materialFactory: MaterialFactory - private _descriptor: any + private _asset: glTFAsset; + private _materialFactory: MaterialFactory; + private _descriptor: any; /** * Creates a new parser using the specified asset. @@ -32,12 +33,12 @@ export class glTFParser { * @param materialFactory The material factory to use. */ constructor(asset: glTFAsset, materialFactory?: MaterialFactory) { - this._asset = asset - this._materialFactory = materialFactory || new StandardMaterialFactory() - this._descriptor = this._asset.descriptor + this._asset = asset; + this._materialFactory = materialFactory || new StandardMaterialFactory(); + this._descriptor = this._asset.descriptor; if (asset.textures.length === 0) { for (let i = 0; i < this._descriptor.textures?.length; i++) { - asset.textures.push(this.parseTexture(i)) + asset.textures.push(this.parseTexture(i)); } } } @@ -48,7 +49,7 @@ export class glTFParser { * @param materialFactory The material factory to use. */ static createModel(asset: glTFAsset, materialFactory?: MaterialFactory) { - return new glTFParser(asset, materialFactory).parseModel() + return new glTFParser(asset, materialFactory).parseModel(); } /** @@ -57,8 +58,12 @@ export class glTFParser { * @param materialFactory The material factory to use. * @param mesh The mesh index in the JSON descriptor. */ - static createMesh(asset: glTFAsset, materialFactory?: MaterialFactory, mesh = 0) { - return new glTFParser(asset, materialFactory).parseMesh(mesh) + static createMesh( + asset: glTFAsset, + materialFactory?: MaterialFactory, + mesh = 0 + ) { + return new glTFParser(asset, materialFactory).parseMesh(mesh); } /** @@ -66,24 +71,71 @@ export class glTFParser { * @param accessor The accessor object or index. */ parseBuffer(accessor: any) { - if (accessor === undefined) { return undefined } + if (accessor === undefined) { + return undefined; + } if (typeof accessor === "number") { - accessor = this._asset.descriptor.accessors[accessor] + accessor = this._asset.descriptor.accessors[accessor]; } - let bufferView = this._descriptor.bufferViews[accessor.bufferView || 0] - let offset = accessor.byteOffset || 0 + let bufferView = this._descriptor.bufferViews[accessor.bufferView || 0]; + let offset = accessor.byteOffset || 0; if (bufferView.byteOffset !== undefined) { - offset += bufferView.byteOffset + offset += bufferView.byteOffset; } - let size = accessor.count * componentCount[accessor.type] + let size = accessor.count * componentCount[accessor.type]; if (bufferView.byteStride !== undefined && bufferView.byteStride !== 0) { - size = bufferView.byteStride / componentSize[accessor.componentType] * (accessor.count - 1) + componentCount[accessor.type] + size = + (bufferView.byteStride / componentSize[accessor.componentType]) * + (accessor.count - 1) + + componentCount[accessor.type]; } - let buffer = this._asset.buffers[bufferView.buffer] + const normalized = accessor.normalized === true; + let buffer = this._asset.buffers[bufferView.buffer]; + + if (bufferView.extensions?.EXT_meshopt_compression != undefined) { + const meshoptExtension = bufferView.extensions.EXT_meshopt_compression; + buffer = this.decodeMeshoptBuffer(meshoptExtension); + offset = 0; + } return glTFAttribute.from( - accessor.componentType, componentCount[accessor.type], buffer, offset, size, bufferView.byteStride, normalized, accessor.min, accessor.max) + accessor.componentType, + componentCount[accessor.type], + buffer, + offset, + size, + bufferView.byteStride, + normalized, + accessor.min, + accessor.max + ); + } + + /** + * Uses meshoptimizer to decode a 'EXT_meshopt_compression' buffer. + * @param meshoptExtension The extension.EXT_meshopt_compression Object. + */ + decodeMeshoptBuffer(meshoptExtension: any) { + let meshoptBuffer = this._asset.buffers[meshoptExtension.buffer]; + const byteOffset = meshoptExtension.byteOffset || 0; + const byteLength = meshoptExtension.byteLength || meshoptBuffer.byteLength; + const count = meshoptExtension.count; + const stride = meshoptExtension.byteStride; + + // Decode the buffer using MeshoptDecoder + const resultBuffer = new Uint8Array(count * stride); + + MeshoptDecoder.decodeGltfBuffer( + resultBuffer, + count, + stride, + new Uint8Array(meshoptBuffer, byteOffset, byteLength), + meshoptExtension.mode, + meshoptExtension.filter + ); + + return resultBuffer.buffer; } /** @@ -93,26 +145,31 @@ export class glTFParser { */ parseAnimation(animation: any, nodes: Container3D[]) { if (typeof animation === "number") { - animation = this._asset.descriptor.animations[animation] + animation = this._asset.descriptor.animations[animation]; } - let channels: glTFChannel[] = [] + let channels: glTFChannel[] = []; for (let channel of animation.channels) { - let sampler = animation.samplers[channel.sampler] - let input = this.parseBuffer(sampler.input) + let sampler = animation.samplers[channel.sampler]; + let input = this.parseBuffer(sampler.input); if (input === undefined) { - continue + continue; } - let output = this.parseBuffer(sampler.output) + let output = this.parseBuffer(sampler.output); if (output === undefined) { - continue + continue; } let animationChannel = glTFChannelFactory.create( - input.buffer, output.buffer, sampler.interpolation || "LINEAR", channel.target.path, nodes[channel.target.node]) + input.buffer, + output.buffer, + sampler.interpolation || "LINEAR", + channel.target.path, + nodes[channel.target.node] + ); if (animationChannel) { - channels.push(animationChannel) + channels.push(animationChannel); } } - return new glTFAnimation(channels, animation.name) + return new glTFAnimation(channels, animation.name); } /** @@ -121,90 +178,136 @@ export class glTFParser { */ parseMaterial(material?: any) { if (typeof material === "number") { - material = this._asset.descriptor.materials[material] + material = this._asset.descriptor.materials[material]; } - let result = new glTFMaterial() + let result = new glTFMaterial(); if (!material) { - return this._materialFactory.create(result) + return this._materialFactory.create(result); } if (material.occlusionTexture !== undefined) { - result.occlusionTexture = this._asset.textures[material.occlusionTexture.index].clone() - result.occlusionTexture.strength = material.occlusionTexture.strength - result.occlusionTexture.texCoord = material.occlusionTexture.texCoord - if (material.occlusionTexture.extensions && material.occlusionTexture.extensions.KHR_texture_transform) { - result.occlusionTexture.transform = material.occlusionTexture.extensions.KHR_texture_transform - if (material.occlusionTexture.extensions.KHR_texture_transform.texCoord !== undefined) { - result.occlusionTexture.texCoord = material.occlusionTexture.extensions.KHR_texture_transform.texCoord + result.occlusionTexture = + this._asset.textures[material.occlusionTexture.index].clone(); + result.occlusionTexture.strength = material.occlusionTexture.strength; + result.occlusionTexture.texCoord = material.occlusionTexture.texCoord; + if ( + material.occlusionTexture.extensions && + material.occlusionTexture.extensions.KHR_texture_transform + ) { + result.occlusionTexture.transform = + material.occlusionTexture.extensions.KHR_texture_transform; + if ( + material.occlusionTexture.extensions.KHR_texture_transform + .texCoord !== undefined + ) { + result.occlusionTexture.texCoord = + material.occlusionTexture.extensions.KHR_texture_transform.texCoord; } } } if (material.normalTexture !== undefined) { - result.normalTexture = this._asset.textures[material.normalTexture.index].clone() - result.normalTexture.scale = material.normalTexture.scale || 1 - result.normalTexture.texCoord = material.normalTexture.texCoord - if (material.normalTexture.extensions && material.normalTexture.extensions.KHR_texture_transform) { - result.normalTexture.transform = material.normalTexture.extensions.KHR_texture_transform - if (material.normalTexture.extensions.KHR_texture_transform.texCoord !== undefined) { - result.normalTexture.texCoord = material.normalTexture.extensions.KHR_texture_transform.texCoord + result.normalTexture = + this._asset.textures[material.normalTexture.index].clone(); + result.normalTexture.scale = material.normalTexture.scale || 1; + result.normalTexture.texCoord = material.normalTexture.texCoord; + if ( + material.normalTexture.extensions && + material.normalTexture.extensions.KHR_texture_transform + ) { + result.normalTexture.transform = + material.normalTexture.extensions.KHR_texture_transform; + if ( + material.normalTexture.extensions.KHR_texture_transform.texCoord !== + undefined + ) { + result.normalTexture.texCoord = + material.normalTexture.extensions.KHR_texture_transform.texCoord; } } } if (material.emissiveTexture !== undefined) { - result.emissiveTexture = this._asset.textures[material.emissiveTexture.index].clone() - result.emissiveTexture.texCoord = material.emissiveTexture.texCoord - if (material.emissiveTexture.extensions && material.emissiveTexture.extensions.KHR_texture_transform) { - result.emissiveTexture.transform = material.emissiveTexture.extensions.KHR_texture_transform - if (material.emissiveTexture.extensions.KHR_texture_transform.texCoord !== undefined) { - result.emissiveTexture.texCoord = material.emissiveTexture.extensions.KHR_texture_transform.texCoord + result.emissiveTexture = + this._asset.textures[material.emissiveTexture.index].clone(); + result.emissiveTexture.texCoord = material.emissiveTexture.texCoord; + if ( + material.emissiveTexture.extensions && + material.emissiveTexture.extensions.KHR_texture_transform + ) { + result.emissiveTexture.transform = + material.emissiveTexture.extensions.KHR_texture_transform; + if ( + material.emissiveTexture.extensions.KHR_texture_transform.texCoord !== + undefined + ) { + result.emissiveTexture.texCoord = + material.emissiveTexture.extensions.KHR_texture_transform.texCoord; } } } if (material.doubleSided !== undefined) { - result.doubleSided = material.doubleSided + result.doubleSided = material.doubleSided; } if (material.emissiveFactor) { - result.emissiveFactor = material.emissiveFactor + result.emissiveFactor = material.emissiveFactor; } if (material.alphaMode) { - result.alphaMode = material.alphaMode + result.alphaMode = material.alphaMode; } if (material.alphaCutoff !== undefined) { - result.alphaCutoff = material.alphaCutoff + result.alphaCutoff = material.alphaCutoff; } - let pbr = material.pbrMetallicRoughness + let pbr = material.pbrMetallicRoughness; if (pbr?.metallicRoughnessTexture !== undefined) { - result.metallicRoughnessTexture = this._asset.textures[pbr.metallicRoughnessTexture.index].clone() - result.metallicRoughnessTexture.texCoord = pbr.metallicRoughnessTexture.texCoord - if (pbr.metallicRoughnessTexture.extensions && pbr.metallicRoughnessTexture.extensions.KHR_texture_transform) { - result.metallicRoughnessTexture.transform = pbr.metallicRoughnessTexture.extensions.KHR_texture_transform - if (pbr.metallicRoughnessTexture.extensions.KHR_texture_transform.texCoord !== undefined) { - result.metallicRoughnessTexture.texCoord = pbr.metallicRoughnessTexture.extensions.KHR_texture_transform.texCoord + result.metallicRoughnessTexture = + this._asset.textures[pbr.metallicRoughnessTexture.index].clone(); + result.metallicRoughnessTexture.texCoord = + pbr.metallicRoughnessTexture.texCoord; + if ( + pbr.metallicRoughnessTexture.extensions && + pbr.metallicRoughnessTexture.extensions.KHR_texture_transform + ) { + result.metallicRoughnessTexture.transform = + pbr.metallicRoughnessTexture.extensions.KHR_texture_transform; + if ( + pbr.metallicRoughnessTexture.extensions.KHR_texture_transform + .texCoord !== undefined + ) { + result.metallicRoughnessTexture.texCoord = + pbr.metallicRoughnessTexture.extensions.KHR_texture_transform.texCoord; } } } if (pbr?.baseColorFactor) { - result.baseColor = pbr.baseColorFactor + result.baseColor = pbr.baseColorFactor; } if (pbr?.baseColorTexture !== undefined) { - result.baseColorTexture = this._asset.textures[pbr.baseColorTexture.index].clone() - result.baseColorTexture.texCoord = pbr.baseColorTexture.texCoord - if (pbr.baseColorTexture.extensions && pbr.baseColorTexture.extensions.KHR_texture_transform) { - result.baseColorTexture.transform = pbr.baseColorTexture.extensions.KHR_texture_transform - if (pbr.baseColorTexture.extensions.KHR_texture_transform.texCoord !== undefined) { - result.baseColorTexture.texCoord = pbr.baseColorTexture.extensions.KHR_texture_transform.texCoord + result.baseColorTexture = + this._asset.textures[pbr.baseColorTexture.index].clone(); + result.baseColorTexture.texCoord = pbr.baseColorTexture.texCoord; + if ( + pbr.baseColorTexture.extensions && + pbr.baseColorTexture.extensions.KHR_texture_transform + ) { + result.baseColorTexture.transform = + pbr.baseColorTexture.extensions.KHR_texture_transform; + if ( + pbr.baseColorTexture.extensions.KHR_texture_transform.texCoord !== + undefined + ) { + result.baseColorTexture.texCoord = + pbr.baseColorTexture.extensions.KHR_texture_transform.texCoord; } } } if (pbr?.metallicFactor !== undefined) { - result.metallic = pbr.metallicFactor + result.metallic = pbr.metallicFactor; } if (pbr?.roughnessFactor !== undefined) { - result.roughness = pbr.roughnessFactor + result.roughness = pbr.roughnessFactor; } if (material.extensions) { - result.unlit = material.extensions["KHR_materials_unlit"] !== undefined + result.unlit = material.extensions["KHR_materials_unlit"] !== undefined; } - return this._materialFactory.create(result) + return this._materialFactory.create(result); } /** @@ -213,7 +316,7 @@ export class glTFParser { */ parseTexture(index: number) { const texture = this._descriptor.textures[index]; - const image = this._asset.images[this.findTextureSource(texture)]; + const image = this._asset.images[texture.source]; const result = new Texture( new BaseTexture(image.baseTexture.resource, { wrapMode: WRAP_MODES.REPEAT, @@ -244,42 +347,27 @@ export class glTFParser { return result; } - findTextureSource(obj) { - // Base case: Check if the current object directly contains the `source` key - if (obj && typeof obj === "object" && "source" in obj) { - return obj.source; - } - - // Recursively check each key in the object - for (const key in obj) { - if (obj[key] && typeof obj[key] === "object") { - const result = this.findTextureSource(obj[key]); - if (result !== undefined) { - return result; - } - } - } - return undefined; - } - /** * Creates an array of meshes from the specified mesh. * @param mesh The source mesh object or index. - * @returns An array which contain arrays of meshes. This is because of the - * structure used in glTF, where each mesh contain a number of primitives. + * @returns An array which contain arrays of meshes. This is because of the + * structure used in glTF, where each mesh contain a number of primitives. * Read more about this in discussion at https://github.com/KhronosGroup/glTF/issues/821 */ parseMesh(mesh: any) { if (typeof mesh === "number") { - mesh = this._asset.descriptor.meshes[mesh] + mesh = this._asset.descriptor.meshes[mesh]; } - let weights = mesh.weights || [] + let weights = mesh.weights || []; return mesh.primitives.map((primitive: any) => { - return Object.assign>(this.parsePrimitive(primitive), { - name: mesh.name, - targetWeights: weights - }) - }) + return Object.assign>( + this.parsePrimitive(primitive), + { + name: mesh.name, + targetWeights: weights, + } + ); + }); } /** @@ -290,10 +378,12 @@ export class glTFParser { */ parseSkin(skin: any, target: Container3D, nodes: Container3D[]) { if (typeof skin === "number") { - skin = this._asset.descriptor.skins[skin] + skin = this._asset.descriptor.skins[skin]; } - return new Skin(target, - skin.joints.map((joint: number) => nodes[joint])) + return new Skin( + target, + skin.joints.map((joint: number) => nodes[joint]) + ); } /** @@ -301,42 +391,47 @@ export class glTFParser { * @param primitive The source primitive object. */ parsePrimitive(primitive: any) { - let { attributes, targets } = primitive - let geometry = Object.assign>(new MeshGeometry3D(), { - indices: this.parseBuffer(primitive.indices), - positions: this.parseBuffer(attributes["POSITION"]), - normals: this.parseBuffer(attributes["NORMAL"]), - tangents: this.parseBuffer(attributes["TANGENT"]), - joints: this.parseBuffer(attributes["JOINTS_0"]), - weights: this.parseBuffer(attributes["WEIGHTS_0"]), - colors: this.parseBuffer(attributes["COLOR_0"]) - }) + let { attributes, targets } = primitive; + + let geometry = Object.assign>( + new MeshGeometry3D(), + { + indices: this.parseBuffer(primitive.indices), + positions: this.parseBuffer(attributes["POSITION"]), + normals: this.parseBuffer(attributes["NORMAL"]), + tangents: this.parseBuffer(attributes["TANGENT"]), + joints: this.parseBuffer(attributes["JOINTS_0"]), + weights: this.parseBuffer(attributes["WEIGHTS_0"]), + colors: this.parseBuffer(attributes["COLOR_0"]), + } + ); for (let i = 0; true; i++) { - let buffer = this.parseBuffer(attributes[`TEXCOORD_${i}`]) + let buffer = this.parseBuffer(attributes[`TEXCOORD_${i}`]); if (buffer === undefined) { - break + break; } - geometry.uvs = geometry.uvs || [] - geometry.uvs.push(buffer) + geometry.uvs = geometry.uvs || []; + geometry.uvs.push(buffer); } if (targets) { for (let i = 0; i < targets.length; i++) { - geometry.targets = geometry.targets || [] + geometry.targets = geometry.targets || []; geometry.targets.push({ positions: this.parseBuffer(targets[i]["POSITION"]), normals: this.parseBuffer(targets[i]["NORMAL"]), - tangents: this.parseBuffer(targets[i]["TANGENT"]) - }) + tangents: this.parseBuffer(targets[i]["TANGENT"]), + }); } } - let material: Material + let material: Material; if (primitive.material !== undefined) { material = this.parseMaterial( - this._asset.descriptor.materials[primitive.material]) + this._asset.descriptor.materials[primitive.material] + ); } else { - material = this.parseMaterial() + material = this.parseMaterial(); } - return new Mesh3D(geometry, material) + return new Mesh3D(geometry, material); } /** @@ -344,85 +439,114 @@ export class glTFParser { * @param node The index of the node. */ parseNode(index: number) { - const node = this._asset.descriptor.nodes[index] - let joint: Joint | undefined + const node = this._asset.descriptor.nodes[index]; + let joint: Joint | undefined; for (let skin of this._asset.descriptor.skins || []) { - const i = skin.joints.indexOf(index) + const i = skin.joints.indexOf(index); if (i >= 0) { // This node is a joint - const inverseBindMatrices = this.parseBuffer(skin.inverseBindMatrices) - const inverseBindMatrix = inverseBindMatrices?.buffer.slice(i * 16, i * 16 + 16) - joint = Object.assign>(new Joint(inverseBindMatrix), { - name: node.name - }) + const inverseBindMatrices = this.parseBuffer(skin.inverseBindMatrices); + const inverseBindMatrix = ( + inverseBindMatrices?.buffer.slice(i * 16, i * 16 + 16) + ); + joint = Object.assign>( + new Joint(inverseBindMatrix), + { + name: node.name, + } + ); } } - let container = joint || Object.assign>(new Container3D(), { - name: node.name - }) + let container = + joint || + Object.assign>(new Container3D(), { + name: node.name, + }); if (node.translation) { container.position.set( - node.translation[0], node.translation[1], node.translation[2] - ) + node.translation[0], + node.translation[1], + node.translation[2] + ); } if (node.rotation) { container.rotationQuaternion.set( - node.rotation[0], node.rotation[1], node.rotation[2], node.rotation[3] - ) + node.rotation[0], + node.rotation[1], + node.rotation[2], + node.rotation[3] + ); } if (node.scale) { - container.scale.set(node.scale[0], node.scale[1], node.scale[2]) + container.scale.set(node.scale[0], node.scale[1], node.scale[2]); } if (node.matrix) { - container.transform.setFromMatrix(new Matrix4x4(node.matrix)) + container.transform.setFromMatrix(new Matrix4x4(node.matrix)); } - return container + return container; } parseModel() { - let nodes = this._descriptor.nodes.map((n: any, i: number) => { - return this.parseNode(i) - }) - let scene = this._descriptor.scenes[this._asset.descriptor.scene || 0] - let model = new Model() + let nodes = this._descriptor.nodes.map( + (n: any, i: number) => { + return this.parseNode(i); + } + ); + let scene = this._descriptor.scenes[this._asset.descriptor.scene || 0]; + let model = new Model(); let createHierarchy = (parent: Container3D, node: number) => { - let mesh = this._asset.descriptor.nodes[node].mesh - let skin: Skin | undefined + let mesh = this._asset.descriptor.nodes[node].mesh; + let skin: Skin | undefined; if (this._asset.descriptor.nodes[node].skin !== undefined) { - skin = this.parseSkin(this._asset.descriptor.nodes[node].skin, nodes[node], nodes) + skin = this.parseSkin( + this._asset.descriptor.nodes[node].skin, + nodes[node], + nodes + ); } if (mesh !== undefined) { for (let primitive of this.parseMesh(mesh)) { - model.meshes.push(nodes[node].addChild(primitive)) - model.meshes[model.meshes.length - 1].skin = skin + model.meshes.push(nodes[node].addChild(primitive)); + model.meshes[model.meshes.length - 1].skin = skin; } } - parent.addChild(nodes[node]) + parent.addChild(nodes[node]); if (!this._asset.descriptor.nodes[node].children) { - return + return; } for (let child of this._asset.descriptor.nodes[node].children) { - createHierarchy(nodes[node], child) + createHierarchy(nodes[node], child); } - } + }; for (let node of scene.nodes) { - createHierarchy(model, node) + createHierarchy(model, node); } if (this._asset.descriptor.animations) { for (let animation of this._asset.descriptor.animations) { - model.animations.push(this.parseAnimation(animation, nodes)) + model.animations.push(this.parseAnimation(animation, nodes)); } } - return model + return model; } } const componentCount: { [name: string]: number } = { - SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 -} + SCALAR: 1, + VEC2: 2, + VEC3: 3, + VEC4: 4, + MAT2: 4, + MAT3: 9, + MAT4: 16, +}; const componentSize: { [name: number]: number } = { - [5120]: 1, [5121]: 1, [5122]: 2, [5123]: 2, [5125]: 4, [5126]: 4 -} + [5120]: 1, + [5121]: 1, + [5122]: 2, + [5123]: 2, + [5125]: 4, + [5126]: 4, +}; From e61823a5aaf8afa1e3542fcd0c19389a38ac611f Mon Sep 17 00:00:00 2001 From: MatsErdkamp Date: Sun, 26 May 2024 16:09:26 +0200 Subject: [PATCH 2/8] added meshoptimizer dep --- package-lock.json | 23 +++++++++++++++++++++++ package.json | 3 +++ 2 files changed, 26 insertions(+) diff --git a/package-lock.json b/package-lock.json index fb44ca3..40213bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "pixi3d", "version": "2.5.0", "license": "MIT", + "dependencies": { + "meshoptimizer": "^0.20.0" + }, "devDependencies": { "@pixi/assets": "^6.5.1", "@rollup/plugin-commonjs": "^22.0.1", @@ -120,6 +123,21 @@ "@pixi/utils": "6.5.1" } }, + "node_modules/@pixi/basis": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@pixi/basis/-/basis-6.5.1.tgz", + "integrity": "sha512-afecmtiGf0/ESqc0sMP4Pbd1jlHmu/3IVrkB2mCfG3UtLbTLUYNLw3Aj1D5/OYDm0ANA7Sxzwwzg7+/bPfqhkw==", + "dev": true, + "peer": true, + "peerDependencies": { + "@pixi/compressed-textures": "6.5.1", + "@pixi/constants": "6.5.1", + "@pixi/core": "6.5.1", + "@pixi/loaders": "6.5.1", + "@pixi/runner": "6.5.1", + "@pixi/settings": "6.5.1" + } + }, "node_modules/@pixi/compressed-textures": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/@pixi/compressed-textures/-/compressed-textures-6.5.1.tgz", @@ -2170,6 +2188,11 @@ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, + "node_modules/meshoptimizer": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.20.0.tgz", + "integrity": "sha512-olcJ1q+YVnjroRJpCL1Dj5aZxr2JMr2hRutMUwhuHZvpAL7SIZgOT6eMlFF4TbBGSR89tawE/gqB79J/LrW/Nw==" + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", diff --git a/package.json b/package.json index f8dce31..e86861f 100644 --- a/package.json +++ b/package.json @@ -89,5 +89,8 @@ "tslib": "^2.3.0", "typedoc": "^0.22.9", "typescript": "^4.4.4" + }, + "dependencies": { + "meshoptimizer": "^0.20.0" } } From 9929b1b8b8a96d02ec0605513db1b2492c9a4019 Mon Sep 17 00:00:00 2001 From: MatsErdkamp Date: Sun, 26 May 2024 16:38:48 +0200 Subject: [PATCH 3/8] added a (failing) test.. investigating --- .DS_Store | Bin 0 -> 6148 bytes serve/assets/vase/.DS_Store | Bin 0 -> 6148 bytes test/.DS_Store | Bin 0 -> 6148 bytes test/assets/.DS_Store | Bin 0 -> 6148 bytes test/assets/teapot/teapot-binary-meshopt.glb | Bin 0 -> 128220 bytes test/gltf.test.mjs | 93 +++++++++++-------- 6 files changed, 54 insertions(+), 39 deletions(-) create mode 100644 .DS_Store create mode 100644 serve/assets/vase/.DS_Store create mode 100644 test/.DS_Store create mode 100644 test/assets/.DS_Store create mode 100644 test/assets/teapot/teapot-binary-meshopt.glb diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c3ac046f6c51a2cd4bc2df32c603e493458c382e GIT binary patch literal 6148 zcmeH~F^{dt=u>Bl;zddfqZjB=JE!l7EiIwCR znHYd=e=Zwf1hAw#vGy=AV?N-4FWm9?zTD5J+wJN_+D8XGrH`2G=e8gPq<|EV0#ZN< z%t(Pe#+RQndL})J6p#Y*P{6+rh3>4$))}7;h8O|Jf#oo+W0oL`7s#4yovhF-rw7Yc zi!sFO(N31Ut|nV&Z-?dZVR>isDTZdf9afmotOgXMfE1W0u;}^d=l`Dm-~2ymQ7Q$b zz?&&x!|t%#@}=@@{quTWKW5d}jZVhp3{O7+O#CQb(Zjf3d_mS^>tuzdAAyiTK?=N7 FfnTX=61e~X literal 0 HcmV?d00001 diff --git a/serve/assets/vase/.DS_Store b/serve/assets/vase/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..937a4837a24e020d2b293e314dd5078e6a13aa3e GIT binary patch literal 6148 zcmeHKyH3ME5S)b+K`BT{d0&9UADp64Q1b)e1ZYlj5lHQh&xY9tQY^FHE?ibh9*uWI#uF|Ax>w!L|qLW9GwoamHD1nS>lEw zwmRd*$|2P;$5bE{7%On;)3x^hd-}}%e@x0*Dv%2NDFtM)ecW#NOVL{=Kc~I6(4Xi( n##$@qFggTqqOJJmq^{^Q>uTWOXmrMnPRxUV>XMcU{DuPGL$V_# literal 0 HcmV?d00001 diff --git a/test/.DS_Store b/test/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2564ef96e30d5d784ed89c52c8e1409f56e5b866 GIT binary patch literal 6148 zcmeHKJ8r`;3?&<*2#_UXM_r*e5DfPOy+E2KnF4rlvU}>edbB=%h8j*bcWCfvP*0-x zWcUrDDI(fl53eFy5n00x<;#Y-*}nPAo-(39IL^4pa5@a1`@TQVvTp~B`z#+a;5q*0 z+XjsaPys4H1*iZOSX_ZDvFpv^C-XomKn4E00(L(XxM58k1O4j2;4J{KLD&s*?GL$1|kB}paO%c*1ee_?4w_!NWvabh>`zR-Qmsk1x z&9@C26`%rCfC^9nDzLZ$Sz_0l#ZTseRDcTHy8`xoC~(7?I0pKs1B15!zy@JA%)OTY z77GAt;uwesOoIvxs%DF!K}Wn~UQHYWgD#rQhvv+Eq>YYTi2x14Xd8RkyG;N=+T1dQ*k4A(OWmIEJ|;g zT57k_Txq#xZsGS0v%))% zTo68c-t+?88=Td>Z}&l{o?kq5dhxV^X|sp08&-2d5*3zALyrljnDD9N=HfY1T#ALg z%$CF?tJ$2AI2yN#&n+?ocO#!V@jUoauObK$rtvkGFu$IqKx zkXu;DhJtp-mlPHj%p6u!FekjTc~Oj$uwgy2EWd-SKh{A)`wkNC+Cf6QakuSY_RNxB zAAqGz`@f5`W_0G@to7ewWy-Jf^0$Ucg#7wY%}KZQ@H+=pv)nv=A@MNUHs;xS=;pRE0fkb4ogzv?-)md*+NWhE8l-`EXgT}=HD7iN>cJ| zowOa!FER|zv}2eggO-Hkq~w^?+sH`y|}q%$;2@jeHC` zS`gl2#E`tH1+yj4f8(>>?GYm0`&=dK;1aWNW=t9EuKKGsr!&2gL-EU z9nxbkWQ8gXo;|aO?try+;RVTyXx4v$XmbLnY}Y%R$rgrZ zBq4){b?@J^?;SGMIxC6EX7jI$S!!ZR;%!rtXfly#w;c^yyiJJz-N9K*2{e+nTr?*o zCj6Ex=4K7)-m6FUpgTmdRjvq)iCG znve>le%j7}IU&KCEP1Im)1P2XOihK{{cgzu=$tZsT>gXMoym1W%t7_kaWK?Hh)~$z z?1cBT7si=PX3#mIxMcj4g2C`Xh-M_OKYjeneg(79UQzy_;*yDzrorivGo0zT$QhT< z;xtp71v-V7*_@hSN=-Sv0k1cF{xj3gc$OWsWbIO_vM8`{xeo*S&9eXY@QEcX00^WWGB? z42JX=k(HY}C>t$c>KCC4W*Z?;4z93i1{p<&Aj3Ob7mX&TYiE+ZcVf`*>K$s>+PfKg z+pc@e?r(JultX-&1|r+dmYonnma{1yo-%GW!yh9}G3L0W#1yO5nryYgRwNKQBZ!wj zsc6cCnFSQRCf#L73_-S5Np4%EZ_&g_v+Z)xzPH3UQ!>1p)nZCcO-Qi7i&^7RO_r1- zb82cTLgr+1YD}ydk6KYDH8C;4l$2meP60VHi)TAYjx#5vnvy|qazc{Tlxj)^#c?1% zCB+A+lev!Y2fV76G3Qp{#6>RFRfKiQg)oRVTqw6vo;3o1Et++7JzjIY)wrxr<$y($yN)a+LV$E4{0J_oQ#=CpwXq6@D!1bA`_Z!BV>%-cTDX623Fre z^#7G?ZZE>I32~_@rUVPbHzheK)rvs2AT|Y4#3V8$!HiOCf;A;Mm8sFJ{BcuY=UH+n z6#|Z%i3v$b7Hcvdf!zF75oCyr8d*5X|FqZLI5P~VIVB<0l0w>$n3zPO0>p&`6$A^O zDkUk26v|;&tyYK+LW_dfWHjuc%?!a!wm@PNk`iG7pc^m>zoW=Wo0)9RxM}ciWL55J zc)I5^gX46FQl+A4(@Owo;U|krX0ub+yrOAvH`9w9CMvvd@BTfIhNJP{Ss%Xbm+FJ2 z{7K`cO)IcFu2xsvio;;3nLP&u)5lS)*N(-Ds88o(Ak6{a?tIK?pWuhu=%(H7T7Gr- z05Fc>!>{}&h67UV!)Zqsn{(tx+s)A6e3U>F9X}Kn9{-E~ndiOo2PpR1iU+>^;DUh#`b@7Gt z659^(hg$J}PKf8_#)b>4UtnYlKlu#cX1_7-F?QAU?Sp-CZ(Mj#4A`LFkk&8hz~H=F zl{csEvF`e!e)^(IhMM_bUu?fez zH0&x}QMF;&rnl$ocb2#0FAT0*l`*RJRnh!5h*)2|_s19CIdgacu2%bfT)o$0$vYn& z{=RI+;Oj@1)r94Five?zzP|QA*B=glXQ2Zfn!T#LOGxR-pjT{*BVOGc7Aq3EV0?YN}XpYn;SYpoSHqQrB+-grXP8^ zp`uw^b-8rGwT6nl+*hMV$k{AsjGcw%&%P2QwO;QT&7Iv4ktSWO!&OqpFN{ovp3lpW znZ%Hv!`RhVqcUW#r*3g!a&xd5LezM!ms+|yi>o8Ot-VOR6leVhaG#|2oG)D^;c80% zt=(DQrivxI`@ZmMdw zT`WysAkN~S-qZ9$6k|wl^cFd{ND|;p5a7Xi(T}Gah+; zFL(2Sr+SYTBQCWxtXx}~8dWAB z+C|%@xlpn9dY;9$Q*M+5HQ|{9Rfn?P4Y8RoPkrmh19LxI{a*T^*h|;C1*;op-mH9Q z_mSsre9xqR_dkx^nlkE#A8X2H6t9b|{eV3*t!VR&uce0$OnrO5|A~DV_(-wO;j8zz zOgUZq<>k`F@)2pFK{U}ac^Sq&cYo@!d`Su(kmP&#jZH5vC8_rBnJ1oTbbNi$SW_~h zWw++aPZz}beP1pYFEm4KDjH9m#h`A8)y>(pP1VOvh#!kbj-8ipbkS&~PW~^FYaLf# z^^9JU@*~Q{rKP3fu6f&3s`dRhKe~B$>9zR>rd3bMdg4&AEx6n2BZdZWZe8gRmFlE8 z^G13yr!s8oDXH08ZqeNOf~Y4SW&)mwtDx7r-p7<~eUA)TU5uqDglvDt#_k!iPnY~S zkTJL}2Kq8_P460sF)CcS#FRLi=`LR zzkmjh+$WaDaf2G{UP!T5q2oqR851svL~PyX$683#m(|Y7UDdDl_z9bN%(UH3Bb`!u zBk}8N{TE%**1(J(A6E5V!>-|E#@9!!JF~@Z#t*)__j=it#=5T)pIg6-97RPe|E_rc zWOb8RTeaj%Q9VNZqH6uiH!9w#UfmLLXveM6;W9zpNS6Aomv+6Ym1KR_j`ini!pV{^ zIQihBj{Rq@X%5)^_80XFVe=~uHS4cGQ2m%>^G^<|s+Vj&t0|fH2S=t=RWkFQKCNY$ z_~A!iiQg}-cu2gV&8YJJDO8%0Z!rn38#2C;JaU>GzH)BpSI3s_s$KD}t^8J(rlyLc zhVqBcS3J2Kitt5#(4v;Sa|Y+vZYKH1Nd;DaXl|~4>qf<{dKmeDxH!pJ8()6_)_Uy# zUYf5f;B{H@#QA-htPF#ZAEuqm&fU7M$24C#+qX;-vB~falQUH`<=oOrEu`bj3qPBg zbc~4I$c^at8ap@E&BX{R*ICje`?^i3h5*<330xsb)Vvo=+-&d2V8mAAg+!o=uc zjr;x=`folyCa?U&@n%T8B|W3MqG3a0%h%%K>$Y<)##s#?^<$K+1kJtcK9}UQuS}V- z>D|+;L1XU>+0%;nSVqW)x&hpT^N0V8M_`s-4=IzZ{MEH<#S8Dea<*dO>B04e)jwRR zP9Ib$?uslGfoiUd6hEpKbMwTHYO2nNC)Sr@HjjVPQvy{P8~Z>PKRDY#n)t8e@oC?b zm$!`Hf7)*87rcLSy3KCsJ02?D{i?%~Z%9}7o7&KI6rr4?Z#ypSQv!@r|J_>?dr9iC zc%;ih@rzwz+WXL%VCa(P=XczB1(#SB$ z4?O#I{js0_w#IgMJP&oKzp#28>ow9nnp^qps~wn%twA?^7wwS{&`Ka6_c0H!PM$`W zA0Gcx9>ENKyLeu|^G$}Lil_QawjHiavx+l%>&2FclIFa_W~=DHLe)*VFN)V1HtZJ* zYQ?Y5Ri7mM*{h}LS1o_Up=I@RB+K8;ym()%Q}mnX{Z#bk&o4Hej6bwnmhL`ks}unA z%l~=aJMY5WpPG3=y3c&a;E`!+0#ak9@rUw8-x?{3ErE4Cx5EjPUbFe%xb^1Z@m=qZbY}9eEN_Oce1HZUYv%8^m44f#7N1%5hWJ5Cw|ue6W~-T>x6jt1 zFpaPLo8-CsMs4JtpOQIy8JpP^!idq_`xlXJSU0&KOk*In{z593%(dv zEmL+$u4I$%0IqKrYv&whvadrkE>7H5!1^}rfC`=5@|jtZ{tdWF9~mpDNs)y0&+Yu* zf%QAbF)#3A&HA?<+UEB@QW3JMvUFZQVEu|$nkLqkeqCRhYqJp| zZ7D91J>AsMr@!QBXM^8+zuKS8uJj|neRcD#hF!TUs;Bjl#D0vqTd98S$@7v}Pd%6` zUws~c=-hh~tDauVFi!K^r=9n|yLDO$X7g68KDFe^@DuB=1YLt83F=ez-i!mQZ{$yD ziBEf_U+w!XJNF0FXl~`bP`eT?;B5M068WABMO11w>)*5?uCU(9g90$i@A=^w@%^&> z3!BYny38qZ%D04adj3$x96{IcQV9z|;KSpeK(kjX`|X^v*bI%>Ip;$9I6Lln^Bj5$ ztS%i@d(VaR=AK7Swp8VE5fAlEl_Y5bB^ahv+tM$T8T-A1KKrh7C9r;H@h)Gb z`@Oe|N#ReOcq!uQ7pdEs`DZNO(9*IQ4rf+I)iRj>pL(B{a8`zKIIzNn_%2MUdjage z@_HW$FwX>H*<*WCl1Cljg~z^EBzW=XJ+ibKuhLg1*epN)Jp1Fm;;DxD{f-njpG~jl zuL1P0sou~MKZioy#)dxy(fkXz2_R^C<2xNoTFU-WS7LS8&Wd+W=OD`M*ed>| zS8cl=67xoLuUzi7(jJqI#)QWu#cL#R={NW1pBFE>bVu|$fB?_Jn^Q=VnSwkw(h(7K zTexAJq##dRnHg-doj++A^RWJCbz0Ws`J3Op{^XTYQ;R5OW-+f6m#Db)mi?kf48 z5k5H#{J&S6q-!msLW50LkGv!A$yk`b)@tD)j8s{K-$U4!S;qU?Xf( z?~amVc^j=vP1_|o4}IphKhEy_>O%;JH=RvhT)&}F)`W!IC$&|!8*fn@yk=YOe>ctZ z%gH2gwC2r7Qi|2LdQQI67ly*?d-t3f7AYuF{;Wx>4bPsJ*^f6e&f2caMZ6 zv~&D~-rUX5ZNa0*?XmBVUrMoW=?ZoAIeYAjSi)rm5;`kRYy^g)NML9AqWtEEM$TV5 zh13Eeas8+C81oBVC$tPdVSBmKaBkm<-%<;LO6NucDt%dBzKvV{#Qqptz-sSHEmFX6 z zmPuE$Bo8w6%;c<9_AFV2=@3{zB<+FyNSr1>YINegE z%6PBx{GXTx(s5M@s}6N%79t4tLN!7!MJQF_h<%=0lPJyKr^b!kq=Q$ZB?I9HPk-=7 z+cT4|>CLs7C6MQ=a~s5OanvztAM2&T-^oeKox z*#bdG@z1dBE7Fh7p}?WuP;rCM)Vtz4XZqLz)b~>y{!>f*r3V6jF$$?p`uVX#oH(bfGQK`-2p-rQHD7aDyrB{*}%Pv)}F9QvzK_^`+OFCzgso1ImS%zF)9y z)`ZsR?zN8DQb6GL2ZR}-{a;*$n49`y|8>bHKmWF2{?vI3>nA>0-CS%NXicQ`GnRMV zCAM6I#vDA`=f1lu-+!0hcFQIdd_S1F>8Y!2BaT)tlOC78pxF_40C}xGAm&{*d?|X6 zragXojHHu__9->2IsEW1;wwtjo~z1oz_NE$H+vhO+C97Lp$J&@eI)vg_7I!EKjMvL ztr2(vBI>V1`!e3EefHV3Cl>V9)$|-Gj#g&8@IykQ`OHvT)uI2mZm=bdh%fU-)dElf zetrMMGrtmzZ*;=1$_od{gUk)>9G>@L#2D8TrNNrY3w4^t^C5PfA=Wzrkb}!>cN~4@ zfJmUN5+O`!iEO~b%Ng9EC9eO35BL+rumUgfxz3U-JPN#ao}eGDAuvP#QXHV$}@iF;>Y{_}X#XgmV#eUC7u85-`IW9Q_;7!&-`8F3{-kdhz|r`i*?(c(u0RyBqTx9yn)p9?i@Zs`W!TrqU^R$= z!9f*`hFc%B6enRZL#)Vq|Emw}K}-ILbyFnl<^hS<9owjtuC@cvZr}ZX;ug6CtlD}1 zfMo6wv1HVs`n+zJw=|X=u5vnfv0-IXOKI$$Th%~mov}S*4p#4Mn3gSh8-8H)FUeSq z^}r{N^kG-uzM&j5e{Wvitq7#=Y`&k zAf3`upOiKepemz)IleJpidAk1hIgyTQ%D;=TGirm=j)%(9aUR`RM%f8oGAT%e*9zO zPw%zeaxQN4{4C|egqs1C=dL&1c;})KClMIbxIwuM`rx zJJeiRn^tqH>_S>W%QDLHMtHZ}Y>`&E*%MMG?8e7|r=G+*hoM3&-UDwUR7?@2G~eRo zP>f}L|1nMqf1|NiVEKuqZ+tgU)R$-F{J3J?_a)Pt#j>UwP>N0yKhc)+HDc-UiuPL3 zaO8uoSnAHde6`lT;yq}U*OO9m_tVerlLBV9DZtoYJu)!DJ_mr`W!Aj;eHhG3LauAd zKVQ>G;ra*|waT^e2P7P@4ncp?xZ`dT8jpco<{w#oMxwVLT3KhB{D`FF&tWWYob6LA zh3iphrC)$GlXalSvR2)Q`d4>L@~lNGQ7eMNSZ2AbL)|UB%0G$Q(0}tM*D6Nex!A{6 zw%6oU{CAi1emMa~>)D4N9CSkLb?YuFarnyfO)cM0pi=SN=_`O%TWWUOX5Fa3+AOX0 zmD@K^-F>YuZTv##>NSfo91(o$>a{b*n0vPvgY5vhG;HrbE~L}mB?SJXXRSUYt#98d zt6%|s-JV-Fp)wzhhc4{9RoO6RZ^OdLXU6x0QDi+UU zd82Y2>0cm4H7jZzIp4;%xnAsbLMYERfS^9Mccd>COE)#tc3h&lci6AQ=vg@|MrSFi znv$Coqe~9rg*4m6Ml4v+HjnD#?NhzTq#uvWs*#xc(#e&~7t7@T1+hiUy1nu9kv>XIzmss$ ztsku=w$(NEH9Yabk3vPs@|X5**ljzp1B*%-=P&;1LdG95@|5*`uW{e(q7$X(v78sb z`_#@oeQNB&{Z0J|Z2nk0^17jvkXQY8vu<2r0{vA}-a|LaE*yw_;*Eytw{82dHILc! zeO0znpZA$jZ*08YLV9PPV6X6m#z59{DZV- zW)+rhqSguDNaihqcE4QdJU4sU1%v?mS=j7KVe^ONK@l&UEX?kT9US^&ca!V<3l*1= z#rZSQM#;nDnW$ylp5}ou{Vztw?IKMGeVYw>2id;Uq~cvK{m-$VkJnwhUN)xu#HHY= zlRm@V)A!E3+4%Cir58%;%eKDMoi?DJbJ_S^euE>;gHimaA^yL%k%^*bY|Gn~5{Gr` zi`aZRtFp9W|E8w+%KXVTgbY^k#L6xuWSM*xy0v*R?SVXoqPV^KYp6@u3GDoBG=9bZR5)ZxzL)sUhxX%CO3N?dhZg z2#@EP4}B@-?+~$FPeSv@Rv&Mi&T>NUy!Fwh6W`CZ9Fo?k-zhS-!}$`1XW(Dj-8bdU zpLaW}Ji^Lg4eyJ>T6c_(l}1}ptw-&mUUB=non*|hD+OEi#G*_I!=#rSt!B29TbhcPc-GM;#z+Cz zXk6{PI$7HO`w2EPJU?s9wZ|D4x!4J4=h@*k65i+!;d-I|@5805;keqlKS;`_&%$cN z*%$l|NIT2+;p(9uzer(V=lQ`{_==;j5CH1%AMa>=Iel*eyA zR&+uketENDe#`Ej54G$=hIMrM>!n4fZxpwPhdaJ`VZ$Y9XX@)0u73W<+xJ*9dY)Cn zjAyZl>66s>`8^o2dI&Mj#gDhmU=v{4423*8@3VU)pR^5E!^b=+ty=yn>wr5U?&m*@ zqhxAc<>@OF5!d8BRe8J_8~3ng(P2(A7B9}<+#z=OqPhttZ{ZSP8S6&}Bmn9515o13 z(*+V_+=6XOUthq+YT91P7U-}oBXE$knHnqDj%^pM0dd)tnfvbCnN3^roREcW(*tO! zt%PNt-)r5O$FPAZApb&jQ*q<-&W+c7uAZ4FA&(nxe|>d~WW}F*X#R#L8GO0DP}-Sy zs_^gv38{}f`SYSANe$S3)ixWiH~hypUdKl~FD5O+?u=qv=@a6Hba(;etDCh?RoF4i ze@Hg}^RiYm+Wi!l5Pq!>i=^{`&tY({ED!cu4{dpJeIts6I z8&&BC)nb|Jz3(^r%zCEl1G8tJdW$>o_L|%Qh1Sw#``=Zi*QrI{{HcR+kq$C5us(jA5Ugf)3pOd# ze%Hvk3Z5aP^(s0-1x`iUN%hGThQNBMxKMTop*S(jRzIrO6!e2upk)Ag_4`h)#2Cw5n^k^a0=YLxJ_Wu9;_F)zRVSXG2L^31)v2S%9SLd`4WGhU9ztI@XtRr>V z&Vn#o(%$%}Lj@HxkgfCy23QXxFgHw;$>g+WWD#$#Z19GyF|tmK?jZ(27i+{nuZf3! zLx1@^K2-2`QK@*zD%y-j{r~zw&Mk67aAUlTP4DARAGz!5+L}(78ZleXh-j0Ky z=tjSgn;lV%qIA1CHHMs2TchwZAyylmapVCN<(_W?@}G6LFM2EL;V7LM!WO0*E{` zMFg5itQ_+s{Q~2%G2QkMJ*Y_c=3E1%VLPAtrTXe6WbE5igWYc>QX)_y)J`fD(ZS$0 zzJN&#`Z^&)pcjBD#oQP?J_A6$NeEGsbTh;aJgy>6DCsT}wm>cz(+C_6_K;4HaQPaI zG*Osc$~yq+S zG|DW15v2o2cl3GhoakYVtLWJ2fRk4+|b(CDiG zAqpZ47KIUobNtftt^tl90dTHUq>~U(4-U7s!;xHU`M=rDh1;Lc#VV(q91%p7y0P3vm5C*U+2*!HqnFvd)M>;zhG4urroRGf7iM8fIYeq`wi8iwP&_1}iK%FFex* zvb7F{wZ$aDNa;Paj*1vHbs}{`2#luP1f}&SuoYq?``re1vrd=*z#&3sfcH#<*aU6~ z0Rxi+j2M}qo8HuT2K0e?q-*M%b?LZ}is=!F5<0u%;Krf|FSQ}w?nI4xqk_!?ha{!L zk~l<{*;>+6lOKeHcomOPN#NEl1Z}HIqC(D@W|R6+VpzDO76Lrnsf6qUkwCObXOtL3 zhD2T4Mw%oHEXklj@IIi&x<>e=<_UF zfM7Z>MJ(<^DCUKsP`SUG2gi4YXz0Len=zi?S9*N91_>Y2SaOwZDFQDgd_f(7HbKhV zw}L;)OEDEPIyv2*4Sq$6`f{~+#>ma?n0r*@faWh4uhbLXEQ1R4!{lcPqR(dMmkuW=(u2}vW;lB zk^zdyc(lrS`{(Q&AhHC1cZML^3s0*oQ49ns7^*=1+b|CUX!sW=0f!h-tw5e^F_})n z&IGSac1LFq5ZSbo!wSHT4s0nP;ly?V6ZL~hCudCM%(&s&UXQJL13MtQgdl}6(kKWS zSR;dHXSjl6b7zDQ09`rejDFc$QtH-8L$}VcsSJ)aE&H57BFH(C14JiyZ}c$fNOeFh z5Q2ncFgYedG;ETb!XKFqac@y2$R z5I&UU?c$A&-tK6{Uci0~Du#uS?`eheEE9oo;+Ia5 zU@(0XXUI~!lhpvC4q#M3HY5SYRAcr8r13Zt^9HWS0AbdJ&;TRm68$p)!GwDS3xc+e zHD?rzJX%IqFt^k`E;6Vm$Q%=MLJd%SH`W&fEigc@mwOP z_w3y;qNSOG8*tonXYrBao(TeJWTHrlMSbH#@&-XS%1oxKMg*Ef6j>$2eXiX)8wozc z(fYaGpT)f3&&m1Ydg?j7cVDNaiSE*OmgHvpkdNLK(>I5 zY(Ylk;%KsHQ3C`(Wp#8)%1PLPjI{$gP&tcgK_LS)1chnhF(79?9$PTU@TLXAHaia1 zxP-)sDko}m;BefBnGy&>Qf2`%5GZ(>B(*a!bm|X%q}lW%T;ajS=)}l(wAc=d)6N_q zLOdsB`X#nF9&RP2-9r*eV#XH_yOXboHA1SG;+kakAcgox=o?fSw8lCZZxfqZB9+7f zoRfA55l+Qg1VhF&#Y6+AB6{qT8jN6B+EGXo00kEUJ;;ce;N55%47u)l4y%JkFn&73 zU&?$LUF<%JIwKU+<`<{zfU>}#8`P?+MqE2=7z(knC)FT1fcYe!hOxqcN-KyiW0_d8 zN_No#4uQ)IFv=0XlNP#?)L_z(3lfZ|4kd%GQ)tXWUQ%w11k%B#&P3he8kg}{9~?{R zIjWxSi_CsE6QLYRnDa2)>$USt z;;=mnNHa~WBt>bBahc57+3ZvbGRAEVVm}jn3F=E?di%Fv1Qr4h+rDY|5O08YmQn)O zO)iA^n4L>U3e=C162y9P9vBg^E0DY~gywg7cbI2}1_Yf8LsNj2HkB$5!rT4;2gfpw zu?bPbTQSZfYafCGg)l0tW9KZfBH&$<&S=Ll#AJGdy#w$?=lBkiazQRcGfT8!g@u$- z!Q{m`Z(tw{KAvG{h8~CTlOnNbTcE)^4=|CD$*{KF<|6u(biqDJFk_e%Cf-yX&9Y^9 zX+2wlw`hZKs%Ow$a18THMiMrl6E@WLcn7o)P0$78`A`7{f(Vf&z11=~C58pJR8Mjg zt-`_TLd_XiVAT=cVD+4~V57b(g*dcoj&tO}9Ot+~-*nIy-jpUJ`%V%l5ds5`fLwx7 zt*k?3VqWXPPVEu4(*yv;vknCe&XnB6?qDzNdY-9N{ADibJU`lY5TDoa+VP?7<|>p zlZCQX>3CW;VZIi6$U3mg7vn0NDznM0lch*ANs`q{i!_xi+oGbxh*!EcmQ$yvQ?iTJ z8=>k_l;Ci&Y?Ya#OpCos))obi>}?7lyqHFbcd`t3IfrdmsJ(d`=0ymFGeJg>%@DCB z*vKJaCBYvfCfhlAl1-!J4=HFGM>{&~u`At@R7Ap^Shu zgT6St8y<7;osEj-;r2Zx&L-%D^(1tnzbugW?|6)UrAL2ppNXR(5>&z=%pB8{u{4+$ z^V85F3R;YEZ~~4OBOpg;#OS1Oh87qk^^jP|aRUO4#t$#lgnu8Hik2984S5JFr88WCkCaIU}yUOmKl%mto8G zMs}C+uN?zeF(kr-=NM{Wae4->Igdbto;((#h#UZeFql`?wpl;u zyd4YCCv2?K&X76+(Sc9xHpsyH(=v?--2;NSt2hcK!A>e-$qQ^%Z$t28cf1is0p&anGCfg19{h5@~co^$VJLMGc7XVS1V z5h}R_)qx0bmv}Ax7|H8n1g|4CVx;(2sUx{0lGvW3$hE=B0FXL z^&;T{%#%=*z~F-#)-533fTcU3 zu1ba(wH;cC-?5w{9V*piK$?OD^29(s-X>%#>5AYdN3al_gsn#A&eYBhQ(WXN3}~|+ zf^Hb{w~Gkc4|V#!JBGqU9V_RQnW3CpMwgT3k;94BKT`9-2s zK%SF)q@xo|uo30-X8>rKe5WK>%oR|R(z*cbtHU7C3OwQsW``gb*b>IPIKgqU!y%AC zgcBtEEFCBlh9N*srFhv8$`*gn0~ib86eveSri$WPnkT|P5I}p2U>WYvEW&|d4+Ena zkR&(+t6=Djbd?PWP~9GRQkcowK{<2UZOSoHOh0q-_UIEO{zi_gb27uh6zSJ7{cWd` z<*_J}1!_#-6$lK3@02!#VHD6TQV-l>dH~@kvLP-Ar|i!{yE58$WWQpqA>ly56*WwM z{6&ix9A|?8|HZ@67Br1~9`-Fr6T$YD(M=2!Uz~lZ0_W`y>85>;f+R$(-{jLNB)~QaMw*}jWC#(iVnpP1jcf`Tn~6}|4={qw?2Q~4FcIzB!W~D1fpZE< z-CVupb*P1dxeq)^y2U&{T(q$bz(x%cE9{72f=I#=jKf}x3|@!;7>jGzK{t0!-UTCv zdO;S9ta;p{1^|4Z0dOg;+d7>_89+Z)YTy?XQCpCq_1KbM*h6s`!p&dB{;HXKOdDW`8aGux#9>Pkp#10aVCLiJxmOmtX;*4S142{eRMQSV^PL_eGnA88D#oG=hjF6|c zmXKTn^tbJ9>sDo>9NSos0mlP}NEV6g8vQ~_?()mH>q>?Zb7;4e#QfV2yNzo@2=?wW zfk09$0|UR?apyyzU4OUwx7Ea+;C2nF4ZxXWy@x-`fA zO~(I@ms0VHtk6Y4a0~M9qWFC;7Z>koJRify@dxza$9A* zlIJ^lS#UWyk`IdY-6P|;M{s3)mhV<457BvrHp+c`e9q$^mS7ZmyNu$MN?zHQk51&H zqb$Q>y9}lRo_8JcoSakUcn#Dj=JFxDE`-Bs+F#_P7oTop&`(zqsw?b&-;QS-HCEn{%M)VYT0YD0NzJO zZ(V3;nD%oS|AkC^TE!>%c!lzh$ao{Dpu;cf z9z{?mK3Msvj2D-N%K1kO3O_Bxuk|%5iBIs;CMg4Yj0od1$4LT|#y=ngm6*_c-m#Z>U-&^WIrFdR8 zN~xSPy|=oP%Nf~pC0=ST*Zova$*VHJc^zaw4!=?S4!J`4JU!3ze}OnEqxgq{{M@CoxBG*~+L$7O-Rt8nK!e}m_n_**=0gfh0d{FNmV96UT$!;s%*a(JX6}byb{~EvFG_iT3X$D++phMeE~vP8`h) z2u^ z@K{(#|M3a12pWZAh0I;4bf2#X7r&FqUXyQ@E4TB!&dV#C*Xz2@m%4R#%Shl~VB>OE zOtF`d4R>Fil_ajsQg9C~8#Zz7mPZR7oAkV_2n%51R&g6A_fO=o2o@d|85Y$cDk3r> zvO`pd$PVEV#xQ-ZK_3<#5l#iTLARsAA`Ri8#!!8*J}}5Pz&Ah?tJlv@k!NUhY9Aqg z^L>Z+om#W{S+Qj3V3SXLTGF^{#f8~&jcA>4W_-R<=^7i|Js`Y;-Xha!W!{2EkPv3_ z9ywImd7v^VI4CqEI3zSAFeEU@Kg2)CH|P<6y}urp`rwdYeK0Bn1_b)y(E00peSCei zwY61GJlIpI)o9cMXa4Q&ubR{H)Ixf?=31`CV%GQs3b(eES8(!xRK*y%wCh%ZYmPwk-6EKM%ipFZdwKpEx}Y+QZyD#W_i<6 z#VCaWr?_FY4z&!{>NI9e=C<=o2Pkl&(P}M&@n3YC1wkt9rO;?)REn3`tdW^D+_v+a zqL)T1I7_vHMI-NWafza@Lb0{z{^zL2M++xpXNMCJS_^#}(4(5VEX|zqcX0iFUrjH zaAc-~0c$sHZPF6WD7RX)W`9t}#yx_@Ekm)^o_a~F+OlUV_ND5fi1z$mlTYAqQ) z)M_!9hiWx4V`ky?NV%{Q7nY$zhtHcgeCW{hIYZ6yU~X|CMr6|tT|H;sntk82>{+vV zLmtH9(+B+U|7TCg8=p5mOl}M{zzOSlrAif)+bt_8EzTO{<)!uTz&|h4^xDlOa~K1# z6G368X~F~y)<`VTSm-;*PST`8j4nI}C0Yx88$ggi6g@tvurNEb5JZ}X(sw%ONhE@7 zA8&n4cx^cmsU1q+hP+(y*2L)}$M+l7qkq~t&s%k_?mCYkZ*Lbx;e>+m`S6^3WU^2m zagy}fY7@6^nw>4@Ei+cHelWFPu2{P@KnT`ued^EzW8I*nC~dm9XU{I1O`{3*Q`>4z ziPw9Jr9!kIsI$6uPc#haGoX)773-$%WR8qAb_|NZhQ(kvPd7I=7rYeV!I6fjh?owY zB0BYwDSF=5vlosW91*hc4#p1ph>ECtBJb%b=X>?0JH2|~h?Iv#8KOcXLyduYUyCXv zF04~AS*jm)0q?-%)HVd0~NQ4=txZTGKU zGk?S5FBR%C|Gx6Ea#0lY58VI2B1^YE}bIDk_RY=2a{X|;kO zsG~79(%i`!*Co*iBOhVx5P9#NWA1TZxPr#)0}C#baq`G)37sgXjD3+4An;S=l)`4?OqWCXFCxy-t{rQ;<4%gk@+;);6A3d%Nu( z(rK@PS0rR-X6IxLwY)g=8-Or(x19%ubb>}i%H`nz$ywF}7#qPnaQfrB2cX#FoyTj* zFK1_2c)Jzi66D$D_rtGl19nwpW=|kh#}mBF6&>5z!EM^Qb+1FOmgY5N^89cw zF$Y%rQQQfS%FN(+ZJyc0%}_*+56>-}G+F)lYs()k%*lL&^v7Cn7G9D^=49v2DH$AQ z@=;`8pO6tI@|HPsR?E~=+%GL*2ep*)uEb)eO_xU_k6us z;~Qwub=1d3S~>-LdkZ?e^2BexK5$jMPYsKxiQKw%>j4YnmOL^N%p=oehRMR;utpYu zETRg(dESI^lkz7|omA9n1_0k&6b-{BgsVky zWhg1g90xnMzI%Sp;pWR?o!D{;;lH3(t1F(jz5CCLpMR?fJvzNEm%EZ)uJZ{HeCujw ziM5R$Exy6(9-#^5=EqakHrrmP*J&dH-J?9@N?-r}u0#8e8&%w<@9qTlUA$}e3^fa) z*c22BKit(PDYEc2|J4@L)mlG3SDml6gm);8A6Hy9CATWPW8}R?gF>C{;@K~!PY(1G zS(xnA$ulh7jYbbu2N&H4@gDK2s24&FV`-$ji>6JSoL>kNJvJ|s=Jt9qhPhV%wf><4 zHk)psbwp-zVRrJxW>HWjj>#MQ*QoLNg-^<&Cr_QZP8qXs&dhZ#(bJkm)pnQuuIMkY zbn)d6KdByhccu=0pyz#(C(>J{I2Y0N0&&%H5WJO=ztKX2UzFjgcdR53jo-W6K0SrNV zOL)Ow?Y&v0pIr;UTCGl}RBL=dfQJ{>@T6DMB@wTc z81W6WK2+c3ad>nLvVIm4!?-A=LK!BDR?3wTAKzQL&?QpY(FIie)W1jH9{2UA zJ1L620Yh#= zqwje;#ie^vYEm)|D~>R5v!f{@&JZ0uG)ZPvBw11{oh?VYC3m%Uwx-~qT8UP95^qgR zPE1Wqjj=|@hu;$t5n#aI2?l!kisCKt*!Q((cy~`PUr+D{2^uxB$=+UGJ|3=mRkio~ zyEb`xuX%jIbd}ri?D!ZB$A_3>1dsc5Zk|D$%9U3ud@bI=pL%faN0j&SZ3f|+5D^m^ z6&UGn)ZWuNo|s=8X^bH{Fx*$K2@xwcc-*qZ+@}uk(`kY{6dHM3uGy>}`ZhzDi}9vd zlIU3A>k$;JRj6g2?!1r8&o#g^L}T;~_m2vWHn@faMVLA!#dfpa&KrjajM=hvZMm2m zyz1)BW;CPL`1%L?82rKlI)rRe$Xw(KxqSJO?BRTANp=Qbj`(NW-o3E2k&#i6QBgV3 zIlZEza&Q_M86H{Z=cCI_OiVOoj0Gsn%E}6hlq1CzAlF8X_4Y4u+y0^0_^jG5f*Xum z&ku}1jSSNOV|YXV3X_k&t0Izji#{rf&&d3X{j`1^tmV&beYSN9+Yi8^l=E=5fN{QB zUtg=g)vc9|a1g_2p0#)R61W8c)LczUOdOe*n1Fx$S}xO|=d2u=EHm6qV|9C^?}BFo zcGiiFgMG9U??K!#X$KQ!DsJdU&{S6A}}VtU({$!{mSiFh&o&CeIknD>998nYGX;i=13*5XB=p zEgIHT{%!gIOZLDh51FeALgt=hTz!#Z_6ZgnKl`6EKV`gHI(JV5hyzU;V`lcG?OT7|wkrkp@rUa$Rf$C9y2t-+=3O*qlY#?gc-cBMYSQwggy5+LU%$~^ zpLU+qY!h zvqP~4&CxKvF0p9W^jzw%P=Ax%>rrw4c2TYA*N_^`d1se;?$L3>##C&0VQVQgr?l?q zl`CDU_5o=Mf!|!bc=1fjHx+^ET&-xkeEG;XYD|$D`tBMxE2;JlRcZWTH+@xl;NeJLFGYx}UxJ4+{k|JF8a~*(?4Am%PSE%V>RnT0 zQF29CbS@vMQ^mnIK% zLAE9#(pAu88zW~VCp#2f*il|Jry}RNPOS}DWa2F0x(r34^-viv0|N{LXT1LURq^s`K>?OW z#*ee!zv1(ZYPCLQg;*=T;-mBFcwf&YFFjl^wOE}J8tWY;gm`K0v6w8TcrMP*4LO{_ z6Q3zIt8dlr-KXR^H@_H}rDM!J4=C>SRmzuFiQ?a}dh8PrtI znB4ExDCH720CU^-u)K%ZdUGg`F}cHb~BH@CvbD;QdAomzb_7vbm0 z2fO*2-CZ(zC%I|M9xPh6bUPwP&>8BMEbk!W!=ou;GDc{5`O0&R*Bfp71ho$~&`7Vz zYlp76Rv!V~?H;Ci-zb=XvY9q3<=Eo?)aab!3MKyWR}3oe&+YkQRj>l-IgJpg z%3Qwm4aeBGp0C}feZ%bYu*So|Iy-L>NSbc2s3@E@llJ8;H(Jfb>ua|w=!3R1{}JM@+*`bGyP zsg(8%C$C`Q$&{GiRE-Zx0=^KwqQHcF{M&L)ual6%PA>(+9&>|R#C!GF7{r39(c zwQs6tUyGNgpSx~D>Eg1V#IkxF{`nwGf0t9@<=Ber%i+8<+5M`g7uNpsfLdsFT8wEePGwXzf?}z{_&c5mhW?ZC{^p!We+~}ptzco2|f`TxRgg=X0h;D zwlykeeNP`1>^BzY=)=Czyh?^LjkgAWKi9KyTIkA23M{Gk)$%Qbv&plj_g$rn^JeaOi1R@9J=|0??Tx}~1 zvuK69Q=f-aUw*`>4zlDO*k5tEFiaibJMvVM`0=B;TCGuSS-s}wYd6Izt-ns|Q=A(g zFP;xqXmwhh+6edk_dP=gnq{2KQ{~BPfkL#AyxMs4#T8pA6CK&3FuIfR-oTDZUPzD$ z35v0!n%xyD{K=5?dSFY%gQ8e34DjtS;HKWt$uO+&ADcICIjDK%*82(saW2#U{_;LR zHW$lVb8dKec=-DU1gaHR0)3NaWCI8qiOC2?#M7C8xtOj@u5Ar??-0Zr{t zzy?Yz(o!2J$bnKDO0h6Ba@q$;N{QU^9yY;5rkmYDkm+W#S;~f;#P?*c5?X?N+Jh{I*R8eg~cGB2Sr=gAamS|o#B z9^baRwDqCZCQwx}O2#Pk6zU*FlmRp$sKwd$-aq#P4LG}KWwx*5G~49B^)3R%Dhq#> z5=eDod0$`Z!&!EE-@2_U))@1G6_(eYN_KQyJ&chgu9(V}%pHGChoMW~`t)ONIe(s= zKQq~$1^@F+ur~HiyqVr`vi&<6@+C_&NLwoc8ua-F+8X|Snq~BqNQdP?hOklsVmPu{Lo2e{hT&WgQ6-00{%P!6;$;+$n(Ci9J%G^$y1wg!cleq3`JT94XRcNbqb~2 zBa^OJ@9;VaGq_h`8Bp1^W$RzAEDNuFX)X3WKKHC^nq zFn05XP15`#UHrxC=j0e6WCS^h?)-{#$Y%XYvTBXx>}l4;HknBWW&%tc<7`%3t`f_H zGQNoPe*cv}&d#%d>tX{=u2&O<-pn z?C$G*>d0Hi@eEAgJyin^;vA;&>jXRzBKln(MIk1gif1?5tOmW!n&+@sAqZhLn7KfK z#b`6bYPA7(hI^Mo33XBsMy{p0)ZWkBz!)G7kC3IS-><8FzDpt&#t7f66$ zn_VOTI@sc}|H-MB_r;rMB74~;?W34Utfp`nCtwW&KrhhbTe7ExzroAnV6M#1o;!Q~ zt+zg=HQ;H#HRm{iVLuqaD8AzZFFy8Pj?0tk7%*g^0}EK_yOJvzBeD7fzUa0a5Z3Q}j(eiW-Btd%fT zZ`sGPfpce3gMmivhNpbjoSTb2xdB7Bb%5<0tIFqW)4zk6$-X{t&e>zf-k+lgu~T0y3NvKTsj0d1nHScB0+yG9i=(XN=UwyX-yRhSCF0xY`=a&{>{;J?%9mw0 zf$G2@t1jyZ>>-WLdp{EoE)6zOq?XOC#mSA~-5QME{RCj0Rh8hX zE-oMK-_Ocp0F4IiJHPYr(lSI)v9YKaE;`L42WR3TI^KQ}h3Td#V-a}hkg10+;-~_*k7ordr zFsm`Mpkzsjx2P0Wwu!}ivGPoQQE^GJ&r=zE;p{xyffX1Hv_?XhFoRMjQtE_wA*sQI z5>hI`kyRQlJqQz1aIG*>sU@Upb%~r5mGbg`8{6}}9d-*|4u%^XF0LUvZo7g${M1WF(V6o<&E-s=Op?GQ{%mgi{Sdm^4093j z>%u&>vA=4a((CvUR$2MNy0(*hp6EtH=-N^^fkFYyW(8cVgD|M+j#l>5nW=1t1`gYb zIZy1~ciqVoAr`&9xG29!45wF8?9m8ijHy;og3wjMl0qS$C-DgKE3l=))4ANt?dZ~E z`BKjkj}t3W@ErwqmpM;wV+>m0G%hZm9{K3}T)l-Y&_Ps{F|78ZWsWsEYOxV6R`C_c z<0};vovCl#rWKEB}lE4;Cq8*j=yZ~LqUtl?i7EwXqWgU z#pAa;`-`IP^XIQHpxk*8#gtsC;?9+)Nj@^eo;m-t8^C=C81scPomH4uggPY_u|{R5 zjKyAt#(Yj4uF|-HcPx-7akJd2K}j9v#Pct9-+cD`$Jcp^USz*}CV*lVmz5%<5+fiB zIs=cV6AKxXQdzYc)Qq|nMz4{UOU()=vs`3!Nv#sOo zS@w!^qf5GTKZU^tmq#I%C?y0Xvlf|Um9{}jNHeH`lHwMnU9Fa=RVs}I)iERqsEQS) zL3M~7{^gZ)JQ?`&stg69;u)zf(gNiVUS9a>^|DP$Csek@M${yjY_3YMc=C zQY{lx4jHfHEX)3aUGm&^ynqZ=K_Hy3&J#J*0%oP#=3DPUNi!zXmgy-QiKtM?c}3T+ zTTi9g6GA<$1yoUtKbQP7bT>&cc%I%O5Q-VITtVtR?w1u9jT#A|K&;XVaaw9rY|~3$ zxr3cPed2F8Nz+FCw!;>h?~2~{WUWSLlk0>eA3$5dW-{-Y2Y$)2VYo&tJAfDH{iLE273+ftpoN!11h4w( zj+wPhaB2=xV<2ont(>PWauryYxG9mKY~`vzaAgZ2V0flA%UAinS*(`XdDiNJl@-g% zzvj+2JM}A_6&`QN4GuG7(w5oEii%xzRz0PoicBTWe9M)3Jw{_1ROixJA^S$HVfnZ6 z1(;~LfKLS1^Vcq`yw$O@<(5~zo5a;BMh!j`L21dQgD_b%;EGlqEz*6=&bRVaYNvrS zr8Eo#V;R{gxVhTE_W92UEu;Mr8NR#wGq$yn)iaGXr6r}iFP9XENjwjgEPPAYFF73-%d3|S>-5NzH-%N-=xG!o|1=aRxYnuBE=|$SS>x9 zC(M_u)PtTBSomhc3M)`v)|+%z-7;&wDgSbv#!8kt+_Z~E9V&aVw!n*)$x4^W%9j3> zXRct`nLQ$%z$CKgtu+F#c5(U1`EzV;|8Ll(dWir)bnesD1~5kTCapoOE5($_T;)}_ zZtT7R2)MCimOa1ZY=^-{*z~l8un;!Nv;wCYl?#A(Tdsqh6HNDIEvuIqmg|>lwWu9e zmC8KDAzTLJJiUSv!(F}HHb+1q0o!Ws^w(JSZ27AU!`Kbx^0{+II%@Xl^R-a=L(Uga zd|)0|)Zhzdzt(_5m}^-coU1)xHrgx(n@LVD zV~l#UUTxAq5idG`UJK(-4k}jzj)lss)oYbXz@*_*mEwxC-QDM#*=fc~ zfsSKnN-s7FfWjjx6&vh2D{UlIGMQu>-x!ih{Cee1cE|mz_&rK69epSb+&g@sK%`M4C*f?l*jn17w!V$`j*IMxA(l?NPa zOQn`Cps5O&go1?T!#50f>X*amuLidy0CU7Da81@|C_8v3Ycwby!6*kzT?n%nj>qrR(Mx_;&rXUI zDtwBV0M&agISc0VlaKAa2ebC2*fjWw8q6XS$T9ZRhOIE$p_oawL{702q96EQZwx(wJ1@bC*3FxRSSRxNdrYhy5pJKo*k@2PNFC!REQP07&pps zC5=ll@L_E-iJX)wDex-`{_uly5W!*J)#zbDEJuGZ|JK`CwjL~5jA6`8$m;6%=h=A* z{1fhR5t&)=Y*=;1EL;zE&19BO*_gFmt+Ae(?5 z&dv27P)?1{!Jc02M?RaGIrjq!H*f_Q7M8Og&9OJUEUE{R>yEX2oFFUiQJCc$`83MI z1gJuwME3*r1@p#MlL7s+R!r$NC2hv_0GNENFoovX;bSKl6g6V!-+gtS{pi>U@QlnD#9W$;|4W`e+9_|3Xv`&=_3{@etZb zAQBR1inK0Muu;@=JsaQnq)?fY9 zbNOOPN$EoP8zw)P0F)S0CN~E}I)oBvMOukgcAiaT*C8r?DPPIMDAkSMyl3x2Fuo$8 zC+>OIv!q~&vlJ!r1f){O0M_9)tq}2qZk{~v37o!+gy0TcBr~oijksN)bCfo6;=NkO*Y?AbZ?)Yo;8*nN3gJ`u#j#*-~%(Z%nTFk?v_i!)6BmBl(+1&aepRIJ`IBos z_vL-H!d6#qxu(oi>(Ol}(p5QiQbJYe5GzG;xw}AVU~|%bA*Pm)B?A6G7;jAx{OEk> zk1$3maf;C9-719uAYe{1Mu(e)cCwIMDlZLLrrr0q=DDvjnRJEp61_d&2Gc5OCEZ%1 zwL)($V~id>Q=rv4wUkY(fchm$janyBLU6ZGq5*ox5{Zt|5<-<$^f|+Xo$uQwl}rCZ z=%f-=lm_S4X+ac%LWk2~F+1}^d{n^W{4gPf!wQ}bHAU)SF2c1Xo_3DyCMpKL8;JY| z8>p&$+a`v9$xh?3xt-Tgm|CSY;3ZDC>l#9>M3ppJP{7Z3)nZD(J#m!_;N2_L(tBs; z&b@Uu*+DZJl0^5$E@#;jTf%vHX4~49j*eS9c2+t7a%<&bWo6l)oYvsR)9+UQDJSrj zq3C0~rq7)_!%kDciU4SU-Jd;XG3TuYEM4#j##V|n91fP8-_|X`LW^v7AuU?MBJfho1q zp>Y|krb^&swgUbI)2giqSX}4(EUZc+%y;FxHc>Qj8DRLe;2*Cv*-bTYy-_N_FD?Lt zL{L;%CS-dgY;j59RaT=+0+|oL_{EX;j~wBk%8L{0{#!q~#c_M>#@jplb9#o4@l}L` z3!$SlB_z%jq))PMtp5=Nwh62)`9Pw2IOLhb7``6SLFF46PXbvw2J)wXfNosTvAT&g zI1MW{Z^o%mT?dc1v-w8$A^LuiQ2OxqH#OOLk6w2<8^`FAfjgQko4$6v`-k1n0Y-BG z_^)ANZU!~TR0^|^!sNhi(i!v!FYwayJM$`bA0*{RcitF&s&hDTgTuiuoM+)moT+%l zYLW?rNjBc6=807VtXj|`uMiNs0&m5D*`6pdmV8J7_7VvGA+JtMgh(=358*K48AdpHG zRQ%(qO$eS%-}9*h=OREL_*m;n>t2;nC>Pmh)_P8r?_~Phqc~oqg-fnG+T* zfE5&huxguHJ^KM%w_m>gTxC@qnndyaPla|LF6?^_mB|#Jx{)G~M~Lky^}u}m=%d#k zyOkXqXnOTZ<`?JBo;`bS!gt02CI$9JjWbbLTzX!2`1W|;-naLKpJtz)nNI}23q%6! zv)SE;mmf;`Y^e6{^5`pfqG=QWGK~q+1`G@u zYT)_h8Ud}9&N~F=GPzr}O4AF={RbH_{way`Q@*Ea$ z5**9n*07aAfox@!PgS+9X8nfhD_3rqyRv%2`WisiRRw&BPJGUU=q-8!BUsKC3htb} z_ulS%&xF3#&z{enn7uQ|&)abqV+y%EnfWuHg~$TSDhiCjA}b}L37>~lLC&VcfkDEH zh>-KOBFG~UYA|sZQY3Rots+KgAq}dH0OLeNdP1#rsUSqaeB}p$bCao)EVTpXVdgrX zP{flVDEI<6fg3fI?1?~`r();MRRu+xbgAT#6xV$&2LxZZQ_6!(RN&PDI`_yz7i(SpJAw*j6|AdqYCsal?yuz(A<}yXTS)vtq z^cuRc!*iVBLVinFE5Fg5y+yl`orU&cJVX^*5&Y%yPiI&AHfXdJf;Y^hPOe#^12_n58S%$2 zPk*pRaJP=-qDfYj2G{FEe4{fc=Ak0OV9e7O$bN+>AZ&pPgd(+skr+fmg;8zEC!B?v z5{1NJXUgp+Ibp_Mkm5%S62@7J7ijgQVHs%>TQ!B0L}El86`EyOp@0&T2BhLL`!c@8 zXLYDt2So}(j>0_5$3YyZJhckL9VB>wC??X1ttzvj)KF>x1*Q>Gm{Or4OQ95lf`@7; zpSh;Ottp4>=Jlo3jF%GekPW+e2uuqwF}Q7RgPkplXkOSlYQBpZ- zmg@3pztoT~vdLxQUn!Upi4trqr_p(}P$VWQ|H5AWI?1xQQRghuF43(BQYw>cs^?VPj))dg%j6D{+dEe71Hf6W#o%6Ft zdy=1+*#fAkpj#d^LdtYGW_(N^z={z5EEZt5t=~{-*DIW2p&gyhk@ejjKe3%ZX{~sB zK!z$bE+b?i=`C6dWyA8+1@a;x?v$=B(HCRRf)xd7E)@duuq<{ndAv}mcv?ffbq7JFzz4l=&BJg~$?;^a8Pj-K1;#SB`9=*jKn z(Ptl{n$L&TeOW)}-nO%CD{nqs6O|8w{&m5r%^YkrO&z;Tu=Naa2 z7=sxFY71}@eVq4tVpht(^4&Jm2yy{(IMrF4N|kK zhX89{uYiiAb63PWJfB|gFWjYMgkUW<9y^YP7PUcQE; zasJw!x1`}UL6InA(DDRPuyou`bd}4!{9;!eVc8=^?4BGNX|>Q^sANEK9Cg&Lboo8` zYPnM;6G;%6OsVlIbU=A8Md_35oaeF5R2IhzEjVc`$@ePd5{xRUDEs>IZF-e?ov%#k z&8u_Um863<2MUVvXt@?$>Tp}lE_0E#L~gU$P|ykrAWkUMdJ(F2sMLf|iJ+8|qHw}# z-~%RRh%5CHiuNmHVv$`eBNVngZepSe9JqyAa+TT3FR&@?xLGwn@!`o6O=t(irDy|P zDh1Xm&EO6w@u+er<{5q262pg>h^G?KZi1)2_wF+TeLvaOp)tZGJ6|POe|7bh0pSMK z6*WGb;~7=0U$tN;`S>cO1L{7JS{}{#1P&u@T*r3<1bq49`A9Lr*=nev#OicGD8Y>v z+N}Y7k%AIIJO$C2OC)d*R=A;&5Ch(utMn@hpl}Mn$iM(7@)w)edCk?UTpKLgS6@js zUj2Y3ze1pfSyrLM9cG>h2#^wrAb>r8G%kzY$UDa8dojh z5J5u6;&NpEsgEq(*}64H?-Db(O-Askik%qn-%v=1lcV_v6V`g^02IZ!21T?W&!=zL zvYs!-HaU%tb-mD;|6`B@nv2}%%OpSp(wglAS*SJmbxZ0CTpAgoDqiX>g=o9WbD4{j z8?Ih?Z$72g$W4|&AQ-sYjLA`$44^mO_>X-d+_!So@~B?Jo?MY4qzbXdu8hyC4PYySze=_Rtr^1MIlsb zxJFjH&$DP}-FuB)>MFi(BbZxvYz0!lJy(FywXL?W#C_*V8Ln9(v#b^Ci}Bv5T1n5Y8jQjOhZGQY$?opD5Ek+3}Ug>Na|N|{CWde zE2ZU8JEK9Qd8jh5e1*{F0zv+*NorLW_@Qd0mNxoKz@Z9S^2~WvRWT za|lfEr(g;~1t=eVj}QUqq0v6gm-6^UKns%KZUCQ7pKc9hO4py%0{;xd`sxqgQv2vD zW9NV~2V{Dh0O)b=-oC!9jxuOXkm$~yKfLd$N9|rGkP=YfGQKm1Eh7c=0Hz64ILn=r zC*%P>6`;@pT%h6!;fR2Wgrdh;PvF3TpB)GSvh&Vwev^Y_+$jL_2<*&#+w{Vf;m0?y ze)|bNFSri%j}G_q0A7V3SX{(!{^~!kzw)a2lNidT1`tAoI|w080DA}7BPkm|fE{|V z>A(K3xTBT(pl3nUYsVYf(56PVkwL~rl5~4_d?Gv1nY8%WDAK_utbvY0xu&jQtiOL| zG)MQd?r18AMI&s4Mn=>9OsuDCGB?@LZ}qcHNGIEC33eWAZE6Xp2L{sN;CN$0$}4#% z81D-F+BHxg$pnzPkyL7=E|7`T4|t@%4s^wXt8-XCSOY0$rhhQlkjXSvXC(fy>gf2u z#AKkU2MN?nx3sl*>m*(aZD~Z>tzoNF+U6N;MP{MaVJTNEI)cP3bry-F)jQTUJtGMOyZZyZW7UlVlJ#Bjs&w$m6Kl#qhP7&B$=%#Nx@pF(Hn45u5vp}NJK>iGHXtW%_$)`0?PI8?JcuyDcMfA;`ks#-PBK;PB5644x}mizm)WBG#+SACa1@;OpLC=*R}PEKYSZ{kPkWA~mu@4*cib>@NZ-+6l3 zjX%bmgKvKL@f^f~KtqEbV?TYH8xr7=qn8c~V4B0wgGeOH0(AX@f(=}x*|oE>ar53- zjMHJ@*zgnobW3h#I-3kLiFoN~Fg`F4 z4~~|`6HGXnou0|{*U`OQC1ZiERH`d5R?^i=*FD)ZI22v&uj^tPtJ3b?UU#~xk?pGU zuZ|84HeKcm$JxmG5mzGN8d)D<<6+-r{*F#xdgOa;J-J7)Nfe#L9?A8zeQzY~>+JB4 zq#thW&OVAxVAuruXtulc;k0WcT$lF7$J@3>5+C>y3Eu~a$kw)T0E<7a2t)olq=+x1 zkw;>1h4$iF@wwuX0Nbn z?@LozY-;Lg0zL{GWim*tgJ~Gx07$Wu$#!{#k-jOwOn}M-l-<6LwdA!hU14VMnxGZQ z@j(EUfh5K<$1TIqBM|t25C=RNPOc@tr3ECJfW|NZ14)bHe?W6ofeF_@c|I>P#%-Fo z4V1U^A~oEm_Tq;yv3Y@*`i~o~(-%5)WKoa*TLFk%(xIU*ApmWcbZGyV5P*qGJM>Ek zfcugT9r+Rh(CqK+_5Z22aZ78a%ky^ufDFGQdv+{TH28^$htslGkX{rfmR_UC;sfm}SU_s>Bq;4X80$qqjc0`Mx! zQL``YeVGXI*0~%!E+VOB^nkKZ?CcOoK#GMB0CfN2m|YyTac{aVJmTrfv~Fqa{nI5U z`KxY=wda@KWPcUo{(3GP&VAj@RP`slzwEJEx_{;APp+q3w`J?u_dKZ%;y0e41!e}jy-Au0)U1@A>&cR9=U zUT-$)_0S=}YS44-WeRAnfz~FA$)JPMU62sUwdXQKwUFjvwwmD^TKIxeyU-rOZHvuq zChiL)s#i%R=X9x0DVrVpGJgW ztBb1FJIXPCzLl3J;F0<3?J(f4;f6AFj2E$Sn=rQ-N09QsZ@{?tIeRMS<^CU*N~D=_ zl%4fTxL2?q$iSDM<68f-GT^@aT`&v8TepU#yAe4U!dtg)9g!_e4VS9Pe-OAcmk7|8 z5xBlf1gP~(2;ACB1PJ>Q0_V9zfa<=4z?G!B+yimn$XJaeFo`y@4TD}Zny#-;M@O(4 zB;ZMgy3mlc5oGJdOLZ<(ErTH{Luaa=Ax+qPS_AaxBAjlNla{e69Vk!PN{&vkqAr7GsheZE+D1atRGB!f}56`l`v z#FOYire@U5rqN_VGU%=gvkkt%NF!EX(-5Dmsh${zmjkuqNGg_|h@kaMeQ3Nklu3t> z8Miyy6Gk$w0cJ2blWwhUt8LB2>f0mHbZjJwH0K&ZGgc-SL1URnC*zOC`v)Tc0ok+6BGb#CqQzUOr*Vboa{YXcytu5z8TC(+Z6Vea8-94V8GoglD za|-Fqj@H%ro`lj=w%hUYWp+TiZrpP72A8bzMbVZYNQ98qi<8UlASSaXXnOaIU}oCvUy=)~A1?>io<6 zTCFa|Adq;mqtePdQU29ta`)~>HgEn-IakMMN6X&T!_<}!k8Dv25O2)uQKs2kdkofb zFuyGVMESZumKjI9g5h@GK<0HA$m1EQq#YJ1vS`bI8;^KY=zi_PKtY#_(AS@@P8K?b}&KjKv)KEDzf81VV0uYI9!D|etkmq2ix!xklwuM@2+D4iN`$H;ZddrL+jh1^B2`NpwmUo(bj8Ms z&E+?)YpdJ=b)>2uTSs~;e^#*~xPAM}0!!t#Wq!tASGTnuN=^_JkKR$cv5ct3mw1<4 z6V|`SabC8sf8c=|@7w;$GxxQvU2F3P*96zxcw^*>x6-`w3IZ~RfX;JSc^TqyR2_=- zQG4^ND*v)J2ZZzs3)d@|NORY&$f&0dj_?GW)uDJxG&he(v;2`(Y1_~Y;7y*f#%Nobmy@JhTGA7ZEo;(klGed#mYwmAMt1EU z;b$AN(#c6_R@F7yXkE$;5=DGk0HnEb6tEHVh5c-_<5Sqi-)+9r zEBq`ntH+01ff77>bPr595VysGg6Xrb0tL{()mFgC=~vGm2LECPM~y&5QDQ16sKr^C zVAgyRTT8Ov%CJz84$60MUWN=nMM%_z!VfTunWY*i`hjw`4Zx!;JNw2Bny;k5zF9Ce z!4d+~39KN_FA;HekeP5& zbzumqL-puk1c)c)PYuE8$uR>|Mg%7yhau8pRBT~X{p$+VoL;Ya4@Q;&1?^xqEzfkZF zc5#9S8lUB6i90}88#u`uY?XYW;5D^^;7K`Iodo&53xqYXAbF+MFBH5(9uPd$f~zHVy-O|^0eySo2 zw9$9zU87rI#8%$6m+3n;?B3I~GlXe_0$=m1uR+BW6kY!G!RB%#v{OGbe2mR*NI(f2 za9m@bfot`8CJ`|Cd6ik%G{1 zW#1U~SazP1InDy7M4T(knco+L?)=Le^S=hBJqo*B6i}s{lT0wy!PL5-T^3B#o^BIU zC|Nv7Yb-4uZIc$z=AQ2E9y47aZ5tJ*V^&N1h-hqV!ndSug(a1;tf*V!n^=*p=jOYH z-113(S$Mg1V8FUOT;`u#o@?MH!uss=!5h*Q7~@PPoeWlyzTqHUQ|I-BtC>UFN0vcA zzP~@8Mwg9jKg3jrJ>I&STyJ-EO;4f)dD}hmcJg>4aXk6TKf!dT#H%hw(Ev~WTA+!s)Q1g98Bj+nFUYiS!4;PSMaD3=X9UmC&%tcRz zbA8l{b}MIB(ic4+aFynE5s{B!&;U<-p*iQ781)>rPs!P>3TV$YU+`oMI+}fVWB!|Q zVlPx>f}k9bEvydi8{k|4by^A;OERFU(mJkv9^6e7oH2Y(0Le-s-a|Lvdh_~q)q))k zdt{<5T3Rvr+zF!c?z?TrP6_*HP{g)6h`VxV3T_Q-$UqbL+ z?H|MHC%UHTj$W|mTP_&v)Aa{yLyxzcU7rT5^=zZBzU}WWc8;@@w}zkGSC_2oNVvh` zkB=u=Hr4lU1q=jc!L#NB5(exS2^gwcbj3MW`E!AkezAmsZ>`|wAkZCumq_6y@}!bU zczmh+U&)_scZOvf13^v%uVre>pvDelRJvmgn;y7v-MR(W8epJJHoW{Amtg6oswFuw z5}oV~hkKhWO-&Ym*K~RyR6Ei-G~-WLJN+TIYowd0lcsth93&Z-=xL54@$`ta(LI(P zOpc-rlkpj*Gl9C2%|ne5w4u6wrr}Vi_KBFuJr%Sxun~VlYj8*|^&D*XO!Kmw`>~~N z-;?3~aA&V;%*A$P`jZngBjH$UTYLXdQ*=<0NH&Es$UytZWNm1$p{}}NreVC{P^2e3 z-9Sfr*ztx8GTkGsMW(@uMWrK=WOGxxX{@VZa2jl4_JYw{Q#~0y6zY})M$`VT6yk~_ zfA@HkBMl8BjggU&h$KE_b@%nX(6_cGvgf`nyEbpCcy6hcvIgNHMuK9}d%4K-@R@I) zDj;aF&{4V8PI&R=U1EAY)Hn(T?Q1sUNIq_NRF$<=ve%Tz1tKLXbv#!`kOB%S{UZ>6 z5IK-d+O12Mo%st{wfu&=wtaQmvZa3EW)iP!>*;Ch;oMQMFWr!jzhl>RFW<8YhO8Ho zhlXcaKe#(E=odyl@{e0Ds~qOkcQE9&4i68%F8u-(!Lm8z0yF~Ph!;WG3n~Idl-$oK zPXz*B{4==)=Ol+_f+0{t<)Dogeg*+g^Z!>y7nKqeZc-wG%=im^FWkbN3>>9Qp6h%e zx9 zGI_V`Ns@xi!UJ~H8RZ&% zNqhUF7o7M|?NEKVrhd9UQ-83ryKbhQY3yb*ZMD)c0zQ30l5YIpO=OYAbh-)q`$YCX z9Bp*{$Y=-B1>>!?q2~W!#DTBhH{a8u5i2msa&YZ~YrWRBUZkNG+3po1TkCHtzvjmT z^TZRceE*uI(PeAb<~J|Q7zHj{Y;WQj2p()HE8Drlu_qSMgtt`WZCkgNw^j%bq(O*m z7l=D+H*K29IElKDw*ee6J8jG3(ap8nzj4jdrIe8G&_Lv2J7KT9@lpi@ZYvf~_D`mp zCK7dZiKsOiwff>SBNyCNU$3RZSL<@8d+4xaAOT!2X=<{YX-JQCf+muH|5`mdG6b6c zh3fWXSLaaY%t-n`E8_Z-znzYwgSFE$BL!(+`pusD>@%&s$XFYmOLm|2O}l&Bcrf+N z_%nlOoNo8Gj)|+h@ zjJC#*#497!GgnzdXuY(lr9ItSl+jG@?)5h>^FlH~O$iMsu!rA}hPh4Ka1IOm`FRtA43$GDB;*(x@`T3g{ntkyn z9P9uFlB5kmp#BIbCr+!8muNIlZ3}1*wV>Ak(OGM_meXVlOGE7<-ox2jTahn(@gD-N zeBOlxd_Jy>-i75KUT~`H|7!9aO|;#1iFEJkZG9$N-%|?0z8;zOjSpd6Ow8AojtyNh zwvhDyd_euh7nL_p_GU9DXWB+e5*wnvWe~M$$$U2RU@Xzw9F;U+wU*k}+SDau>$86| zp!%M_=~_m(?yDzo#En8l#0~53vw6#cSXmIMZ9p~!MYfx6T-S7c<&7-dBbWrKw_BT= z-#!kaKbQxxj}xrt)SsVO!=v{RxV?PyffFZApdK#Pp+7!+Y-(XV!5EnObL;%Vc(Msq zLfOKII(hOWjHh(7ulX02wP642wW+t?JN_w*I|Ka~ZvEwbYj|b1A2@*?KX!Z=tTCnn zM$@rRrluA~6pW@zRiAd|u7t0p!rz4We0N=PajLxyp+FNd8l9XT@V$~43uTTqBEjSb zJ$_`Y`$T^evh(fjwdvkJ_?u=Lni`R0u%iJDMv<|miJ6rD<=)ZS@wX#Lp#M+ZK4i4# zWHO4}bnJ%ENaBya=yZKFg7p7a0P|P7+7H&(xwF~U+ScZt&O@G&K-a;~-wH3h@$g|w ztalgMGIF1#gwFORXBs-+>D_fG8JcN}C9Xz#>T`!i>U;aK#+e8bVH>gj-ujV9%e1FW z+MNvr4`oKj#(L?lp=SS3y5H}L_xQZ`OY1tY-_J}~2U|vF(tkad?0f%{;p6l&4%W9@ z-#-57EO$khKrJcR7aH#D10>eb9z|rmB1hfEB5AZGfs+~Mr5PMN*y#cU^s|-BpeG-LZj&uxm2c7K%mV9VzcSHSw<4fVCsh zf;ElCqQAa6;rnCkVA`Fk&pq$$PGH?$=9QoHB~P6E=^ab>Ywss4Q&Xp2<)+-(MDc*3~>brLHVabwEny7fEH zoUXfNFW#{I!NE5_+ZD&|>if~FuXatnm3jR8FR|}D4)q#==MALT1-1{Z<#Svfj@gD& zdAD~|s7OpR)9SP5vPZRi{H!~59jJbV>suK-} zq^qqLe7ZN_S{*FO!nHd1kn?hRFn{kMxK=s1R&_nijp>F&5(TY-G^o)DqXU7^-@UsnV zo(28$%1qQBy;0I7>Bg*yfw8(+&tOA+t9zfSj*T~l6Z=lKwl(;A#=@hiUQ1^;(-|K^ zcC-G96E$6((Te(9RrTP2-+4XiiBI>q8`(N+G>h!38~Eexx1r&Qn6zWH}L{#Yy<>`l*v!V?*AQ`--D#xvG1 z%sIW0Tzh+x2}BTgG>9OhW2_sPhiq3iIjbyZTSFG8v{KvFpNqQ@?tKJEh}k5(y~;bw zTK%;l;BLUjqAf4YdP2% z&a@<2s)J~I-FPh1%l6mzgN4(ctd=}pmpwRGm*}M<(;(N=e^)G6TW-=7ZWxHUYfxV@ z_S1OHJKoeyi4N;P?1%Mj?$(ZhtD>>dCak}IlJUOpy=SDOemY#2NVe>Z4E2s=>L!D( zfrhuon|qsUJZM)q)7~7ix}|?;KQ#8^Xy?d%Eg@H17sHGW)OI%wq{Dr|6`}+<$G{zO3%v6iLaubMtd1 zmJcNWpcI&#{cSBl0QYR}x4&MM%JKOqgE>m)-YtWQjM$>8T4Wp541scQaeErO2Rp_> zA0Dc8BfjR;OxNha>5-n6T7Nbh9;tVE+a8xp_%PRh_2>IiLpAHO4F`GbT}U_PmyGqK zCa0$-hC1tAu|osXhguS?o`K1RoO|F*dT6|PCRelOuM&T{x3_(yKOWj&3wH&6u%|BR zqYq}j=|OA!0|6|VPWU5DXHv1Ty7yz&4Rb%j66O~mPej`*R( zbpIa{H3O1^-pnJu$%C#~INlp+NQ4g!u;Iq;{+gP0Ndi6O9k330#sV{~fZ5W@OuV{qe~eJUQ@L`(mZZ}r9A(csy+_cHrGNd!^7 zvBF_Tc>bVg94b`swf>vR5Sx?`YNdMPsZTx_Uzt2kYj#JbhToeT-K-046zuulZ1y;Y zj^4S%e+wk0Lr5dQW=1!|D=2!0a#e#e2k(S;A#=@!wn?EP()I>ew+F5JVi}20ED@B$ zy?w-ht<^Vg8OJ(n^7~78Fc+l`?lh%_MW_RcLFdwym|Tf2gxD<_^pNu`G#<^+<<0Gl0ttN4&pp z$9mF%;o$%qjKw-TIw#u$!I5q>_B&6@sH+Q!yT_Wnzwi5T-;+_$DlhfP_i=gzG|JJQ z9=O-YzNb17PS4aLp|3sKclLFeTrPTHiExjSIQ;(@dmA`4%5!h{-eq@TH=E4BZUTuK z*v$tdelQ71Ku;=Uvk64of{7cbj}@ZHLeg5s*a)#zh}D2;Etnc%<54g*LO4;-en7N3 zQ&S7IYe3Iyp?cz={VC|9VazrKZIXGfVH2y*d!F}s75yxB4*Eo?yODAgj)xP!4~a7*+bF}bUS%%)P_K2xWS`k88|oIsA;rxB-9 zMG>)LE9X_2%ef}TAy*@aFrU}w$8)(f9yM1yemoF#*mYX7qu{e9FDv+u+?R8-nlZ-0 z)Vxe)Z?xEng(_)pAS(?GB6Q8fUq3y3vTQXAR2lSjy(0Qs(n2t$v@#TO<9RAR^sb9aAOUYJ#E*dA|>8 zo!Jd~kudE=I-H1BYg9Tx)v8fK5C4IKz;}G~@+~tvAumt;r+09^6w}V((b0VK$G#*m zRUzgCM5r%B15*+SgqS5$_2NA<1)?q(I70aEhkA~?Y8C{Ip*nEi>B4<}V{rl`M1k?& z4-_iUO99HAcmRVi-r3haIwl?*SS*o1@d+pyF`$Ag_xJaS1R_X>YsUuptxF|?rzR8W zmq6uvHXkm;{yB0RTIbXdgOJ|HCwhwc_u?lYgP@Dc^JmRCG`wu~*GLIL3&eU~AaW+Z z+NVsb%6AEkdZD+hNZ3^$l6Moy(TM?8kl_3XIO`V9!3+=zwA!CtrX4b^U2M&{rYJgFAaS4TcJS8*$ga1!I3- z*dq`L?pxmd=%bHDAHC}??_J7gI)@jIc0(;P1Ql2fT3}1{%;}jk2(mnq&MvWIPi8># z%SV6vLGC|yaOJY6Z4~}HgV0DLlxe#{)0W|12L}E-!*OvpH#OA{#s$nvkUy?}^7tfg zWCGt(dr=uFp=Cu~9zuxp){CalfRt?`czpF_KjzxZdq$I!MQ+-8A}<>pF6$ZyO@pxaVU`+V8D%@a<rb(!2KS- z56fmn7HBQ-%h)ZrRu_qsM3%Ukim5qUrsU0AoCb?MAHY=x8-#W_mp2@4wog^re14&) z?)G^DkcpzxRv9-*sA(!+@cF^8IrIjhCrks%K(9I)V5C0Mh3+y5q$i??w(%3 zY=dCi{p&Ff206Ue1|;KtIdSYH-CmPJm5J|i3XenqUrP-@R`pR< z3(;f(fL29=IdwrFP5gg0#j^5nLYJ}D^s@Lf7+Kmrz1jklDwdS-C>6<%?_SYtuJ1vt?I2Aq};n?NMqyt0nLx!>ALSolRFLr8obiAuNA2o)FRvgDb0uVe)GA zC8Z@+xyRA0J5xW7D!zWP_X#`r<$n&Vx8&KTUn@2-wR_5f|Sh2mwAAdj_nW1`}L(-ykiR zI@=H1Y(aPjjewi1H(NC-0BaCfIS$KpgpMPaYXp-`rrK(bts5Hy#ue((04CM0dkDLLBel^`Kc^0gA`Pn{!vKzyPCANC zA4dd)QP!qUS1U0r8p)B@z8O{0K7uaC@kli5&KX)-qov>{3_`8imp7Yq&04_b^Tqu+ z^>o(QSV)m;-|Np>S(=An+c!54Uj46c%49$mFIKtmMp=DRXENv0gj=M`_>Mr*(p5N}YEE5nL@5;R(U$-}z6z|(G zacpAeCKy+8+E8d3Mwva(HFFsT#Q-v+9|Bkgc?zK)-5_dke~*H65GKBF^28^TDbWu` zkw)9R^Tn627bkqXZ;<`rhhH-_1^qg+E?j9v6K2)R45KK-l1P+{tt!Te2x(5#>Pd?- zAJ+R`6BYthuioV+>c(DC=3^IyDHSbK!vK!vJ`q`9o5?c7EY}Yr}i|hEPEj zDmPmdpj6cAl-X*}8cOv#sTWf7xwojF`@R0A@1gB)8H46pK&T`_x~4~%3s_gWP)nF- zDb&G$uYi?&4vR==tgD2StFE)yZld+Y+Jg@f?j}VRT%4gMG;(;z)8d4jqD9v0B_I7^ z!oud$H7C>*2}Bt6taHNM^50C@58krx>bs9Qs`KKxOn7%#X8Bld#P}Fjs=n25$I5#q z{>RCaFaBcJe?r>VjvXf&r)J>XxfdrJ+$^Zg0J5L>^B{{4$3K4UM^Iqw?4S9?KG7FX3%3pr z2=;U?N6Rf1GW;-g?74n+jss~%c5dtOMjySm6HPe(j_|R!#u(s06VOa)M9^(ACe1O$ zQk5wlFABJtsLB-hsG^Lf`vve?1&h!R{0W54P8FF8%y1ckgX+-aq0!g43vY?IP=PSktldri zhSo)b_ZY*JI$CKp%MrE7w7yt~Y8gfw4cu$3hwBDE#l+Y7>*L+H(3BIBwg3v)$V|4S zyvJti$zGG!T1AMcLWn@Frb$r?4*SzSZz*YkD7DjjcaTJy&zX`)5oz zX@a{w5G0gMqsU$Jt&iC$L!}NyZN03;yS(6R`t_BdzreQo&%JZ(lIZVgjBZR;-(}gV6vx@>(ZutGr80c@-kwJnc5W1Z$5VL9&|$s% zN0-m|{PF&m(BQ1kW`4@DrP3uBmU(!8Y=T_>`JB7@L?DWbKo?hn+D;C4m@9%;B<+FO zP|w#4j5g`^%CD{Ocg6I+TvZL}8s#ct0=7hSzD_B0q6kro7VJ*T4P;bQtl>)|(1hi+ zddtl+Ov~P~=&6ECo6UKx6m7$0dOnfRK$u+~M=?|^g-LrN!BmsUV2gGYYxPnk)Z%kc zo0hmO*lf01OufQ2TQFYpms+wG*5KG?gR1(9h+g(;lQZdLQge)pLQhh!g>gGokGl*q zS);bS90~g(_L9~Zw;*q&rZYQDY{e8aD{CfyQtok^oAR(|Up-%}#!XH_=F$CD^nQ%v zwwtm2@ws6i``zFPXTMW(7(TN+_d#R^S5voPQ<2W5N3Sr$j=Ew??Rd+J6P1O>K96$Ed_) z25%C~Qlfnc(@ic1VWp;1RD_7C@x!f)2WqAWjX}>JUQ1|*vPx!E)jScB5@FDx4ED;igJHb9} z85%mk%>cIO#6%Q~|>ZMx{9(iNwt+%$-2lEUIJvG>)hJ^?0FLk;y7n zFVQp_n?$mXF>wkIF*#$v?RD?0i1NFUh;O&ktV9NHp%DsY7@tz%iWcfzlg=r}gIuIA@7u0S4On3!c|5I0`a_+`A1H;Sv~B z0Y=b?_*6Tv87BVhJdv`I5sf+EY>w*=mo;@?4fnrH*Du0&hKCS;E|ub^5!eg zzjExlS0+q<$eNyt^xYSMo~Hfaf5IQObX2}lt(QFElC-2sl2&1({C?dC0@d-dDe<}Q zrCV?k&~mHN_<_vYln1$R6Sq4JOg-nbsSvGAyOkkFgbd~^YzYEyE;pNSA-TJSXDMAq z8TK`drJO9CbNsy9%|>&&%)3>c-bG^B2lS-2sg0ib#{<{eay8pCiX}~dDWj{Ct@+^8 zV9h2c;xf(G^>yvFANN!m=k(mWA9v&^k2EF=DD`@EG;2gArQRis2TK`IG#7P5>;au8 zXJ@n_sTo%qFcOQ)E3%rbR%6O(qq>p}CktA$`8SSa>}f|>VNU3@PT~ki9=sQf{ntx8 z($l%M2k5((9Izb|6W5*i___sezwlrG^*Ci$fR*sr>q$aOy6pvQFVE50RyRyZHFl2o{&dxy%ImJ) ze&hH5?Wh0o&>U~~wg(k0RUobGRJe90datMs&MaPNb8nx@Z zTM>rQqd(CMUL*RJtE}ieW>__#~la79e)jnA$MtFA{;&# z;cxsiEQ4_kobpF#YZD2Tl{lU@+r0JY7;UM@Kf|Pl{=^b#M7hid8gZR0toL$ElCmXG zLZ9`0aO}~vXwQ0OFw%;| zWu;ou8Op1G!p?BbM8ehKPJMRZiKW-R7RWW?ssvN?wNNr>YRIiIONBML&AM7styozJAL+xo1cceG(wy1aHA^h&0!Rnbp6BbN3s2(elVG$y zPOXO8onVf1KmPF(PyG86FaPe}fA{d~ugwh&%=_t2L)T0_^SXH8=p_>)SBfwFj~9p4 z9kV81+b5RZ9!fgCU^C*m18@iGK{o=FBXGVjGc)s_{S0>LrP!sQLgG0>brVNbU>ug6|?R+H`!U2%m;R#&aP?fHG# z?Xb6`-ukgYePwCAfGix)lSnlJOnvQu6MaM z=AXK_MBu`z}jId$1$zh0(N(kLsH<-nfKYBcLy!_)$^Z35fLG81`}&V_tV(&$q{ z)CO;;l!j*=RzDrb=xim|vNU@>71yjy>oXhN4QIoZ8OaGt0k+yy01tnuuW7RS)G%nU z5(S$!m`sLBveRZZyO&e0dNlR)!NDDqQzx@G4B`8g=XynuEw*qgF6<%{uF0u)ej~$= zR{u$Ey!A!`8{InEsX(LM3zn^1xqjPguf6lw&yF8kP5<@MSKb9?x50K+H}6KMTmJE~ zWjB!EWtB?2H(bhuF)U14HZEU|&so+1Dx4s{2`yjK54AF(HsP!*`ump7JNM5!G43At ziKacAX@`duQu3HZVP!HwfTFq8{*M&Fr5)C z&jy9uhU5hT=taKh|Nkok2D9#kyg#^$zcvMU^X2iz`TZ9se!PiLJ+-4}Oa#dWP<&BH zLM3F&IKF(rh+^?_)yDpZJ6f<~w^x=tvfBefs%WKVnE)JtVadqo`tBY}k3euK(Io;= zD^cHPFjn;1&S!v!D(dFTCidI_Vo2k6O#HE{YkYje9uAU5v&~50nW~H9yz>!}3kzk5 zTv|~8HH>@!$R6S1dN?0?k5# zEy&So0T&30C=!>OD>iR9o=hfloV+v4q-8Gl$Z3zL;1oGcnGoZ1L|J1^Hoshn1++;7 z1ND|jRn94ZEG-XMS+m*X4utk4!1|P{jR>c2p}LI4>wunc!A89tqlAfPLms!+?2{+q zZni}!l}M;#Y>FV8`&Px$sx+vwhYC~^=9o2>Z6(ZEIb4cvBwDTF&u?m$1yWMKhsAI0g{OiS=m-RfXhoXU?L$ZR;_3GpmG&&{c&%Vw;aXS8t zL9iicb^jC$7+?dTc$WhW{u0Bz#r%H3^83#*?0yY4?pwFLW~`0XjpXHcbe zCI$z7R;7O*_wU()|Ec@t*Sorf!f@uf=Srnn%=~lQ`B_&iUE0UrG@^w=BZ^L9(IO?J zEzFo&2{~WW4=`g*eaoheyf@D;b5~{mOgEFFR@$7F=MCjtYX7XPR0| zhJ`_%J*~|uiWns zS@Ro(Hs3YuvX`E`^8Ci%zWIZfzJ2?8%XgD}2*c$My7Jj{!kSJbVh|7DIFr`zXy}bF z7WEkGTEvB7M7_qC(ij$lP;3HI>aA{1AkqSf_+5XEjJbl7rx&HxEKn+!Tz+}ybD3F_ z^Ma#@*hqqRbU2^SFZ{`-m7NQ>ZWXN5h!>2Jf7`lu*L6Dg&;z?fooCYzKRhry^G1oq z&VTY6U`-!29zV%}t>WEf?2^8BdD$pAzYJjvKB(Frxbr>DAnKe2wN`QXz*B=z+!cf; zXG3LBNTz}jK7#NpEPV2_E)ba>5Pz2&V<0m{Gu^%~Hg_Idis@+@fguQjIth3^ZJU5Q zd3plUqBdgz*a|Mx-U1fLGQH-4-{0vb5pn~7?A#6{gnc|%O~mD>~u@VbiA6o_d8RGT>ABH_YcQYYFZDGsU7)aoe#un^Q< zU?Q~RbwEt`G5|QX<8TVFC`r%w`1OLtv>yp39{J`ZxM1PT#Mx9zF?Vp{v3_`!ppJuR zB0g~>Mr^pgxfl|d!4}aSf+Ff0@9iBVE{9WQpfDe{gD20KdMy>702r5NQB}#Y?jFZj zH|p68;X$5H#oI8orL`t$)E9@VPT6OKDjKn=DZ3$%NdLTuH7yOLIWNlw3NC4@yd&UE zi$ttL8ZKlS){0u;aGyT)z_&ZUvrnk*s1$OUj6JBzz&FQblJT%htIQ;959XKp)deo? zh~jkC6w@0_$Bwct`ODSyNI{y@C0%h>oM!7>-YN4fc{N)@{z`*sSd)f~g(l0@ zaMd2w+T{3U%dXL?o%XD!8gZo6*;+Q6VVxBfbXy9AeIlD@>Tl=#9;v74RhC_9y^JnJ zm5~H4w`(z39E)U?0Ort%a1<-cE}o~SQU^fg;IWgRe4J#NP4hX(47{Ybx8pJl+eywz z^`AHef$3_s%9rb)aPFB=k_S3Kdy*FDp=L8SMG~bzcf=b?R5aY`Rjr~7{$Gz(gKafZ~u&u|?u}pL4$`$KDM`2ho+xUBl)wq6* zW)WKP$WtTyzU5yrQXDMTlGcc$INJh54(S$C1DV}%-!-L*Tj`F_&j~Ax(f<~3(b84n z51MK=9_zOGHki0%8trGQ^+LW;JG8!TIHvbn%#OEhRDRxv5Bw?9_{eFBSl&)fU^!RC z_QiW60XAK?c)~G00;}*kSnk}Z`tBs&(_WENhI%ZpUg~mHp3%xo zuq&;$DUeKRB+^A=YlBaX+sRC(32DmiL^I>8$;GPB?HgWql0mH(l}Kk9cnzks2j`@= zI?#ZZE(yL}$-2#EkAmi}W67x7Yv6MG!-d-UXHyA;sZV`!>WP0sOw;^nAu{rPitTrOV77bn4IUfTc2RjIv;A<_MX)ZXDu+KI6< z<*~CYwi?V3Pk}PO0Qv$fZzyaab;5H10OJDk)6`^tzntXRPHD|#KLPy?*bv@w5GKUi zkBfvR@T3t>zH}kav^Qjg8$~_6=;4aog(cpM5eP`-z!4zSpM&XvDS&wCmtgy4a!SAY zty{1R!20?|@vQzhTPhOqVkriUDrn5epl<-l0pt@<03T1KHmBkT5t`^_2lIZhnQAi2v8qY-=0fDgdPHK zc&t*41m^j_ZS&H?K)Iy0%dtfY;WD1!gz>y4B4Up`7voMBysk*&!(zg?zNG5>?UnW5 ztIZ6Ptv{<|scUaq_G#IAn8}6fzcrm~wR8{nZCLaXt!uLqXr8oSlIq1d#YWeAT*y;P z*tf|%M-&a6JJhTv9ph4y&r~Z@OWbDdeuGS%PRUs7FLbqeC*`WC z8bI8kgSw8O2}@gxB%Le!=_=(OtDvW?O)3~Bi;8O4NVq6tu57$3;c!>=xw-kIrC2fT zO4>;^C;LL{O6@rI#inK3wYw+&=h?xaYo+Jd%T!9Xcirw|%w%#a{+-7@*=zjKNe=E& zj{79oJ#h|5R6LQ1`}pbaWKz7k`Y^8fHS^r*5d{khA=V%N>cmWtt~C!zPH|Hn$t(dh z7Le%|&%ErCf4lPfZys&5xmnPn@L64*{hi&o;BUu&`|9D_<~;h7w~Ws5mFvap;=-MW zH!TZcoim`8>kLTi=LPAYmCT$u14_N|=i|JiH|wjWtrljB=`2-hw{!2c>KJW|IVpSO zf%f*Yy-;bTjo~2Bn<-^om626YP3|-jo^R8oSg8)#0(D@3B&(>NPLOJ;sca6lXk9p8 zGW(FnEUayKoz6Th(>u$yBxUy*TJoTUCtX>19+tP_3eD&8eql=p3{Jh_>3Y6O152U7 zQ1yZgmdIo!o(SLTF*?Hu3G$bqPf|75DwhXF!Ms|clDx3jZw`ga@Q%36=`p)(TEp+9 zZ(GA212W0%>ZC>MjCv2i>Hb0ix3Q*X%b2%DoJJoJtK^>rO5)+w!O<<6-4icCDZx*E z79532I1+sJcMk5ob&X{C&Yj`#1am5mF_V8j$JcHU5AZ)hv8cI||9-(g1_>5g-g)^Y zPhPzWTO!sxvT(`LA1%7^mv=r;#Fu|;=^DWy;J5$zOzF>GfKw+!E3J>6={f!UVr+79 zikmTW=1gFTLWN=M`x*%8V+7xg@wT+TKN+|^Hr#<0EfRuP_!jrwx9EPPkMopDu1E`b z6s*lkLIlREfq!KJMik=7nA2wCt-qkRzWwDoiX(mppif|8fSwcdfSvi*5?mc@48KW_ z8dFmm4cH<69mV|R3lI@RU%t`QStMw`DX%^{sn77sdJgMvZ)+cQd~#|M&I$y1J2yEg zn6`Lr0u_u&FHGpBr#H_gV8jhk!1ddHY2u*c-vn=;*edutpqPw)H_+ z-@4YgY>1yN92ICJ;;uP@(XJj0Dt&x^7=&`SJOU@9KP>K zb~ooSy5%yuqo%A=jJBXYY(phk;E!cV)uEt?hP+s@8TU{-AAD0QDthWdy;^3cD&|_o zpWv!~(KFV~uj#XZ#4fS#!sq%j`~7k=5d{jA!)}+8>WJBI-{5Nmr14TF?(xS`+JfBy z-=;Thc{qu`CASlI``Mr@R!qCIYFWu4rz(t+Rn)3~^v1&vzjPq=%Kl@!HVd9~-S@*^bagElmdJi}^G(;So`Op3-y9ftf8>hZ$(PSD zlLA~WfU|DG@#;)|=uyh>m&k<4z}XY$!F_{^Gw&QbKa-_l^UvqS{r*wg;17p}h;Ef& zxMKknndyD;U8Xp16U#yMbSSI~+L&K8Q7pGh27x>nVY7yaw5rRQGx6!h%X*n&0yMMq zLWK12uO{Sw@dgx}Qs>IXGo@=!cQmo)lDinJg4GO+fCA*;@koyUkYh z$ycw+xeTOj!dZ{RirJQzLzbGQy;L?h?e;jG&gc?kysURd6={>&ZZZ`r z_F`OU^yQVf(ri{cd`#R+=v@xQ`+s~%CbYd5E(Ttq?M|UOa4?m6LX;dI7Av1xIkX>) z0ay)uiw6I-N>qIZeG25-qI_U z{I+}Sy*H{D_WZ)}2M%l^`iknTDQ%9p_;6ub^3$=qLi2xzzj@_ny)&=4^0OypGSW3? zu4Ch3Z7&AJ!7$Vb$3Y_0fMU#(YWDSj5PWpY_wI1H<}EsR&pD7@z^1~G)mMUMUW>=t z4I5o--|*Uoph88|&Id&QO8<-VKeC>?m?)F`SI!J6g_#oonHeyDsV;w z^~QxKafsk%+Dnaqtd&nl{cazVrj0>eKxa-9GFQ6f zGN_t*waskd%p#GwT9+1T=AhPGOOOb;C*xb=|zXtBoJ-J9>pg3XPMC39AxDR(Cq3_gXm2UA?Cvqp&s= zGj1uE3MST=H^=PGKs-x|R29V@5-;V79=8jvta@a@0)HLO8?4$QIALTG40FOxsr0lh z)9fY7M$6hrPU208HK)6GDGVS-KrooNNx78$5Y({4q{gz{AbsiY0syh>lqLp05j zlobjT#X@zZhM%(5sE9k#RC@KEX2xjAS9DFk*P2&`AOQRH6}(i{tdr;YuCU{krAwEB zcrMg8S)g37asTtr0UG;8>Fg*k-fJ{&xc*z$^O~^G8S}P7diC`iz8P%oyY8B$0et3* z69a(ZywkoQf;|4{qdSR#8BdH z6fIvNSlQux^{#tIR&H9SYyZ|T(nnCVX*~?w_sN^HXD}|S(9(7U% z?7xuN=eJ}*MqTHXlAmsIk%jAWzPeMPRYh8XxyeMM=xCtY=~g`b+nZdEnrhpk@bJ{`4t1Ji zqdH0`i2lTcQdl8A$A z7Fc&5Shde~^8>fs+%tliHO>6BeKY!I_seC{`CGPpZ|{M%d$;@~?Yes1hz5xlNJcgd z>3cGB=G@=)$hgpbhjh!9TkqFE0WQ`bo5B6Pi<&bCbyvi7-KsSU)*AkP^-qug`X|xR z+qP*|Ze07y-D}n^8(qI;y~N=bkKMM>G9+HOesIg;o}M$SR<_%Xfl$d*$Se%iWtLpt z3gIGI3711U6B#dN96)x?Vyd!S-cVYCDupa^RU=ozbyN_U!=$<{YntnJ5nIgpfIEQ4 zbNZVarm&juIti0OsFLe%Fr%`<<&>Z9NRqOMA)QebYk{PTG`ai{E5W(7O@%G(4%69a zxG2M3qVls;4)qH%@o7AkhJp`5U%}}z3ImudFJzl6mJw2VzNw|vRbJ>{BnoDvSS23t z%iW|3rDY<(NGgb7Wikb4B=QvD%Y1lpZ|XQ%iX;qDCLd>yZd{K*PWR+_sneZ`loJvsC6dL@&k$%4+XJ+AMznF z>CnEW#5ao-4_$KAsaf2K8O0kf!7tl--wj7EzoPSD15Ncd@9WgcB=4hWzi7rbhsCj3 z(E-)Hp{3h83?{S}nYMyTgui2mZz70GmwdPqH(R}FGUE&Qf&LeXLWK*nHytgxnL@qf zWL(aO!ynOkD!Kv#HFBcCw4YX4+(A@`2DCl{ZaS6MwxU#?Sth z?JyiHIo&3s){+Rz%?h{0jKw&6jZ|8zxXdmrf?Nq=RL-!;Xk(WipGqxoRY9PdCy=L`5+#dmy!a^QYrGaj4C5%iWzD0Ug{b{t@WK zBs=#g!{NWnKr~jdQuxYH2b2ef@(uT{wGxB&7eZ~h59=6Ov|^QH<9&0toY*7i8X&sD0)%&-e}6+yS5H^hno-NiZoxvNc)ROsri#Vm z@~_*3oa=gep>ySuve|iMq7GQrD18PycD1pmV|Rwmf2A) zJ(!J!OhO+dQe=_TN|UmN!w?}`ia;(*M_W|}f~+l%s-lhig1%tIDt9nKln|nzui}+u z{Y7C>u8yih(vr-S*0!v&y4S=gO=>2E)Edq6MrgG4w*rUrw)W%Y&V6xljm|W#)fIJpyH!Dg~KWU+%2V7@Yi(E`5 zTDF^fN-QF@D_zWy9Y<0JMU#_I0j9^XWzTI}hCW;MZyS%>KV>tGncDhgUmScKox7a9 zay~Y1=&zTbUa}yooO4>aXxIAThh|IG4vpWY5i5qz{Cdd}7<24z-1O5f=*w??NVBkO zJYF2wwYW!gbVj;w;7D4e=BRB>DBC`JUS`Jglp@o+e@;w6N_YCZha{V}E$mo6bQXQ{ zckco}lcj&hg8>}p;93nKPQ#RetAv5*kBbtN zt>R16WTI><`;bbcl%IIqdAe!VQ|}#)EMOaP56PrF+(TuT(VoT-^af^5|c=h zN}<%FTCH5A#}&&LApF*Ya}9ft$$)RW^LiRakqWUAb#(bSpHJsBWpg=0CBx+zWsw9F zTc7sksiq=RL}8UH1b=c4cQ-RWV2>f6U$dIi`v42PIKd{L|G%60{2t--Q3&iRqzJ+U zOZ>6IFIWEWmiYw3>9f6>Nv<7M5&66#J~{%)ho1j#33373J4Ns@f&qw`z*s$qY4lT? z3nv6U{%r>yO+E4B(Qyc}-rD!GsV)(STC7o9prq2!oIQ)@?%fg{huI#o3k}9Q2D(;( zi4(8*a&B*dh`pf4{soi-gQ!PGC$6}05kaiIe`t~B;!LwbH*%w9#ahCk>+0-Sq=5{j zuNoyYp>jlXS2Mzhc0^s_+bhnmvH?Qx6Oyek5s8`TGOSe_<^~mZG@ZhV%3zWkt8Qgf zOUfxzRG!APMpwn-VSReHJPolvw_M@Pl9VNvqaj^hA+t7&WPy&8ezI7v1x*$MNtco> zuHJIA+?9$}uhj*MHmBR|F=TB1v_obzWg7Mhp2ZpNKK&3BH2JPI&~p5R%?D{hB~=lz zs5Rqo=JW}q6=-&;{ z$NK0)a$)z9U0iQi0OcrwV=~;id}QGd*Y}{Y4}tI~?LUT&_9Gg-I60%($BI%?F>i@1 zn6()uYK9_fbAc)(G}1c18Z{wDF_;Xh^K8Rhi^E#IVsR_X#(XJg$o!R+-jp%qgL>@` zoZ$mM;G*AXEm5kvPI1`x*pNiSzW#rb_DVi7Q!_2M-qho=|C#X0oXv^uN(j>>+OTV z20*YB8hHSnh15nb4$T|5d_`Yc=`$byl9 zgS-eV7;o43xsMYRi~O1hcH&@Syg2$_=ROgDfqYy8W@gTEga9KfgkD1G+3Qmg0G}jb|+(l`0bg<`Q&stCjVu?grt>*a(dQLaZJ%|EF zB}1qPzb$P|?%n4Pb|l%7BbZ359P@I=nzhHPy{1gl?hS^dwp8QgW!~FXCn6HndnVe?l%^Q-u?mM zruxrs5MkYO?$|yuGz?`g)Fi_aPR*nPa4%7q+{xR670b+p; zZtft@R?rX{4WBFwiNbnK@obvcTA5B4=A{{c(||!+Yaa!4G%4t+g4ccwh0J_h9%1m4iAAwWpD4n z-p5`83f5y2sn_~a!;83nCbr}ni3F!_s3=iD{4MdK9;htoa^$>_E1w~9idK-x)LTxqtE_}# zC31s5_jWy-h&f|auK5uaFv`6A0K}>O`fhU~LFbi4DjUhyYD#C-U#yBC6Hh0WcgxdK z%==^VckrK=_H_4%er|k)5cSUKUt7J(Fmqmi$IST{6f7J@l4*t<$8`^_Gj8Ckw|%co zLErvhU`KEK->1;Rh2qiCtvueX;S<6;J4c{?!-!&7tQ+oGJQV)v?rVPfi=%(|oBYLJ z9K}xkVPW{tA^Ng^*(Z)$6SdpI1qG&9XN{MgSEg@J6NL^X;j+m#%1Qc7Gp#V{3k8=& z>T$+A&5%3nZ2?tU7xPw`koE^;j`Ka4juu$q`;0T9vsAK9lh>b=&TiJalX zdaNl;=ZmM?QV2%>(9ByRBC=|Fp!Rdd8;VO^)asSOBkV9K&r ztTu$ap?QRY<^j7ErSB;(bLh4|=<^1a*3>;Ff<>w;c6feLid z=S;%D)8z#G9x_rAZ`4Rk!Ac7t59TvEpEQ5NFY`2b9RF@88?9aG%2BVKV&_P>caG&^ zlMPmk%>ng!1Z55ec|t5C77U<)$+IO5=+J|L#en#ozrH}7VA;+&xa8=$GnYyvL*f~I z*h|NboS)S>eEr}h#$VsW$mvVwV>1r^dFT0=4EOS-mtYzVs|Rp>76zmOoI3?Ulan*w z<0B(bbV}0O(Xn6H*Q*rV=NPh z1c-x9n&rcE2*Pt9Wq|l)K2pa5C!rl54DpCQ0xg#!58Rg!@C$fsA8`CH=ND1_IDez^ zM*%ruEF3-({vC)F>`Q?#OK)UiHxgPLV?8%_cUyXTc-9k0w+JUd7XfHcH;?bz7w_$b zG?%e4$IadJ_r^tWKN%LX_<~{O)`OQA1Q5=9qU*7np4=vI-ZAhY4CZItKQ}7r03H@Z z`r1NNdrm9j*(bi$anmWfMc$HgRwMV+tmQ90yZtZ1>a(o98Y)C1s*IL$NGXFE$FmVC zF2i!!h#ap-sah@mDd)hJuYctqiQ;puU=EZ(O;XzC?$)c80S^FV8G`lTZ?68v9=13w zAp*h!O_|!^(T0t+Xu}Kq&g$~*&eF!pz12*aC}FaKs?f+wsa&y$a;jF9Z7@w=o~yHs z8dpfv=(K_~*%aY`G62cP{_7kM`hj2A2~O zQ~nqGtQ!^%ZdtH)S?TyaL%&v(V{J-}`(4 zwnX&i-9Oy8Y+=XXoHeq+J(pg(^opxyzDT{#E{DE%`oVWNY$6ZkxgY=*SDKj^TLk`X z{qzr;M-OdvqzC{e9Za1v34R2@~6TLtqSdY3Nltg`;Nl|%(oR13NTdX~wRS_w{0 zG3i>jhsW7PyYsJnb`Q|yBP}IW$2lshAY+6&so*H0B?E+=n}*ht4Wbw&WO8jN0M(JQ z1Z~9%_Jmb<7jgLMB1M`AT*O0a6@b#L0HPTHJ~KL9GTetj5C_xCIjzph>r@=Vx^Kly%^ z$bfjdKIRh&DXEH+8wzS$u@zK>nu-D*7D-)5rdK)L!o1ZTlRgnGW}P}Mlz6HrwavP$ z=+>Jdh?UW06$VD8DvOd~9%{GM6Id{>;{o@qU72G$qwE&RlZT|oz6D}&X#m-UfUXN0 zRV-|Z=t-ASX|aYt093(*a0_@X3&}|gz;HThGH2@0hIN)d&K+qaXpN0dhE)=qq zE@XSNE-@b2r^!}UfJCvTdRMHTNMA z_yr0F^b}-Ze{dM1Jf=T15hRFw{yz0Uq(Bfkl$^HSp5YpR}4o2Km@Ey$(*4?W_>xOo00J~ ze&Vru4(dvyM5gXHN##ugiu%mXYBmxDj1!=>To}G)(rA)eY!Nj?U^#~`s+3u5$Q!lN z9{UvNl4)h3tSTIGmD+ho%E2k= z42u^{F)~oF1qox`4*`_0HEhmU;~rXPWR1cZOT=j^>>eshy927dotn|{8WXj%g0dxG;Zg*giPdqtPb6}0K4Se1z2K|0Nf zl8L-XX}QN5wEywV-yghM;ZZAHz;=z9b$B(`)ZK?q++?LwsU1fQ4!_^JiPyC!Iv1|j z40H^SLbCbCkG~V)%U=y|8JV--(9KE+pGkUCoxN$g*gIQ#Q0jZq%Tn7$X3uk8fXCgkfK8I?E|BY$5Xb|k&Hbn}3C zjL_X6kHE$&E<8LsVMl9W!52U>5~R2)yU!H*fS!P@aL}+ z|0mGf>zYQrJ$YA1otB3;YJK?=Hf`B!F6D5S#jmhXL5MZM{iN{NS`}T;qLaB40T9@U zNK0WNWT0Yh6)B*r z@83_t0l~d{*IKO}E4X>XfBB zI@T;e-`w!zBO<)(e#aUU@drpk6KifFHtZQ%0M&-SCu9+BK^_(AM$*AMl!JF{7`(%{ z=n5NnXt%reJOoO0+dD2BJIC08rH?MQRZl-cG&E?0rl9@8i@>*fs<`?qZFw5};ZNY9 zAb^IvfD9o3nPAW@^zwi;FCWD(1BCRyTLNh)*eQbRBLOklK6v^(cuO8#zi=+R$Crx$ zKz;QI21qZ2x4CeDergJ9pTq-S0etg;gDJlK^WI7q|1Jm(!C&Wwh5I*v65%C+CAVJt z&b_*PePqz1h$k!JBON)1QvtNE6^m(MPHF#mMOO-3M`O#8bmI+kurHg zzF1Pp$}_&0uB;0|oU^Ro9D00n7P#Ts%mERO%U*W70`-a~rXYj{TFKdJJr5ML^^k=u zXH-kd>JcmJXTpm4@@8e}yV^!y{oCai!YxP-$9(zEIc!(_uG9wZG))Ra3CfwsgjzCF zA}O6{XX5 z+0*brUFYOA9R1)K${bDCxf-8XDoTJ$bmU;{R<9jLohyHZh=FdQXz$qm@O1ti#=`e< z{*y`YN#)pl9Dng7H}m9twuoVKrB|G&mfkrg{9=%P{@8);`q5)c#)vuN%0f@{VcYKGK?Z#Dd-aEE62LY{`t%#1iykkv5g-9+*iA zv0x9iNDEfNl9{9hIqpR&?Wt9Il9{C2N-eY!J)eH;o;_iP?CDP~TVhd4?b0Ok`;H9V z?w-$net#TIVo5XaT=YE8``q5=veNuC=taA#AQlD+#c&|lXYN?~(ifg9hw(XDDu$On zlfN8ze-MnnZ~%3IIruisLAaD{>6nwbS|ug(rbTBMPBr5%kD)DHzwrKhv7jv#Fg7bl z5rf!XL3xI_hf@qBs3Mc+y;2K%5!<$V zd*>^|E8MV5u?CHCFvHT=PKdS7Y-7Vx)r4yx=OE%i&FSpPcvRJsN$ILD;Os4Mw?$tw z!tTmrY5!zGDVMTZq(aR*<~^F>sCEW;qoPFJ=|tAU=jV(_X3#4d@=P^M%@k)if5syf z)#;AuFMJGzVdXK-_vJ!@PCgl$&2>ayLqqyNDBd?28ShO?@lar<&&W9vGjqB+@rCJ$ zp$7AW12=X%49}M8ySel~D4$Q}PI!x02}1M0)4ZjTSAV z0`pV_2_CU=QSccpdfpXO|Xokdh(PTptkM`rskM$7C_RYT^cQrU4WrLB2RxGxN@e|Lj zx;k~@h85?k+V57axaL~#s#f1D|NKKMtS;9p^Vi=hYuA1VmDA;V%Hgq!Q@<#=t7iN9 zIxL&IUbZ-Qn4K2cq0OjTv2yi#D~{~BdgBX$XMg(JpZ@E=UTgVxrWhM5Ckm2Y7}Y%U z*#&7XTTZ5*yB?m4O&2RsA>)mffe72}(%&9vYeOt_djPzPyosBtMxwxH!2G$ucATuHN(|Uq>hrRMjg5 z*>_T&oSw)!oVUgw9zFH3aG>C$ zeZfF1ePffWmPLnbmK8{uLzcGNwGsX&Ld?#ZZdV(+8xo|^A?n{nqh}NH=n%p0UbP-Y zFYp=eqtz>JXl#7TxORRO^6!y<>AGpXyHvXU6$+Iva0PIp+ZWH!uJx-|{nwornWiQ` zBL3f6_lCTBwQbcuMda@xZ>H(hdp@_?wR+`?ZEbL3JXn1dwAFL-e+pLX1>lb2HzfaJuqs)t0kfB!B z1!eEv=P^rZ&1Nby)=zMknnq;a3YT3f3WM6|wXaJf-}^$$nhCWcF@L!f@rQb`FB&s_ zA81d5OuKlzDIOp5kz-?ZpR?a49^Q^NG~t5>Y`c+5>_{$mE+pa&$>gM*eQ3F}X-dwX zSiXEpZd!WFo@i>qR~6w>jUUBiEuq!V$LU?fY*Ft0`+8&zZ}Q6 zgbyc*xny>>vt@8voLDucb`-+P=wfb8O3cnsGd@;zuABYA z4AC5O+I$&i?E(I+}xw}yK|AGzz^FOGhkJbv~MmaUC#t&y~{-?E`O zl2;BiYgI>x_9FNblqxWo3`H5?fCmY z?0Pr3V%2i{tC7pwRBiF#CdKZ(+vff2DZ9+ET$2)SVh-qeW&y`4%csD+Mhv&cRZd6&85CbKK$eeRSl+rmHR*0e zie6iLSM!#;4?TGNS;b7HK^8z`pHTN@ghhkU$ySdSFyx`BKL}6y*q4YiF-;wcRC*VEvS!aR-oeehx`QPsIajF)egutB=4Ot+1Lom zU)Q!iVsqJC6l%KEN+LuFMqW4Ty-cHaKwgQUPQrVqk!!~7SIq+GCvcVPBW8XbhAC=o z%(W|&xwGesdMCyK6*Z=QhV^`rs{fgNU)u=RkdGPkR%w-`d00@4VCe$8Hb82HLXiRzStRdw1)eoqO8x&`mqdep)tkaYQm- zd(+MXwx*p!+iq=PkvQ{7NE1tIE5B`LkFA&3ccPv0u}`Bep*b%?Gn4aU(LZ^K*O$o% z$xLQmOFZph9*p&_OI20m_hmahRBXaI(tWnO;A}}x&P^*BmognIR~+M1y80(w7OAlr zVKy)4-t41dqd&>#8DC(=kuOI(BkMi8W}=^*63C5X{0IJHC0_y?cM< z{`glf6^oCb_2b{`m)=-+N%{;`e4ckAD8E8+P^H zeeZ5>{`8Mt|N5DWOkmzk-S~??Z$I(zd!IP%`hno8US6kFQ_u_lU9L$zVr^*fGbm81GRWtS6m*2^BC zFY%1)z6oaptJ6WglJgiY87Y`3Ut7r~#>rSB7e+UM!Da8H-bdpmtMEog|wM>T`kEk{5tiW6|0b5ju5O% zE)DNDt-l=w!CkbOb%%Hdm+OvIh*H32Sw)+@G**51+WJ4;pi$sq>s|EqtL?WpG`n3t zG0W{OfBXf+=lVK4rxi`d*y%HY!((y34LDGeNA zEVd|(xQm^$D7DlFpL%{ipf5-eZKdE~j88@0F!Y$F~_gO zggb@)G*iRFLtn*16YHbtPurIIGq=htIM}v2LiQuqvX$StB{;MZi5YDDvYpJTUQv%OvU;O95q5Zcsq(+7{Y-LiZ+ z!lcp5!(wayyQf|m?lzg-Y#)_aoVay5Iz!Lp7Gk|6{|rL{T5zjB>m76#(1O*GP4kI$ z$jqlI2%;WJX45+~M~6%?b4&>nDcidk%CTj(GGC(A1$T0m9j_+&(bOPR1(Ycn=krN< zR7R3MGrQxC92Z$kI;#3)M43+;asPnqINx1Z|Ip~9h_1jI%SMBVxDX4E%6(d5R)-Hi zG|~}HhvU>@t}}opMJ2ZAp7#}~8E=+B)uv))!p|w$J{O&x$t>pFDTF-uQ$nyP(EiD2 ze1g|gi)vQqs(xd1VRF7$sdhSo;dFJ(nH+)laDF@jk#leP)!cWWI*)}bRaMpXYNNY4 zzk<32oXq{f8ye2Nc~utc>#aAypIBEvu(Gptr}dsgckgfQY}tlX7PajM=UET+J{{FWRUhd|G?Hdlli_q-ezUwG) z_uK1-N!!oeb|8G|W#S~?F~_D;vA#v$m^(Wdrhbr)WO@sZMKOTXmXLEYf^Zv`SMxB@ zOIzl1`QUh9Fcuh}$+4*SlO5dJRqQPaLL~L$KaFH!$@C4AiE4r!TjYF`2`S~`h(4~) zse^N6u7eKE1{ZR#b^ANUXc3uDBJ!Ebpki(o9a<-1**uEAc?T6=d3Mk_d&wOa;L#Yox-SU7>DxjUTyXbIwB%ixb5QZEgElkK}O4t6YyIRh@_~P;7cT*->#%9ybkw`f-rsXOXti+z~nDfRuyuk=p z%!L*?qd1%BBthrMz16|~xy2G6j!_d_MO~!l)@%u_q4+`9+FLq()ZWBos<1dOqM#(} zrjuMWQ0C_Da?us4a%<+t-jI|RCIvKKiR42+p)Xx%bQEV$-q7epm&^pp5KA+Zu8w5~ zW0cq18}Jn5vOyBMEcYqF5}o7J`GT$#9a^zk%~6gqF@tFN5o(62E-cP3&MX#;!On;y z;Pm>F4&sI*Y&_(xI?4P(rBaFbI~J-7#zM6>C33NX8-0w5sj^oeK?x@BtEJ4@sTT%B zLc0FE@*n?@?LRFYT+?vR@jHKi^yv7+7n@x|T=G=MlcKw^(KYz4)AjrEhkw!VQN)g{ zx^$JOPWO7pbAKdkn`MD+IFft0I?CT`Ywl^Z-A3Frw6-G~zfwm&xs%Kj^5*hO#UCR>wzt;JqzT+`vKX5tLmAWc-qI!0o&pA1efh$ZH%Uv1iW!!TJc}P0h zc!`RI$0N=F7wL!vt9`L?QprrtMZ@LMSuK#u3j*?uFj@1{1)373X9vq`8nk#htcp3cfVSx`gnN^!K%OC~$~ zWWnVq`}mG{xQH5Ac~&2%C-flF(@}K;8`3-tV-W>>kuxwbOd2+ZPoMTTKeWu%xamN@ z#bRme-y3P{-+f!p2KRP;uha3?VfVqEyW8qI`#-s{t-pCIar0~K_CxHpO&kA?J+;?n zUpKp-AaUk9d;05a_y79DckAu8;;~;&n?)|-4pgwOtE>Ou-~J0?@0QmO?ht*Eh&~?FM#@G^ByQiLW6`5`b9dU8T&+}etBBA{y0DCg;zpcbjrU^E+X4eH)O^mH7YP0Tuy-e@MO z&GAt!k_@SukL&9c=5F}YM4;{jJepVD+%YoT}Jq=~FgaPb&c5g>t(Txr6 zwv9a-X&!Y3PJht)OZA6Wul+D_sBI{@dF?l^y!_8U5KwukOMqk3|_Z9ihTwrcwUMgi2DJw{k2`XMuGSh|mqDU>qI>*M`CEh2L7H8Gj zxVA8x?(|8M43(ITIl1YI5h%sRvQB9s?n*>@2LrsbFYXVQ0;Tkm(Qx1RLdtn|B3qdG zpHyZp$UgKYs0_oXAyk9X!+Lxc-T(1 zV+kMYVeZx@y@a(Rh$zhT2dH#>Q1r{JIl%UPIt!^U9r~~C=SY0ppXmmzf;s~|BsP1hnF6a&~DSAup*$y_~4*L9NSRE?7 z;XaP)j3?YXK9(!dRKY=Ele&EX?)Ru_o_ zl9$XoX$+(2qH~_~51gDD=FR7K6T)Iy(0cefGL-Cg=SI6o>SXL*QMVJhFz>&_ZM6FZ zLRQ*{gJs!P(hk1{{=@oi3~sWC?ctz>Y&@`Y`!;?Lf)HAJc=F!MWHWRuHQhTn?sG`4 zg3p}1Cpu%KBOB-#94Tc3v}Qn!o=>W&K{rQxGoFQ1EM_cLb1@cn6INR$1EZPJVij@b z6VglsDO~DeNL?s67lRWWo#{|)BIxvnWZ6J@0w+1dZ5ilX_(eQEqQ+gZTx^{0K-p*iIu5mTm~*1M&bZzN>S4|%#OfB@=)XGMt7}p_LM=R#s z-RGx>hjEXW$Oz+>Ny6PZHkgRVDusAHTOfh4XmE5qRS2g>2ct#TB&Ua62%0P}DiueD zVJC&W5y~$V6olvWhKdor91ag6$#Ei~d5rPCXl$Y!@eO+V=85b^VYOhPvl=QR3UF?4 zW>DbE=`xyc=6MHd>O|(J^YOr#GM$&h^W%cJ;9ulRk-sRACwjkyrNQ?1-S%hEE3xM! zaZ`NV!Ou7k#@ED==6Q^raMZG5Hb)#J8mHrKO#CmaA92_m4!`wK2Yu+V{y*5l!EpG{ zzIZquK6GgJv17*$j2t~F?Eipk^x^%;E;+Kf$vPUbgng~vvG8Mm+}t>@**(zO5b7WM za{t))gJ;L_*X_QGSe6R08^)inRUH+~Z*xXgJsqJ`GK&^sH02?BVSXNK5?&~5_*3=h zE~gtg=mjr!Q(hfAJf;aeS22i2Bl3gxl)MWI-~=ezu6z7c!M(WXb~({AVBS@w@Ewd| zzDg0@6LKz?A8VUgfdm0ri{=+x%jIw-;%EkFxP;mH#RU}XHb}*RqohFafK}&$;ezKK z-^qNcI98f>T`m2HW>b?Alp5z8E<3jXrsD)Rb?mbo_S_Z16V~kxRtS=WH)UO?sPxD?Y5paOX~(RBc|K@VA+PenC<4?+S^X^ zy=NO~jsF{O+PPDN+i-KEeQ0z0?W^pzy8A8G7F%QI0d^ZpkRa2}e1C1L9bNy9uWkLx z!);r3`g(o6z3($94fBEb&y7e6OxjHr6D83 zT(|@a?2%|b-#I%r=kGH@m3{9XJGU

Ige|HaS|9HHbcMHMb~Na;08=JUln<;?>G? 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�F5?PdQXiT1ak!fEtkDjp4+5^56f@e3y6p<9Mb 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 literal 0 HcmV?d00001 diff --git a/test/gltf.test.mjs b/test/gltf.test.mjs index d6ddf3c..c3d8a8c 100644 --- a/test/gltf.test.mjs +++ b/test/gltf.test.mjs @@ -1,52 +1,67 @@ -import { expect } from "chai" +import { expect } from "chai"; describe("glTF", () => { - it("should render separate correctly using pixi *.*.*", async () => { let render = (renderer, resources) => { - let model = PIXI3D.Model.from(resources["assets/teapot/teapot-separate.gltf"].gltf) - model.y = -0.8 - model.meshes.forEach(mesh => { - mesh.material.unlit = true - }) - renderer.render(model) - } + let model = PIXI3D.Model.from( + resources["assets/teapot/teapot-separate.gltf"].gltf + ); + model.y = -0.8; + model.meshes.forEach((mesh) => { + mesh.material.unlit = true; + }); + renderer.render(model); + }; await expect(render).to.match("snapshots/wdhdo.png", { - resources: [ - "assets/teapot/teapot-separate.gltf" - ] - }) - }) + resources: ["assets/teapot/teapot-separate.gltf"], + }); + }); it("should render binary correctly using pixi *.*.*", async () => { let render = async (renderer, resources) => { - let model = PIXI3D.Model.from(resources["assets/teapot/teapot-binary.glb"].gltf) - model.y = -0.8 - model.meshes.forEach(mesh => { - mesh.material.unlit = true - }) - renderer.render(model) - } + let model = PIXI3D.Model.from( + resources["assets/teapot/teapot-binary.glb"].gltf + ); + model.y = -0.8; + model.meshes.forEach((mesh) => { + mesh.material.unlit = true; + }); + renderer.render(model); + }; await expect(render).to.match("snapshots/wdhdo.png", { - resources: [ - "assets/teapot/teapot-binary.glb" - ] - }) - }) + resources: ["assets/teapot/teapot-binary.glb"], + }); + }); it("should render embedded correctly using pixi *.*.*", async () => { let render = (renderer, resources) => { - let model = PIXI3D.Model.from(resources["assets/teapot/teapot-embedded.gltf"].gltf) - model.y = -0.8 - model.meshes.forEach(mesh => { - mesh.material.unlit = true - }) - renderer.render(model) - } + let model = PIXI3D.Model.from( + resources["assets/teapot/teapot-embedded.gltf"].gltf + ); + model.y = -0.8; + model.meshes.forEach((mesh) => { + mesh.material.unlit = true; + }); + renderer.render(model); + }; + await expect(render).to.match("snapshots/wdhdo.png", { + resources: ["assets/teapot/teapot-embedded.gltf"], + }); + }); + + it("should render meshopt correctly using pixi *.*.*", async () => { + let render = (renderer, resources) => { + let model = PIXI3D.Model.from( + resources["assets/teapot/teapot-binary-meshopt.glb"].gltf + ); + model.y = -0.8; + model.meshes.forEach((mesh) => { + mesh.material.unlit = true; + }); + renderer.render(model); + }; await expect(render).to.match("snapshots/wdhdo.png", { - resources: [ - "assets/teapot/teapot-embedded.gltf" - ] - }) - }) -}) \ No newline at end of file + resources: ["assets/teapot/teapot-binary-meshopt.glb"], + }); + }); +}); From 9427c121f26d75cdde64f284d5927bb492eb3d1c Mon Sep 17 00:00:00 2001 From: MatsErdkamp Date: Mon, 27 May 2024 19:52:15 +0200 Subject: [PATCH 4/8] fix meshopt offset mistake --- serve/assets/vase/.DS_Store | Bin 6148 -> 0 bytes serve/src/index.js | 73 ++++++++++++++++++++---------------- src/gltf/gltf-parser.ts | 4 +- 3 files changed, 43 insertions(+), 34 deletions(-) delete mode 100644 serve/assets/vase/.DS_Store diff --git a/serve/assets/vase/.DS_Store b/serve/assets/vase/.DS_Store deleted file mode 100644 index 937a4837a24e020d2b293e314dd5078e6a13aa3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKyH3ME5S)b+K`BT{d0&9UADp64Q1b)e1ZYlj5lHQh&xY9tQY^FHE?ibh9*uWI#uF|Ax>w!L|qLW9GwoamHD1nS>lEw zwmRd*$|2P;$5bE{7%On;)3x^hd-}}%e@x0*Dv%2NDFtM)ecW#NOVL{=Kc~I6(4Xi( n##$@qFggTqqOJJmq^{^Q>uTWOXmrMnPRxUV>XMcU{DuPGL$V_# diff --git a/serve/src/index.js b/serve/src/index.js index d272dcf..972e2d0 100644 --- a/serve/src/index.js +++ b/serve/src/index.js @@ -1,46 +1,53 @@ let app = new PIXI.Application({ - backgroundColor: 0xdddddd, resizeTo: window, antialias: true -}) -document.body.appendChild(app.view) -globalThis.__PIXI_APP__ = app + backgroundColor: 0xdddddd, + resizeTo: window, + antialias: true, +}); +document.body.appendChild(app.view); +globalThis.__PIXI_APP__ = app; -let control = new PIXI3D.CameraOrbitControl(app.view) -control.enableDamping = true +let control = new PIXI3D.CameraOrbitControl(app.view); +control.enableDamping = true; -app.loader.add("assets/chromatic/diffuse.cubemap") -app.loader.add("assets/chromatic/specular.cubemap") -app.loader.add("assets/teapot/teapot.gltf") +app.loader.add("assets/chromatic/diffuse.cubemap"); +app.loader.add("assets/chromatic/specular.cubemap"); +app.loader.add("assets/teapot/teapot-meshopt.glb"); app.loader.load((_, resources) => { - PIXI3D.LightingEnvironment.main.imageBasedLighting = new PIXI3D.ImageBasedLighting( - resources["assets/chromatic/diffuse.cubemap"].cubemap, - resources["assets/chromatic/specular.cubemap"].cubemap - ) + PIXI3D.LightingEnvironment.main.imageBasedLighting = + new PIXI3D.ImageBasedLighting( + resources["assets/chromatic/diffuse.cubemap"].cubemap, + resources["assets/chromatic/specular.cubemap"].cubemap + ); let model = app.stage.addChild( - PIXI3D.Model.from(resources["assets/teapot/teapot.gltf"].gltf)) - model.y = -0.8 - model.meshes.forEach(mesh => { - mesh.material.exposure = 1.3 - }) + PIXI3D.Model.from(resources["assets/teapot/teapot-meshopt.glb"].gltf) + ); + model.y = -0.8; + model.meshes.forEach((mesh) => { + mesh.material.exposure = 1.3; + }); - let ground = app.stage.addChild(PIXI3D.Mesh3D.createPlane()) - ground.y = -0.8 - ground.scale.set(10, 1, 10) + let ground = app.stage.addChild(PIXI3D.Mesh3D.createPlane()); + ground.y = -0.8; + ground.scale.set(10, 1, 10); let directionalLight = Object.assign(new PIXI3D.Light(), { intensity: 1, - type: "directional" - }) - directionalLight.rotationQuaternion.setEulerAngles(25, 120, 0) - PIXI3D.LightingEnvironment.main.lights.push(directionalLight) + type: "directional", + }); + directionalLight.rotationQuaternion.setEulerAngles(25, 120, 0); + PIXI3D.LightingEnvironment.main.lights.push(directionalLight); let shadowCastingLight = new PIXI3D.ShadowCastingLight( - app.renderer, directionalLight, { shadowTextureSize: 1024, quality: PIXI3D.ShadowQuality.medium }) - shadowCastingLight.softness = 1 - shadowCastingLight.shadowArea = 15 - - let pipeline = app.renderer.plugins.pipeline - pipeline.enableShadows(ground, shadowCastingLight) - pipeline.enableShadows(model, shadowCastingLight) -}) \ No newline at end of file + app.renderer, + directionalLight, + { shadowTextureSize: 1024, quality: PIXI3D.ShadowQuality.medium } + ); + shadowCastingLight.softness = 1; + shadowCastingLight.shadowArea = 15; + + let pipeline = app.renderer.plugins.pipeline; + pipeline.enableShadows(ground, shadowCastingLight); + pipeline.enableShadows(model, shadowCastingLight); +}); diff --git a/src/gltf/gltf-parser.ts b/src/gltf/gltf-parser.ts index 78bd5a0..044ad42 100644 --- a/src/gltf/gltf-parser.ts +++ b/src/gltf/gltf-parser.ts @@ -79,9 +79,11 @@ export class glTFParser { } let bufferView = this._descriptor.bufferViews[accessor.bufferView || 0]; let offset = accessor.byteOffset || 0; + if (bufferView.byteOffset !== undefined) { offset += bufferView.byteOffset; } + let size = accessor.count * componentCount[accessor.type]; if (bufferView.byteStride !== undefined && bufferView.byteStride !== 0) { size = @@ -96,7 +98,7 @@ export class glTFParser { if (bufferView.extensions?.EXT_meshopt_compression != undefined) { const meshoptExtension = bufferView.extensions.EXT_meshopt_compression; buffer = this.decodeMeshoptBuffer(meshoptExtension); - offset = 0; + offset = accessor.byteOffset || 0; } return glTFAttribute.from( From 353b97144d220b4b92d38351b9a642471f6143da Mon Sep 17 00:00:00 2001 From: MatsErdkamp Date: Mon, 27 May 2024 20:30:12 +0200 Subject: [PATCH 5/8] added decompressed buffer caching --- src/gltf/gltf-parser.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/gltf/gltf-parser.ts b/src/gltf/gltf-parser.ts index 044ad42..ab9925c 100644 --- a/src/gltf/gltf-parser.ts +++ b/src/gltf/gltf-parser.ts @@ -26,6 +26,7 @@ export class glTFParser { private _asset: glTFAsset; private _materialFactory: MaterialFactory; private _descriptor: any; + private _decompressedBuffers: Map; /** * Creates a new parser using the specified asset. @@ -36,6 +37,7 @@ export class glTFParser { this._asset = asset; this._materialFactory = materialFactory || new StandardMaterialFactory(); this._descriptor = this._asset.descriptor; + this._decompressedBuffers = new Map(); if (asset.textures.length === 0) { for (let i = 0; i < this._descriptor.textures?.length; i++) { asset.textures.push(this.parseTexture(i)); @@ -97,7 +99,7 @@ export class glTFParser { if (bufferView.extensions?.EXT_meshopt_compression != undefined) { const meshoptExtension = bufferView.extensions.EXT_meshopt_compression; - buffer = this.decodeMeshoptBuffer(meshoptExtension); + buffer = this.decodeMeshoptBuffer(meshoptExtension, accessor.bufferView); offset = accessor.byteOffset || 0; } @@ -117,15 +119,23 @@ export class glTFParser { /** * Uses meshoptimizer to decode a 'EXT_meshopt_compression' buffer. * @param meshoptExtension The extension.EXT_meshopt_compression Object. + * @param bufferViewIndex The accessor's bufferViewIndex to use as a cache key. */ - decodeMeshoptBuffer(meshoptExtension: any) { + + decodeMeshoptBuffer( + meshoptExtension: any, + bufferViewIndex: number + ): ArrayBuffer { + if (this._decompressedBuffers.has(bufferViewIndex)) { + return this._decompressedBuffers.get(bufferViewIndex) as ArrayBuffer; + } + let meshoptBuffer = this._asset.buffers[meshoptExtension.buffer]; const byteOffset = meshoptExtension.byteOffset || 0; const byteLength = meshoptExtension.byteLength || meshoptBuffer.byteLength; const count = meshoptExtension.count; const stride = meshoptExtension.byteStride; - // Decode the buffer using MeshoptDecoder const resultBuffer = new Uint8Array(count * stride); MeshoptDecoder.decodeGltfBuffer( @@ -137,7 +147,10 @@ export class glTFParser { meshoptExtension.filter ); - return resultBuffer.buffer; + const resultArrayBuffer = resultBuffer.buffer; + this._decompressedBuffers.set(bufferViewIndex, resultArrayBuffer); + + return resultArrayBuffer; } /** From 0f347a3715a2270dcbe3089de039925aa5de3bdb Mon Sep 17 00:00:00 2001 From: MatsErdkamp <62242064+MatsErdkamp@users.noreply.github.com> Date: Tue, 28 May 2024 09:50:42 +0200 Subject: [PATCH 6/8] Accidentally left the meshopt debug mesh in index.js --- serve/src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serve/src/index.js b/serve/src/index.js index 972e2d0..7f57629 100644 --- a/serve/src/index.js +++ b/serve/src/index.js @@ -11,7 +11,7 @@ control.enableDamping = true; app.loader.add("assets/chromatic/diffuse.cubemap"); app.loader.add("assets/chromatic/specular.cubemap"); -app.loader.add("assets/teapot/teapot-meshopt.glb"); +app.loader.add("assets/teapot/teapot.gltf"); app.loader.load((_, resources) => { PIXI3D.LightingEnvironment.main.imageBasedLighting = @@ -21,7 +21,7 @@ app.loader.load((_, resources) => { ); let model = app.stage.addChild( - PIXI3D.Model.from(resources["assets/teapot/teapot-meshopt.glb"].gltf) + PIXI3D.Model.from(resources["assets/teapot/teapot.gltf"].gltf) ); model.y = -0.8; model.meshes.forEach((mesh) => { From 4cf2aa735aa4fc5afd4544bd496152987040cc89 Mon Sep 17 00:00:00 2001 From: Mats Date: Thu, 30 May 2024 21:12:15 +0200 Subject: [PATCH 7/8] initial structure meshoptDecoder as manual addon --- src/gltf/gltf-parser.ts | 18 +++++++++++++----- src/model.ts | 4 ++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/gltf/gltf-parser.ts b/src/gltf/gltf-parser.ts index ab9925c..54586e1 100644 --- a/src/gltf/gltf-parser.ts +++ b/src/gltf/gltf-parser.ts @@ -17,7 +17,6 @@ import { Skin } from "../skinning/skin"; import { Joint } from "../skinning/joint"; import { StandardMaterialFactory } from "../material/standard/standard-material-factory"; import { glTFChannelFactory } from "./animation/gltf-channel-factory"; -import { MeshoptDecoder } from "meshoptimizer"; /** * Parses glTF assets and creates models and meshes. @@ -27,16 +26,19 @@ export class glTFParser { private _materialFactory: MaterialFactory; private _descriptor: any; private _decompressedBuffers: Map; + private _meshoptDecoder: any; /** * Creates a new parser using the specified asset. * @param asset The asset to parse. * @param materialFactory The material factory to use. + * @param meshoptDecoder optionally provide meshoptDecoder to parse meshopt meshes. */ - constructor(asset: glTFAsset, materialFactory?: MaterialFactory) { + constructor(asset: glTFAsset, materialFactory?: MaterialFactory, meshoptDecoder?: any) { this._asset = asset; this._materialFactory = materialFactory || new StandardMaterialFactory(); this._descriptor = this._asset.descriptor; + this._meshoptDecoder = meshoptDecoder; this._decompressedBuffers = new Map(); if (asset.textures.length === 0) { for (let i = 0; i < this._descriptor.textures?.length; i++) { @@ -50,8 +52,8 @@ export class glTFParser { * @param asset The asset to create the model from. * @param materialFactory The material factory to use. */ - static createModel(asset: glTFAsset, materialFactory?: MaterialFactory) { - return new glTFParser(asset, materialFactory).parseModel(); + static createModel(asset: glTFAsset, materialFactory?: MaterialFactory, meshoptDecoder ?: any) { + return new glTFParser(asset, materialFactory, meshoptDecoder).parseModel(); } /** @@ -98,6 +100,12 @@ export class glTFParser { let buffer = this._asset.buffers[bufferView.buffer]; if (bufferView.extensions?.EXT_meshopt_compression != undefined) { + + if (this._meshoptDecoder == undefined) { + console.error('Buffer uses EXT_meshopt_compression but meshoptDecoder is not provided.'); + return; + } + const meshoptExtension = bufferView.extensions.EXT_meshopt_compression; buffer = this.decodeMeshoptBuffer(meshoptExtension, accessor.bufferView); offset = accessor.byteOffset || 0; @@ -138,7 +146,7 @@ export class glTFParser { const resultBuffer = new Uint8Array(count * stride); - MeshoptDecoder.decodeGltfBuffer( + this._meshoptDecoder.decodeGltfBuffer( resultBuffer, count, stride, diff --git a/src/model.ts b/src/model.ts index 628b711..2d22213 100644 --- a/src/model.ts +++ b/src/model.ts @@ -25,8 +25,8 @@ export class Model extends Container3D { * @param source The source to create the model from. * @param materialFactory The factory to use for creating materials. */ - static from(source: glTFAsset, materialFactory?: MaterialFactory) { - return glTFParser.createModel(source, materialFactory) + static from(source: glTFAsset, materialFactory?: MaterialFactory, meshoptDecoder ?: any) { + return glTFParser.createModel(source, materialFactory, meshoptDecoder) } /** From 1c7107abd31ed67f9f7f73ae4c908a2a8b3a27a3 Mon Sep 17 00:00:00 2001 From: MatsErdkamp Date: Thu, 30 May 2024 21:35:34 +0200 Subject: [PATCH 8/8] changed meshopt test --- test/gltf.test.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/gltf.test.mjs b/test/gltf.test.mjs index c3d8a8c..33c7ca4 100644 --- a/test/gltf.test.mjs +++ b/test/gltf.test.mjs @@ -1,4 +1,5 @@ import { expect } from "chai"; +import { MeshoptDecoder } from "meshoptimizer"; describe("glTF", () => { it("should render separate correctly using pixi *.*.*", async () => { @@ -52,7 +53,9 @@ describe("glTF", () => { it("should render meshopt correctly using pixi *.*.*", async () => { let render = (renderer, resources) => { let model = PIXI3D.Model.from( - resources["assets/teapot/teapot-binary-meshopt.glb"].gltf + resources["assets/teapot/teapot-binary-meshopt.glb"].gltf, + undefined, + MeshoptDecoder ); model.y = -0.8; model.meshes.forEach((mesh) => {