From 5bbc4fb2a5e0f357e0c880a722617d4f2b655203 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Mon, 17 Sep 2012 13:39:38 +0100 Subject: [PATCH] missing file for suport of mesh upload cost --- .../Linden/Caps/BunchOfCaps/MeshCost.cs | 596 ++++++++++++++++++ 1 file changed, 596 insertions(+) create mode 100644 OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs new file mode 100644 index 0000000000..8adacab72a --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs @@ -0,0 +1,596 @@ +// Proprietary code of Avination Virtual Limited +// (c) 2012 Melanie Thielker, Leal Duarte +// + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Capabilities; + +using ComponentAce.Compression.Libs.zlib; + +using OSDArray = OpenMetaverse.StructuredData.OSDArray; +using OSDMap = OpenMetaverse.StructuredData.OSDMap; + +namespace OpenSim.Region.ClientStack.Linden +{ + public class ModelCost + { + float ModelMinCost = 5.0f; // try to favor small meshs versus sculpts + + // scale prices relative to basic cost + const float ModelCostScale = 1.0f; + + const float primCreationCost = 0.01f; // 256 prims cost extra 2.56 + + // weigthed size to money convertion + const float bytecost = 1e-4f; + + // for mesh upload fees based on compressed data sizes + // not using streaming physics and server costs as SL apparently does ?? + + const float medSizeWth = 1f; // 2x + const float lowSizeWth = 1.5f; // 2.5x + const float lowestSizeWth = 2f; // 3x + // favor potencial optimized meshs versus automatic decomposition + const float physMeshSizeWth = 6f; // counts 7x + const float physHullSizeWth = 8f; // counts 9x + + // price compression to promote complex meshs + const float feeCompressionBase = 50.0f; // transition from linear to log cost + const float feeCompressionScale = 250.0f; // 10000 scales to 1000 + + // stream cost size factors + const float highLodFactor = 17.36f; + const float midLodFactor = 277.78f; + const float lowLodFactor = 1111.11f; + + const int bytesPerCoord = 6; // 3 coords, 2 bytes per each + + private class ameshCostParam + { + public int highLODSize; + public int medLODSize; + public int lowLODSize; + public int lowestLODSize; + public float costFee; + public float physicsCost; + } + + public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost, LLSDAssetUploadResponseData meshcostdata, out string error) + { + totalcost = 0; + error = string.Empty; + + if (resources == null || + resources.instance_list == null || + resources.instance_list.Array.Count == 0) + { + error = "Unable to upload mesh model. missing information."; + return false; + } + + meshcostdata.model_streaming_cost = 0.0; + meshcostdata.simulation_cost = 0.0; + meshcostdata.physics_cost = 0.0; + meshcostdata.resource_cost = 0.0; + + meshcostdata.upload_price_breakdown.mesh_instance = 0; + meshcostdata.upload_price_breakdown.mesh_physics = 0; + meshcostdata.upload_price_breakdown.mesh_streaming = 0; + meshcostdata.upload_price_breakdown.model = 0; + + int itmp; + + // textures cost + if (resources.texture_list != null && resources.texture_list.Array.Count > 0) + { + int textures_cost = resources.texture_list.Array.Count; + textures_cost *= basicCost; + + meshcostdata.upload_price_breakdown.texture = textures_cost; + totalcost += textures_cost; + } + + float meshsfee = 0; + + // meshs assets cost + + int numberMeshs = 0; + List meshsCosts = new List(); + // a model could have no mesh actually + if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0) + { + numberMeshs = resources.mesh_list.Array.Count; + + for (int i = 0; i < numberMeshs; i++) + { + ameshCostParam curCost = new ameshCostParam(); + byte[] data = (byte[])resources.mesh_list.Array[i]; + + if (!MeshCost(data, curCost, out error)) + { + return false; + } + meshsCosts.Add(curCost); + meshsfee += curCost.costFee; + } + } + + // instances (prims) cost + int numberInstances = resources.instance_list.Array.Count; + int mesh; + for (int i = 0; i < numberInstances; i++) + { + Hashtable inst = (Hashtable)resources.instance_list.Array[i]; + + // streamming cost + // assume all instances have a mesh + // but in general they can have normal prims + // but for now that seems not suported + // when they do, we will need to inspect pbs information + // and have cost funtions for all prims types + // don't check for shape type none, since + // that could be used to upload meshs with low cost + // changing later inworld + + ArrayList ascale = (ArrayList)inst["scale"]; + Vector3 scale; + double tmp; + tmp = (double)ascale[0]; + scale.X = (float)tmp; + tmp = (double)ascale[1]; + scale.Y = (float)tmp; + tmp = (double)ascale[2]; + scale.Z = (float)tmp; + + float sqdiam = scale.LengthSquared(); + + mesh = (int)inst["mesh"]; + + if(mesh >= numberMeshs) + { + error = "Unable to upload mesh model. incoerent information."; + return false; + } + + ameshCostParam curCost = meshsCosts[mesh]; + float mesh_streaming = streamingCost(curCost, sqdiam); + + meshcostdata.model_streaming_cost += mesh_streaming; + + meshcostdata.physics_cost += curCost.physicsCost; + + // unscripted and static prim server cost + meshcostdata.simulation_cost += 0.5f; + // charge for prims creation + meshsfee += primCreationCost; + } + + if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost) + meshcostdata.resource_cost = meshcostdata.model_streaming_cost; + else + meshcostdata.resource_cost = meshcostdata.physics_cost; + + if (meshsfee < ModelMinCost) + meshsfee = ModelMinCost; + + meshsfee *= ModelCostScale; + meshsfee += 0.5f; // rounding + + totalcost += (int)meshsfee; + + // breakdown prices + // don't seem to be in use so removed code for now + + return true; + } + + private bool MeshCost(byte[] data, ameshCostParam cost, out string error) + { + cost.highLODSize = 0; + cost.medLODSize = 0; + cost.lowLODSize = 0; + cost.lowestLODSize = 0; + cost.physicsCost = 0.0f; + cost.costFee = 0.0f; + + error = string.Empty; + + if (data == null || data.Length == 0) + { + error = "Unable to upload mesh model. missing information."; + return false; + } + + OSD meshOsd = null; + int start = 0; + + error = "Unable to upload mesh model. Invalid data"; + + using (MemoryStream ms = new MemoryStream(data)) + { + try + { + OSD osd = OSDParser.DeserializeLLSDBinary(ms); + if (osd is OSDMap) + meshOsd = (OSDMap)osd; + else + return false; + } + catch (Exception e) + { + return false; + } + start = (int)ms.Position; + } + + OSDMap map = (OSDMap)meshOsd; + OSDMap tmpmap; + + int highlod_size = 0; + int medlod_size = 0; + int lowlod_size = 0; + int lowestlod_size = 0; + int skin_size = 0; + + int hulls_size = 0; + int phys_nhulls; + int phys_hullsvertices = 0; + + int physmesh_size = 0; + int phys_ntriangles = 0; + + int submesh_offset = -1; + + if (map.ContainsKey("physics_convex")) + { + tmpmap = (OSDMap)map["physics_convex"]; + if (tmpmap.ContainsKey("offset")) + submesh_offset = tmpmap["offset"].AsInteger() + start; + if (tmpmap.ContainsKey("size")) + hulls_size = tmpmap["size"].AsInteger(); + } + + if (submesh_offset < 0 || hulls_size == 0) + { + error = "Unable to upload mesh model. missing physics_convex block"; + return false; + } + + if (!hulls(data, submesh_offset, hulls_size, out phys_hullsvertices, out phys_nhulls)) + { + error = "Unable to upload mesh model. bad physics_convex block"; + return false; + } + + submesh_offset = -1; + + // only look for LOD meshs sizes + + if (map.ContainsKey("high_lod")) + { + tmpmap = (OSDMap)map["high_lod"]; + // see at least if there is a offset for this one + if (tmpmap.ContainsKey("offset")) + submesh_offset = tmpmap["offset"].AsInteger() + start; + if (tmpmap.ContainsKey("size")) + highlod_size = tmpmap["size"].AsInteger(); + } + + if (submesh_offset < 0 || highlod_size <= 0) + { + error = "Unable to upload mesh model. missing high_lod"; + return false; + } + + bool haveprev = true; + + if (map.ContainsKey("medium_lod")) + { + tmpmap = (OSDMap)map["medium_lod"]; + if (tmpmap.ContainsKey("size")) + medlod_size = tmpmap["size"].AsInteger(); + else + haveprev = false; + } + + if (haveprev && map.ContainsKey("low_lod")) + { + tmpmap = (OSDMap)map["low_lod"]; + if (tmpmap.ContainsKey("size")) + lowlod_size = tmpmap["size"].AsInteger(); + else + haveprev = false; + } + + if (haveprev && map.ContainsKey("lowest_lod")) + { + tmpmap = (OSDMap)map["lowest_lod"]; + if (tmpmap.ContainsKey("size")) + lowestlod_size = tmpmap["size"].AsInteger(); + } + + if (map.ContainsKey("skin")) + { + tmpmap = (OSDMap)map["skin"]; + if (tmpmap.ContainsKey("size")) + skin_size = tmpmap["size"].AsInteger(); + } + + cost.highLODSize = highlod_size; + cost.medLODSize = medlod_size; + cost.lowLODSize = lowlod_size; + cost.lowestLODSize = lowestlod_size; + + submesh_offset = -1; + + if (map.ContainsKey("physics_mesh")) + { + tmpmap = (OSDMap)map["physics_mesh"]; + if (tmpmap.ContainsKey("offset")) + submesh_offset = tmpmap["offset"].AsInteger() + start; + if (tmpmap.ContainsKey("size")) + physmesh_size = tmpmap["size"].AsInteger(); + + if (submesh_offset >= 0 || physmesh_size > 0) + { + + if (!submesh(data, submesh_offset, physmesh_size, out phys_ntriangles)) + { + error = "Unable to upload mesh model. parsing error"; + return false; + } + } + } + + // upload is done in convex shape type so only one hull + phys_hullsvertices++; + cost.physicsCost = 0.04f * phys_hullsvertices; + + float sfee; + + sfee = data.Length; // start with total compressed data size + + // penalize lod meshs that should be more builder optimized + sfee += medSizeWth * medlod_size; + sfee += lowSizeWth * lowlod_size; + sfee += lowestSizeWth * lowlod_size; + + // physics + // favor potencial optimized meshs versus automatic decomposition + if (physmesh_size != 0) + sfee += physMeshSizeWth * (physmesh_size + hulls_size / 4); // reduce cost of mandatory convex hull + else + sfee += physHullSizeWth * hulls_size; + + // bytes to money + sfee *= bytecost; + + // fee compression + if (sfee > feeCompressionBase) + { + sfee -= feeCompressionBase; + sfee = feeCompressionScale * (float)Math.Log10((double)sfee); + sfee += feeCompressionBase; + } + + + + cost.costFee = sfee; + return true; + } + + private bool submesh(byte[] data, int offset, int size, out int ntriangles) + { + ntriangles = 0; + + OSD decodedMeshOsd = new OSD(); + byte[] meshBytes = new byte[size]; + System.Buffer.BlockCopy(data, offset, meshBytes, 0, size); + try + { + using (MemoryStream inMs = new MemoryStream(meshBytes)) + { + using (MemoryStream outMs = new MemoryStream()) + { + using (ZOutputStream zOut = new ZOutputStream(outMs)) + { + byte[] readBuffer = new byte[4096]; + int readLen = 0; + while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0) + { + zOut.Write(readBuffer, 0, readLen); + } + zOut.Flush(); + outMs.Seek(0, SeekOrigin.Begin); + + byte[] decompressedBuf = outMs.GetBuffer(); + decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf); + } + } + } + } + catch (Exception e) + { + return false; + } + + OSDArray decodedMeshOsdArray = null; + if ((!decodedMeshOsd is OSDArray)) + return false; + + byte[] dummy; + + decodedMeshOsdArray = (OSDArray)decodedMeshOsd; + foreach (OSD subMeshOsd in decodedMeshOsdArray) + { + if (subMeshOsd is OSDMap) + { + OSDMap subtmpmap = (OSDMap)subMeshOsd; + if (subtmpmap.ContainsKey("NoGeometry") && ((OSDBoolean)subtmpmap["NoGeometry"])) + continue; + + if (!subtmpmap.ContainsKey("Position")) + return false; + + if (subtmpmap.ContainsKey("TriangleList")) + { + dummy = subtmpmap["TriangleList"].AsBinary(); + ntriangles += dummy.Length / bytesPerCoord; + } + else + return false; + } + } + + return true; + } + + private bool hulls(byte[] data, int offset, int size, out int nvertices, out int nhulls) + { + nvertices = 0; + nhulls = 1; + + OSD decodedMeshOsd = new OSD(); + byte[] meshBytes = new byte[size]; + System.Buffer.BlockCopy(data, offset, meshBytes, 0, size); + try + { + using (MemoryStream inMs = new MemoryStream(meshBytes)) + { + using (MemoryStream outMs = new MemoryStream()) + { + using (ZOutputStream zOut = new ZOutputStream(outMs)) + { + byte[] readBuffer = new byte[4096]; + int readLen = 0; + while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0) + { + zOut.Write(readBuffer, 0, readLen); + } + zOut.Flush(); + outMs.Seek(0, SeekOrigin.Begin); + + byte[] decompressedBuf = outMs.GetBuffer(); + decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf); + } + } + } + } + catch (Exception e) + { + return false; + } + + OSDMap cmap = (OSDMap)decodedMeshOsd; + if (cmap == null) + return false; + + byte[] dummy; + + // must have one of this + if (cmap.ContainsKey("BoundingVerts")) + { + dummy = cmap["BoundingVerts"].AsBinary(); + nvertices = dummy.Length / bytesPerCoord; + } + else + return false; + +/* upload is done with convex shape type + if (cmap.ContainsKey("HullList")) + { + dummy = cmap["HullList"].AsBinary(); + nhulls += dummy.Length; + } + + + if (cmap.ContainsKey("Positions")) + { + dummy = cmap["Positions"].AsBinary(); + nvertices = dummy.Length / bytesPerCoord; + } + */ + + return true; + } + + private float streamingCost(ameshCostParam curCost, float sqdiam) + { + // compute efective areas + float ma = 262144f; + + float mh = sqdiam * highLodFactor; + if (mh > ma) + mh = ma; + float mm = sqdiam * midLodFactor; + if (mm > ma) + mm = ma; + + float ml = sqdiam * lowLodFactor; + if (ml > ma) + ml = ma; + + float mlst = ma; + + mlst -= ml; + ml -= mm; + mm -= mh; + + if (mlst < 1.0f) + mlst = 1.0f; + if (ml < 1.0f) + ml = 1.0f; + if (mm < 1.0f) + mm = 1.0f; + if (mh < 1.0f) + mh = 1.0f; + + ma = mlst + ml + mm + mh; + + // get LODs compressed sizes + // giving 384 bytes bonus + int lst = curCost.lowestLODSize - 384; + int l = curCost.lowLODSize - 384; + int m = curCost.medLODSize - 384; + int h = curCost.highLODSize - 384; + + // use previus higher LOD size on missing ones + if (m <= 0) + m = h; + if (l <= 0) + l = m; + if (lst <= 0) + lst = l; + + // force minumum sizes + if (lst < 16) + lst = 16; + if (l < 16) + l = 16; + if (m < 16) + m = 16; + if (h < 16) + h = 16; + + // compute cost weighted by relative effective areas + + float cost = (float)lst * mlst + (float)l * ml + (float)m * mm + (float)h * mh; + cost /= ma; + + cost *= 0.004f; // overall tunning parameter + + return cost; + } + } +}