// returns streaming cost from on mesh LODs sizes in curCost and square of prim size length 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; }
// single mesh asset cost private bool MeshCost(byte[] data, ameshCostParam cost,out bool skeleton, out bool avatarPhys, 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; skeleton = false; avatarPhys = false; if (data == null || data.Length == 0) { error = "Missing model information."; return false; } OSD meshOsd = null; int start = 0; error = "Invalid model data"; using (MemoryStream ms = new MemoryStream(data)) { try { OSD osd = OSDParser.DeserializeLLSDBinary(ms); if (osd is OSDMap) meshOsd = (OSDMap)osd; else return false; } catch { 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("skeleton")) { tmpmap = (OSDMap)map["skeleton"]; if (tmpmap.ContainsKey("offset") && tmpmap.ContainsKey("size")) { int sksize = tmpmap["size"].AsInteger(); if(sksize > 0) skeleton = true; } } 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 = "Missing physics_convex block"; return false; } if (!hulls(data, submesh_offset, hulls_size, out phys_hullsvertices, out phys_nhulls)) { error = "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 = "Missing high_lod block"; 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; tmpmap = null; if(map.ContainsKey("physics_mesh")) tmpmap = (OSDMap)map["physics_mesh"]; else if (map.ContainsKey("physics_shape")) // old naming tmpmap = (OSDMap)map["physics_shape"]; if(tmpmap != null) { 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 = "Model data 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; cost.costFee = sfee; return true; }
// calculates a mesh model costs // returns false on error, with a reason on parameter error // resources input LLSD request // basicCost input region assets upload cost // totalcost returns model total upload fee // meshcostdata returns detailed costs for viewer // avatarSkeleton if mesh includes a avatar skeleton // useAvatarCollider if we should use physics mesh for avatar public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost, LLSDAssetUploadResponseData meshcostdata, out string error, ref string warning) { totalcost = 0; error = string.Empty; bool avatarSkeleton = false; if (resources == null || resources.instance_list == null || resources.instance_list.Array.Count == 0) { error = "missing model information."; return false; } int numberInstances = resources.instance_list.Array.Count; if (ObjectLinkedPartsMax != 0 && numberInstances > ObjectLinkedPartsMax) { error = "Model would have more than " + ObjectLinkedPartsMax.ToString() + " linked prims"; 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) { float textures_cost = (float)(resources.texture_list.Array.Count * basicCost); textures_cost *= ModelTextureCostFactor; itmp = (int)(textures_cost + 0.5f); // round meshcostdata.upload_price_breakdown.texture = itmp; totalcost += itmp; } // meshs assets cost float meshsfee = 0; int numberMeshs = 0; bool haveMeshs = false; bool curskeleton; bool curAvatarPhys; List<ameshCostParam> meshsCosts = new List<ameshCostParam>(); 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 curskeleton, out curAvatarPhys, out error)) { return false; } if (curskeleton) { if (avatarSkeleton) { error = "model can only contain a avatar skeleton"; return false; } avatarSkeleton = true; } meshsCosts.Add(curCost); meshsfee += curCost.costFee; } haveMeshs = true; } // instances (prims) cost int mesh; int skipedSmall = 0; for (int i = 0; i < numberInstances; i++) { Hashtable inst = (Hashtable)resources.instance_list.Array[i]; 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; if (scale.X < PrimScaleMin || scale.Y < PrimScaleMin || scale.Z < PrimScaleMin) { skipedSmall++; continue; } if (scale.X > NonPhysicalPrimScaleMax || scale.Y > NonPhysicalPrimScaleMax || scale.Z > NonPhysicalPrimScaleMax) { error = "Model contains parts with sides larger than " + NonPhysicalPrimScaleMax.ToString() + "m. Please ajust scale"; return false; } if (haveMeshs && inst.ContainsKey("mesh")) { mesh = (int)inst["mesh"]; if (mesh >= numberMeshs) { error = "Incoerent model information."; return false; } // streamming cost float sqdiam = scale.LengthSquared(); ameshCostParam curCost = meshsCosts[mesh]; float mesh_streaming = streamingCost(curCost, sqdiam); meshcostdata.model_streaming_cost += mesh_streaming; meshcostdata.physics_cost += curCost.physicsCost; } else // instance as no mesh ?? { // to do later if needed meshcostdata.model_streaming_cost += 0.5f; meshcostdata.physics_cost += 1.0f; } // assume unscripted and static prim server cost meshcostdata.simulation_cost += 0.5f; // charge for prims creation meshsfee += primCreationCost; } if (skipedSmall > 0) { if (skipedSmall > numberInstances / 2) { error = "Model contains too many prims smaller than " + PrimScaleMin.ToString() + "m minimum allowed size. Please check scalling"; return false; } else warning += skipedSmall.ToString() + " of the requested " +numberInstances.ToString() + " model prims will not upload because they are smaller than " + PrimScaleMin.ToString() + "m minimum allowed size. Please check scalling "; } if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost) meshcostdata.resource_cost = meshcostdata.model_streaming_cost; else meshcostdata.resource_cost = meshcostdata.physics_cost; if (meshcostdata.resource_cost < meshcostdata.simulation_cost) meshcostdata.resource_cost = meshcostdata.simulation_cost; // scale cost // at this point a cost of 1.0 whould mean basic cost meshsfee *= ModelMeshCostFactor; if (meshsfee < ModelMinCostFactor) meshsfee = ModelMinCostFactor; // actually scale it to basic cost meshsfee *= (float)basicCost; meshsfee += 0.5f; // rounding totalcost += (int)meshsfee; // breakdown prices // don't seem to be in use so removed code for now return true; }
// returns streaming cost from on mesh LODs sizes in curCost and square of prim size length 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); }
// single mesh asset cost private bool MeshCost(byte[] data, ameshCostParam cost, out bool skeleton, out bool avatarPhys, 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; skeleton = false; avatarPhys = false; if (data == null || data.Length == 0) { error = "Missing model information."; return(false); } OSD meshOsd = null; int start = 0; error = "Invalid model data"; using (MemoryStream ms = new MemoryStream(data)) { try { OSD osd = OSDParser.DeserializeLLSDBinary(ms); if (osd is OSDMap) { meshOsd = (OSDMap)osd; } else { return(false); } } catch { 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("skeleton")) { tmpmap = (OSDMap)map["skeleton"]; if (tmpmap.ContainsKey("offset") && tmpmap.ContainsKey("size")) { int sksize = tmpmap["size"].AsInteger(); if (sksize > 0) { skeleton = true; } } } 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 = "Missing physics_convex block"; return(false); } if (!hulls(data, submesh_offset, hulls_size, out phys_hullsvertices, out phys_nhulls)) { error = "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 = "Missing high_lod block"; 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; tmpmap = null; if (map.ContainsKey("physics_mesh")) { tmpmap = (OSDMap)map["physics_mesh"]; } else if (map.ContainsKey("physics_shape")) // old naming { tmpmap = (OSDMap)map["physics_shape"]; } if (tmpmap != null) { 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 = "Model data 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; cost.costFee = sfee; return(true); }
// calculates a mesh model costs // returns false on error, with a reason on parameter error // resources input LLSD request // basicCost input region assets upload cost // totalcost returns model total upload fee // meshcostdata returns detailed costs for viewer // avatarSkeleton if mesh includes a avatar skeleton // useAvatarCollider if we should use physics mesh for avatar public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost, LLSDAssetUploadResponseData meshcostdata, out string error, ref string warning) { totalcost = 0; error = string.Empty; bool avatarSkeleton = false; if (resources == null || resources.instance_list == null || resources.instance_list.Array.Count == 0) { error = "missing model information."; return(false); } int numberInstances = resources.instance_list.Array.Count; if (ObjectLinkedPartsMax != 0 && numberInstances > ObjectLinkedPartsMax) { error = "Model would have more than " + ObjectLinkedPartsMax.ToString() + " linked prims"; 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) { float textures_cost = (float)(resources.texture_list.Array.Count * basicCost); textures_cost *= ModelTextureCostFactor; itmp = (int)(textures_cost + 0.5f); // round meshcostdata.upload_price_breakdown.texture = itmp; totalcost += itmp; } // meshs assets cost float meshsfee = 0; int numberMeshs = 0; bool haveMeshs = false; bool curskeleton; bool curAvatarPhys; List <ameshCostParam> meshsCosts = new List <ameshCostParam>(); 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 curskeleton, out curAvatarPhys, out error)) { return(false); } if (curskeleton) { if (avatarSkeleton) { error = "model can only contain a avatar skeleton"; return(false); } avatarSkeleton = true; } meshsCosts.Add(curCost); meshsfee += curCost.costFee; } haveMeshs = true; } // instances (prims) cost int mesh; int skipedSmall = 0; for (int i = 0; i < numberInstances; i++) { Hashtable inst = (Hashtable)resources.instance_list.Array[i]; 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; if (scale.X < PrimScaleMin || scale.Y < PrimScaleMin || scale.Z < PrimScaleMin) { skipedSmall++; continue; } if (scale.X > NonPhysicalPrimScaleMax || scale.Y > NonPhysicalPrimScaleMax || scale.Z > NonPhysicalPrimScaleMax) { error = "Model contains parts with sides larger than " + NonPhysicalPrimScaleMax.ToString() + "m. Please ajust scale"; return(false); } if (haveMeshs && inst.ContainsKey("mesh")) { mesh = (int)inst["mesh"]; if (mesh >= numberMeshs) { error = "Incoerent model information."; return(false); } // streamming cost float sqdiam = scale.LengthSquared(); ameshCostParam curCost = meshsCosts[mesh]; float mesh_streaming = streamingCost(curCost, sqdiam); meshcostdata.model_streaming_cost += mesh_streaming; meshcostdata.physics_cost += curCost.physicsCost; } else // instance as no mesh ?? { // to do later if needed meshcostdata.model_streaming_cost += 0.5f; meshcostdata.physics_cost += 1.0f; } // assume unscripted and static prim server cost meshcostdata.simulation_cost += 0.5f; // charge for prims creation meshsfee += primCreationCost; } if (skipedSmall > 0) { if (skipedSmall > numberInstances / 2) { error = "Model contains too many prims smaller than " + PrimScaleMin.ToString() + "m minimum allowed size. Please check scalling"; return(false); } else { warning += skipedSmall.ToString() + " of the requested " + numberInstances.ToString() + " model prims will not upload because they are smaller than " + PrimScaleMin.ToString() + "m minimum allowed size. Please check scalling "; } } if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost) { meshcostdata.resource_cost = meshcostdata.model_streaming_cost; } else { meshcostdata.resource_cost = meshcostdata.physics_cost; } if (meshcostdata.resource_cost < meshcostdata.simulation_cost) { meshcostdata.resource_cost = meshcostdata.simulation_cost; } // scale cost // at this point a cost of 1.0 whould mean basic cost meshsfee *= ModelMeshCostFactor; if (meshsfee < ModelMinCostFactor) { meshsfee = ModelMinCostFactor; } // actually scale it to basic cost meshsfee *= (float)basicCost; meshsfee += 0.5f; // rounding totalcost += (int)meshsfee; // breakdown prices // don't seem to be in use so removed code for now return(true); }