Warp3DImageModule.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Drawing;
  30. using System.Drawing.Imaging;
  31. using System.IO;
  32. using System.Reflection;
  33. using System.Runtime;
  34. using CSJ2K;
  35. using Nini.Config;
  36. using log4net;
  37. using Warp3D;
  38. using Mono.Addins;
  39. using OpenSim.Framework;
  40. using OpenSim.Region.Framework.Interfaces;
  41. using OpenSim.Region.Framework.Scenes;
  42. using OpenMetaverse;
  43. using OpenMetaverse.Assets;
  44. using OpenMetaverse.Imaging;
  45. using OpenMetaverse.Rendering;
  46. using OpenMetaverse.StructuredData;
  47. using WarpRenderer = Warp3D.Warp3D;
  48. namespace OpenSim.Region.CoreModules.World.Warp3DMap
  49. {
  50. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "Warp3DImageModule")]
  51. public class Warp3DImageModule : IMapImageGenerator, INonSharedRegionModule
  52. {
  53. private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216);
  54. // private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 128);
  55. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  56. #pragma warning disable 414
  57. private static string LogHeader = "[WARP 3D IMAGE MODULE]";
  58. #pragma warning restore 414
  59. internal Scene m_scene;
  60. private IRendering m_primMesher;
  61. internal IJ2KDecoder m_imgDecoder;
  62. // caches per rendering
  63. private Dictionary<UUID, warp_Texture> m_warpTextures;
  64. private Dictionary<UUID, int> m_colors;
  65. private bool m_drawPrimVolume = true; // true if should render the prims on the tile
  66. private bool m_textureTerrain = true; // true if to create terrain splatting texture
  67. private bool m_textureAverageTerrain = false; // replace terrain textures by their average color
  68. private bool m_texturePrims = true; // true if should texture the rendered prims
  69. private float m_texturePrimSize = 48f; // size of prim before we consider texturing it
  70. private bool m_renderMeshes = false; // true if to render meshes rather than just bounding boxes
  71. private const float m_cameraHeight = 4096f;
  72. private float m_renderMinHeight = -100f;
  73. private float m_renderMaxHeight = 4096f;
  74. private bool m_Enabled = false;
  75. #region Region Module interface
  76. public void Initialise(IConfigSource source)
  77. {
  78. string[] configSections = new string[] { "Map", "Startup" };
  79. if (Util.GetConfigVarFromSections<string>(
  80. source, "MapImageModule", configSections, "MapImageModule") != "Warp3DImageModule")
  81. return;
  82. m_Enabled = true;
  83. m_drawPrimVolume =
  84. Util.GetConfigVarFromSections<bool>(source, "DrawPrimOnMapTile", configSections, m_drawPrimVolume);
  85. m_textureTerrain =
  86. Util.GetConfigVarFromSections<bool>(source, "TextureOnMapTile", configSections, m_textureTerrain);
  87. m_textureAverageTerrain =
  88. Util.GetConfigVarFromSections<bool>(source, "AverageTextureColorOnMapTile", configSections, m_textureAverageTerrain);
  89. if (m_textureAverageTerrain)
  90. m_textureTerrain = true;
  91. m_texturePrims =
  92. Util.GetConfigVarFromSections<bool>(source, "TexturePrims", configSections, m_texturePrims);
  93. m_texturePrimSize =
  94. Util.GetConfigVarFromSections<float>(source, "TexturePrimSize", configSections, m_texturePrimSize);
  95. m_renderMeshes =
  96. Util.GetConfigVarFromSections<bool>(source, "RenderMeshes", configSections, m_renderMeshes);
  97. m_renderMaxHeight = Util.GetConfigVarFromSections<float>(source, "RenderMaxHeight", configSections, m_renderMaxHeight);
  98. m_renderMinHeight = Util.GetConfigVarFromSections<float>(source, "RenderMinHeight", configSections, m_renderMinHeight);
  99. /*
  100. m_cameraHeight = Util.GetConfigVarFromSections<float>(m_config, "RenderCameraHeight", configSections, m_cameraHeight);
  101. if (m_cameraHeight < 250f)
  102. m_cameraHeight = 250f;
  103. else if (m_cameraHeight > 4096f)
  104. m_cameraHeight = 4096f;
  105. */
  106. if (m_renderMaxHeight < 100f)
  107. m_renderMaxHeight = 100f;
  108. else if (m_renderMaxHeight > m_cameraHeight - 10f)
  109. m_renderMaxHeight = m_cameraHeight - 10f;
  110. if (m_renderMinHeight < -100f)
  111. m_renderMinHeight = -100f;
  112. else if (m_renderMinHeight > m_renderMaxHeight - 10f)
  113. m_renderMinHeight = m_renderMaxHeight - 10f;
  114. }
  115. public void AddRegion(Scene scene)
  116. {
  117. if (!m_Enabled)
  118. return;
  119. m_scene = scene;
  120. List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
  121. if (renderers.Count > 0)
  122. m_log.Info("[MAPTILE]: Loaded prim mesher " + renderers[0]);
  123. else
  124. m_log.Info("[MAPTILE]: No prim mesher loaded, prim rendering will be disabled");
  125. m_scene.RegisterModuleInterface<IMapImageGenerator>(this);
  126. }
  127. public void RegionLoaded(Scene scene)
  128. {
  129. if (!m_Enabled)
  130. return;
  131. m_imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
  132. }
  133. public void RemoveRegion(Scene scene)
  134. {
  135. }
  136. public void Close()
  137. {
  138. }
  139. public string Name
  140. {
  141. get { return "Warp3DImageModule"; }
  142. }
  143. public Type ReplaceableInterface
  144. {
  145. get { return null; }
  146. }
  147. #endregion
  148. #region IMapImageGenerator Members
  149. private Vector3 cameraPos;
  150. private Vector3 cameraDir;
  151. private int viewWidth = 256;
  152. private int viewHeight = 256;
  153. private float fov;
  154. private bool orto;
  155. public Bitmap CreateMapTile()
  156. {
  157. List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
  158. if (renderers.Count > 0)
  159. {
  160. m_primMesher = RenderingLoader.LoadRenderer(renderers[0]);
  161. }
  162. viewWidth = (int)m_scene.RegionInfo.RegionSizeX;
  163. viewHeight = (int)m_scene.RegionInfo.RegionSizeY;
  164. cameraPos = new Vector3(
  165. viewWidth * 0.5f,
  166. viewHeight * 0.5f,
  167. m_cameraHeight);
  168. cameraDir = -Vector3.UnitZ;
  169. orto = true;
  170. Bitmap tile = GenImage();
  171. // image may be reloaded elsewhere, so no compression format
  172. string filename = "MAP-" + m_scene.RegionInfo.RegionID.ToString() + ".png";
  173. tile.Save(filename,ImageFormat.Png);
  174. m_primMesher = null;
  175. return tile;
  176. }
  177. public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float pfov, int width, int height, bool useTextures)
  178. {
  179. List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
  180. if (renderers.Count > 0)
  181. {
  182. m_primMesher = RenderingLoader.LoadRenderer(renderers[0]);
  183. }
  184. cameraPos = camPos;
  185. cameraDir = camDir;
  186. viewWidth = width;
  187. viewHeight = height;
  188. fov = pfov;
  189. orto = false;
  190. Bitmap tile = GenImage();
  191. m_primMesher = null;
  192. return tile;
  193. }
  194. private Bitmap GenImage()
  195. {
  196. m_colors= new Dictionary<UUID, int>();
  197. m_warpTextures= new Dictionary<UUID, warp_Texture>();
  198. WarpRenderer renderer = new WarpRenderer();
  199. if (!renderer.CreateScene(viewWidth, viewHeight))
  200. return new Bitmap(viewWidth, viewHeight);
  201. #region Camera
  202. warp_Vector pos = ConvertVector(ref cameraPos);
  203. warp_Vector lookat = ConvertVector(cameraPos + cameraDir);
  204. if (orto)
  205. renderer.Scene.defaultCamera.setOrthographic(true, viewWidth, viewHeight);
  206. else
  207. renderer.Scene.defaultCamera.setFov(fov);
  208. renderer.Scene.defaultCamera.setPos(pos);
  209. renderer.Scene.defaultCamera.lookAt(lookat);
  210. #endregion Camera
  211. renderer.Scene.setAmbient(warp_Color.getColor(192, 191, 173));
  212. renderer.Scene.addLight("Light1", new warp_Light(new warp_Vector(0f, 1f, 8f), warp_Color.White, 0, 200, 20));
  213. CreateWater(renderer);
  214. CreateTerrain(renderer);
  215. if (m_drawPrimVolume)
  216. CreateAllPrims(renderer);
  217. renderer.Render();
  218. Bitmap bitmap = renderer.Scene.getImage();
  219. renderer.Scene.destroy();
  220. renderer.Reset();
  221. renderer = null;
  222. m_colors = null;
  223. m_warpTextures = null;
  224. GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
  225. GC.Collect();
  226. GC.WaitForPendingFinalizers();
  227. GC.Collect();
  228. GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.Default;
  229. return bitmap;
  230. }
  231. public byte[] WriteJpeg2000Image()
  232. {
  233. try
  234. {
  235. using (Bitmap mapbmp = CreateMapTile())
  236. return OpenJPEG.EncodeFromImage(mapbmp, false);
  237. }
  238. catch (Exception e)
  239. {
  240. // JPEG2000 encoder failed
  241. m_log.Error("[WARP 3D IMAGE MODULE]: Failed generating terrain map: ", e);
  242. }
  243. return null;
  244. }
  245. #endregion
  246. #region Rendering Methods
  247. // Add a water plane to the renderer.
  248. private void CreateWater(WarpRenderer renderer)
  249. {
  250. float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
  251. renderer.AddPlane("Water", m_scene.RegionInfo.RegionSizeX * 0.5f, false);
  252. renderer.Scene.sceneobject("Water").setPos(m_scene.RegionInfo.RegionSizeX * 0.5f,
  253. waterHeight,
  254. m_scene.RegionInfo.RegionSizeY * 0.5f);
  255. warp_Material waterMaterial = new warp_Material(ConvertColor(WATER_COLOR));
  256. renderer.Scene.addMaterial("WaterMat", waterMaterial);
  257. renderer.SetObjectMaterial("Water", "WaterMat");
  258. }
  259. // Add a terrain to the renderer.
  260. // Note that we create a 'low resolution' 257x257 vertex terrain rather than trying for
  261. // full resolution. This saves a lot of memory especially for very large regions.
  262. private void CreateTerrain(WarpRenderer renderer)
  263. {
  264. ITerrainChannel terrain = m_scene.Heightmap;
  265. float regionsx = m_scene.RegionInfo.RegionSizeX;
  266. float regionsy = m_scene.RegionInfo.RegionSizeY;
  267. // 'diff' is the difference in scale between the real region size and the size of terrain we're buiding
  268. int bitWidth = Util.intLog2((uint)terrain.Width);
  269. int bitHeight = Util.intLog2((uint)terrain.Height);
  270. if(bitHeight > bitWidth)
  271. bitWidth = bitHeight;
  272. if (bitWidth > 8) // more than 256 is very heavy :(
  273. bitWidth = 8;
  274. int twidth = 1 << bitWidth;
  275. float diff = regionsx / twidth;
  276. int npointsx = (int)(regionsx / diff);
  277. int npointsy = (int)(regionsy / diff);
  278. float invsx = 1.0f / (npointsx * diff);
  279. float invsy = 1.0f / (npointsy * diff);
  280. npointsx++;
  281. npointsy++;
  282. // Create all the vertices for the terrain
  283. warp_Object obj = new warp_Object();
  284. float x, y;
  285. float tv;
  286. for (y = 0; y < regionsy; y += diff)
  287. {
  288. tv = y * invsy;
  289. for (x = 0; x < regionsx; x += diff)
  290. obj.addVertex(x, terrain[(int)x, (int)y], y, x * invsx, tv);
  291. obj.addVertex(x, terrain[(int)(x - diff), (int)y], y, 1.0f, tv);
  292. }
  293. int lastY = (int)(y - diff);
  294. for (x = 0; x < regionsx; x += diff)
  295. obj.addVertex(x, terrain[(int)x, lastY], y, x * invsx, 1.0f);
  296. obj.addVertex(x, terrain[(int)(x - diff), lastY],y, 1.0f, 1.0f);
  297. // create triangles.
  298. int limx = npointsx - 1;
  299. int limy = npointsy - 1;
  300. for (int j = 0; j < limy; j++)
  301. {
  302. for (int i = 0; i < limx; i++)
  303. {
  304. int v = j * npointsx + i;
  305. // Make two triangles for each of the squares in the grid of vertices
  306. obj.addTriangle(v, v + 1, v + npointsx);
  307. obj.addTriangle( v + npointsx + 1, v + npointsx, v + 1);
  308. }
  309. }
  310. renderer.Scene.addObject("Terrain", obj);
  311. OpenSim.Framework.RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings;
  312. UUID[] textureIDs = new UUID[4]
  313. {
  314. regionInfo.TerrainTexture1,
  315. regionInfo.TerrainTexture2,
  316. regionInfo.TerrainTexture3,
  317. regionInfo.TerrainTexture4,
  318. };
  319. float[] startHeights = new float[4]
  320. {
  321. (float)regionInfo.Elevation1SW,
  322. (float)regionInfo.Elevation1NW,
  323. (float)regionInfo.Elevation1SE,
  324. (float)regionInfo.Elevation1NE
  325. };
  326. float[] heightRanges = new float[4]
  327. {
  328. (float)regionInfo.Elevation2SW,
  329. (float)regionInfo.Elevation2NW,
  330. (float)regionInfo.Elevation2SE,
  331. (float)regionInfo.Elevation2NE
  332. };
  333. warp_Texture texture;
  334. using (Bitmap image = TerrainSplat.Splat(terrain, textureIDs, startHeights, heightRanges,
  335. m_scene.RegionInfo.WorldLocX, m_scene.RegionInfo.WorldLocY,
  336. m_scene.AssetService, m_imgDecoder, m_textureTerrain, m_textureAverageTerrain,
  337. twidth, twidth))
  338. texture = new warp_Texture(image);
  339. warp_Material material = new warp_Material(texture);
  340. obj.setMaterial(material);
  341. renderer.Scene.addMaterial("TerrainMat", material);
  342. }
  343. private void CreateAllPrims(WarpRenderer renderer)
  344. {
  345. if (m_primMesher == null)
  346. return;
  347. m_scene.ForEachSOG(
  348. delegate (SceneObjectGroup group)
  349. {
  350. foreach (SceneObjectPart child in group.Parts)
  351. CreatePrim(renderer, child);
  352. }
  353. );
  354. }
  355. private void UVPlanarMap(ref Vertex v, ref Vector3 scale, out float tu, out float tv)
  356. {
  357. Vector3 scaledPos = v.Position * scale;
  358. float d = v.Normal.X;
  359. if (d >= 0.5f)
  360. {
  361. tu = 2f * scaledPos.Y;
  362. tv = scaledPos.X * v.Normal.Z - scaledPos.Z * v.Normal.X;
  363. }
  364. else if( d <= -0.5f)
  365. {
  366. tu = -2f * scaledPos.Y;
  367. tv = -scaledPos.X * v.Normal.Z + scaledPos.Z * v.Normal.X;
  368. }
  369. else if (v.Normal.Y > 0f)
  370. {
  371. tu = -2f * scaledPos.X;
  372. tv = scaledPos.Y * v.Normal.Z - scaledPos.Z * v.Normal.Y;
  373. }
  374. else
  375. {
  376. tu = 2f * scaledPos.X;
  377. tv = -scaledPos.Y * v.Normal.Z + scaledPos.Z * v.Normal.Y;
  378. }
  379. tv *= 2f;
  380. }
  381. private void CreatePrim(WarpRenderer renderer, SceneObjectPart prim)
  382. {
  383. if ((PCode)prim.Shape.PCode != PCode.Prim)
  384. return;
  385. Vector3 ppos = prim.GetWorldPosition();
  386. if (ppos.Z < m_renderMinHeight || ppos.Z > m_renderMaxHeight)
  387. return;
  388. warp_Vector primPos = ConvertVector(ref ppos);
  389. warp_Matrix m = warp_Matrix.quaternionMatrix(ConvertQuaternion(prim.GetWorldRotation()));
  390. Vector3 primScale = prim.Scale;
  391. float screenFactor = renderer.Scene.EstimateBoxProjectedArea(primPos, ConvertVector(primScale), m);
  392. if (screenFactor < 0)
  393. return;
  394. int p2 = (int)(MathF.Log2(screenFactor) * 0.25 - 1);
  395. if (p2 < 0)
  396. p2 = 0;
  397. else if (p2 > 3)
  398. p2 = 3;
  399. DetailLevel lod = (DetailLevel)(3 - p2);
  400. FacetedMesh renderMesh = null;
  401. Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset);
  402. if (m_renderMeshes)
  403. {
  404. if (omvPrim.Sculpt is not null && !omvPrim.Sculpt.SculptTexture.IsZero())
  405. {
  406. // Try fetchinng the asset
  407. AssetBase sculptAsset = m_scene.AssetService.Get(omvPrim.Sculpt.SculptTexture.ToString());
  408. if (sculptAsset is not null)
  409. {
  410. // Is it a mesh?
  411. if (omvPrim.Sculpt.Type == SculptType.Mesh)
  412. {
  413. AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset.Data);
  414. FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, lod, out renderMesh);
  415. }
  416. else // It's sculptie
  417. {
  418. if (m_imgDecoder is not null)
  419. {
  420. Image sculpt = m_imgDecoder.DecodeToImage(sculptAsset.Data);
  421. if (sculpt is not null)
  422. {
  423. renderMesh = m_primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, lod);
  424. sculpt.Dispose();
  425. }
  426. }
  427. }
  428. }
  429. else
  430. {
  431. m_log.WarnFormat("[Warp3D] failed to get mesh or sculpt asset {0} of prim {1} at {2}",
  432. omvPrim.Sculpt.SculptTexture.ToString(), prim.Name, prim.GetWorldPosition().ToString());
  433. }
  434. }
  435. }
  436. // If not a mesh or sculptie, try the regular mesher
  437. renderMesh ??= m_primMesher.GenerateFacetedMesh(omvPrim, lod);
  438. if (renderMesh is null)
  439. return;
  440. Primitive.TextureEntry te = prim.Shape.Textures;
  441. if (te is null)
  442. return;
  443. string primID = prim.UUID.ToString();
  444. float rc = 0;
  445. float rs = 0;
  446. for (int i = 0; i < renderMesh.Faces.Count; i++)
  447. {
  448. Primitive.TextureEntryFace teFace = te.GetFace((uint)i);
  449. Color4 faceColor = teFace.RGBA;
  450. if (faceColor.A == 0)
  451. continue;
  452. warp_Material faceMaterial;
  453. if (m_texturePrims)
  454. {
  455. faceMaterial = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID, false, prim);
  456. if (faceMaterial is null)
  457. continue;
  458. if ((faceMaterial.getColor() & warp_Color.MASKALPHA) == 0)
  459. continue;
  460. }
  461. else
  462. faceMaterial = GetOrCreateMaterial(renderer, faceColor);
  463. warp_Object faceObj = new warp_Object();
  464. faceObj.setMaterial(faceMaterial);
  465. Face face = renderMesh.Faces[i];
  466. if (faceMaterial.getTexture() is null)
  467. {
  468. // uv map details dont not matter for color;
  469. for (int j = 0; j < face.Vertices.Count; j++)
  470. {
  471. warp_Vector pos = ConvertVector(face.Vertices[j].Position);
  472. warp_Vertex vert = new warp_Vertex(pos, face.Vertices[j].TexCoord.X, face.Vertices[j].TexCoord.Y);
  473. faceObj.addVertex(vert);
  474. }
  475. }
  476. else
  477. {
  478. float tu;
  479. float tv;
  480. float offsetu = teFace.OffsetU + 0.5f;
  481. float offsetv = teFace.OffsetV + 0.5f;
  482. float scaleu = teFace.RepeatU;
  483. float scalev = teFace.RepeatV;
  484. float rotation = teFace.Rotation;
  485. if (rotation != 0)
  486. {
  487. rc = MathF.Cos(rotation);
  488. rs = MathF.Sin(rotation);
  489. }
  490. for (int j = 0; j < face.Vertices.Count; j++)
  491. {
  492. if(teFace.TexMapType == MappingType.Planar)
  493. {
  494. Vertex v = face.Vertices[j];
  495. UVPlanarMap(ref v, ref primScale, out tu, out tv);
  496. }
  497. else
  498. {
  499. tu = face.Vertices[j].TexCoord.X - 0.5f;
  500. tv = 0.5f - face.Vertices[j].TexCoord.Y;
  501. }
  502. warp_Vector pos = ConvertVector(face.Vertices[j].Position);
  503. if (rotation != 0)
  504. {
  505. float tur = tu * rc - tv * rs;
  506. float tvr = tu * rs + tv * rc;
  507. faceObj.addVertex(new warp_Vertex(pos, tur * scaleu + offsetu, tvr * scalev + offsetv));
  508. }
  509. else
  510. {
  511. faceObj.addVertex(new warp_Vertex(pos, tu * scaleu + offsetu, tv * scalev + offsetv));
  512. }
  513. }
  514. }
  515. for (int j = 0; j < face.Indices.Count; j += 3)
  516. {
  517. faceObj.addTriangle(
  518. face.Indices[j + 0],
  519. face.Indices[j + 1],
  520. face.Indices[j + 2]);
  521. }
  522. faceObj.scaleSelf(primScale.X, primScale.Z, primScale.Y);
  523. faceObj.transform(m);
  524. faceObj.setPos(primPos);
  525. renderer.Scene.addObject(primID + i.ToString(), faceObj);
  526. }
  527. }
  528. private int GetFaceColor(Primitive.TextureEntryFace face)
  529. {
  530. int color;
  531. Color4 ctmp = Color4.White;
  532. if (face.TextureID.IsZero())
  533. return warp_Color.White;
  534. if (!m_colors.TryGetValue(face.TextureID, out color))
  535. {
  536. bool fetched = false;
  537. // Attempt to fetch the texture metadata
  538. string cacheName = "MAPCLR" + face.TextureID.ToString();
  539. AssetBase metadata = m_scene.AssetService.GetCached(cacheName);
  540. if (metadata != null)
  541. {
  542. OSDMap map = null;
  543. try { map = OSDParser.Deserialize(metadata.Data) as OSDMap; } catch { }
  544. if (map != null)
  545. {
  546. ctmp = map["X-RGBA"].AsColor4();
  547. fetched = true;
  548. }
  549. }
  550. if (!fetched)
  551. {
  552. // Fetch the texture, decode and get the average color,
  553. // then save it to a temporary metadata asset
  554. AssetBase textureAsset = m_scene.AssetService.Get(face.TextureID.ToString());
  555. if (textureAsset != null)
  556. {
  557. int width, height;
  558. ctmp = GetAverageColor(textureAsset.FullID, textureAsset.Data, out width, out height);
  559. OSDMap data = new OSDMap { { "X-RGBA", OSD.FromColor4(ctmp) } };
  560. metadata = new AssetBase
  561. {
  562. Data = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data)),
  563. Description = "Metadata for texture color" + face.TextureID.ToString(),
  564. Flags = AssetFlags.Collectable,
  565. FullID = UUID.Zero,
  566. ID = cacheName,
  567. Local = true,
  568. Temporary = true,
  569. Name = String.Empty,
  570. Type = (sbyte)AssetType.Unknown
  571. };
  572. m_scene.AssetService.Store(metadata);
  573. }
  574. else
  575. {
  576. ctmp = new Color4(0.5f, 0.5f, 0.5f, 1.0f);
  577. }
  578. }
  579. color = ConvertColor(ctmp);
  580. m_colors[face.TextureID] = color;
  581. }
  582. return color;
  583. }
  584. private warp_Material GetOrCreateMaterial(WarpRenderer renderer, Color4 color)
  585. {
  586. string name = color.ToString();
  587. if(renderer.Scene.TryGetMaterial(name, out warp_Material material))
  588. return material;
  589. material = new warp_Material(ConvertColor(color));
  590. renderer.Scene.addMaterial(name, material);
  591. return material;
  592. }
  593. public warp_Material GetOrCreateMaterial(WarpRenderer renderer, Color4 faceColor, UUID textureID, bool useAverageTextureColor, SceneObjectPart sop)
  594. {
  595. int color = ConvertColor(faceColor);
  596. string idstr = textureID.ToString() + color.ToString();
  597. string materialName = "MAPMAT" + idstr;
  598. if (renderer.Scene.TryGetMaterial(materialName, out warp_Material mat))
  599. return mat;
  600. mat = new warp_Material();
  601. warp_Texture texture = GetTexture(textureID, sop);
  602. if (texture is not null)
  603. {
  604. if (useAverageTextureColor)
  605. color = warp_Color.multiply(color, texture.averageColor);
  606. else
  607. mat.setTexture(texture);
  608. }
  609. else
  610. color = warp_Color.multiply(color, warp_Color.Grey);
  611. mat.setColor(color);
  612. renderer.Scene.addMaterial(materialName, mat);
  613. return mat;
  614. }
  615. private warp_Texture GetTexture(UUID id, SceneObjectPart sop)
  616. {
  617. if (id.IsZero())
  618. return null;
  619. if (m_warpTextures.TryGetValue(id, out warp_Texture ret))
  620. return ret;
  621. AssetBase asset = m_scene.AssetService.Get(id.ToString());
  622. if (asset is not null)
  623. {
  624. try
  625. {
  626. using (Bitmap img = (Bitmap)m_imgDecoder.DecodeToImage(asset.Data))
  627. ret = new warp_Texture(img, 8); // reduce textures size to 256 * 256
  628. }
  629. catch (Exception e)
  630. {
  631. m_log.WarnFormat("[Warp3D]: Failed to decode texture {0} for prim {1} at {2}, exception {3}", id.ToString(), sop.Name, sop.GetWorldPosition().ToString(), e.Message);
  632. }
  633. }
  634. else
  635. m_log.WarnFormat("[Warp3D]: missing texture {0} data for prim {1} at {2}",
  636. id.ToString(), sop.Name, sop.GetWorldPosition().ToString());
  637. m_warpTextures[id] = ret;
  638. return ret;
  639. }
  640. #endregion Rendering Methods
  641. #region Static Helpers
  642. // Note: axis change.
  643. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  644. private static warp_Vector ConvertVector(float x, float y, float z)
  645. {
  646. return new warp_Vector(x, z, y);
  647. }
  648. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  649. private static warp_Vector ConvertVector(Vector3 vector)
  650. {
  651. return new warp_Vector(vector.X, vector.Z, vector.Y);
  652. }
  653. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  654. private static warp_Vector ConvertVector(ref Vector3 vector)
  655. {
  656. return new warp_Vector(vector.X, vector.Z, vector.Y);
  657. }
  658. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  659. private static warp_Quaternion ConvertQuaternion(Quaternion quat)
  660. {
  661. return new warp_Quaternion(quat.X, quat.Z, quat.Y, -quat.W);
  662. }
  663. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  664. private static int ConvertColor(Color4 color)
  665. {
  666. int c = warp_Color.getColor((byte)(color.R * 255f), (byte)(color.G * 255f), (byte)(color.B * 255f), (byte)(color.A * 255f));
  667. return c;
  668. }
  669. private static Vector3 SurfaceNormal(Vector3 c1, Vector3 c2, Vector3 c3)
  670. {
  671. Vector3 normal = Vector3.Cross(c2 - c1, c3 - c1);
  672. normal.Normalize();
  673. return normal;
  674. }
  675. public Color4 GetAverageColor(UUID textureID, byte[] j2kData, out int width, out int height)
  676. {
  677. ulong r = 0;
  678. ulong g = 0;
  679. ulong b = 0;
  680. ulong a = 0;
  681. int pixelBytes;
  682. try
  683. {
  684. using (MemoryStream stream = new MemoryStream(j2kData))
  685. using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream))
  686. {
  687. width = bitmap.Width;
  688. height = bitmap.Height;
  689. BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
  690. pixelBytes = (bitmapData.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
  691. // Sum up the individual channels
  692. unsafe
  693. {
  694. byte* start = (byte*)bitmapData.Scan0;
  695. if (pixelBytes == 4)
  696. {
  697. for (int y = 0; y < height; y++)
  698. {
  699. byte* end = start + 4 * width;
  700. for(byte* row = start; row < end; row += 4)
  701. {
  702. b += row[0];
  703. g += row[1];
  704. r += row[2];
  705. a += row[3];
  706. }
  707. start += bitmapData.Stride;
  708. }
  709. }
  710. else
  711. {
  712. for (int y = 0; y < height; y++)
  713. {
  714. byte* end = start + 3 * width;
  715. for (byte* row = start; row < end; row += 3)
  716. {
  717. b += row[0];
  718. g += row[1];
  719. r += row[2];
  720. }
  721. start += bitmapData.Stride;
  722. }
  723. }
  724. }
  725. bitmap.UnlockBits(bitmapData);
  726. }
  727. // Get the averages for each channel
  728. double invtotalPixels = 1.0/(255.0 * width * height);
  729. double rm = r * invtotalPixels;
  730. double gm = g * invtotalPixels;
  731. double bm = b * invtotalPixels;
  732. double am = pixelBytes == 3 ? 1.0 : a * invtotalPixels;
  733. return new Color4((float)rm, (float)gm, (float)bm, (float)am);
  734. }
  735. catch (Exception ex)
  736. {
  737. m_log.WarnFormat(
  738. "[WARP 3D IMAGE MODULE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}",
  739. textureID, j2kData.Length, ex.Message);
  740. width = 0;
  741. height = 0;
  742. return new Color4(0.5f, 0.5f, 0.5f, 1.0f);
  743. }
  744. }
  745. #endregion Static Helpers
  746. }
  747. public static class ImageUtils
  748. {
  749. /// <summary>
  750. /// Performs bilinear interpolation between four values
  751. /// </summary>
  752. /// <param name="v00">First, or top left value</param>
  753. /// <param name="v01">Second, or top right value</param>
  754. /// <param name="v10">Third, or bottom left value</param>
  755. /// <param name="v11">Fourth, or bottom right value</param>
  756. /// <param name="xPercent">Interpolation value on the X axis, between 0.0 and 1.0</param>
  757. /// <param name="yPercent">Interpolation value on fht Y axis, between 0.0 and 1.0</param>
  758. /// <returns>The bilinearly interpolated result</returns>
  759. public static float Bilinear(float v00, float v01, float v10, float v11, float xPercent, float yPercent)
  760. {
  761. return Utils.Lerp(Utils.Lerp(v00, v01, xPercent), Utils.Lerp(v10, v11, xPercent), yPercent);
  762. }
  763. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  764. public static float Bilinear(float[] v, float xPercent, float yPercent)
  765. {
  766. return Utils.Lerp(Utils.Lerp(v[0], v[2], xPercent), Utils.Lerp(v[1], v[3], xPercent), yPercent);
  767. }
  768. }
  769. }