2017-10-07 21:35:42 +00:00
/ *
* Copyright ( c ) Contributors , http : //opensimulator.org/
* See CONTRIBUTORS . TXT for a full list of copyright holders .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ` ` AS IS ' ' AND ANY
* EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
using System ;
using System.Collections.Generic ;
using System.Drawing ;
using System.Drawing.Imaging ;
using System.IO ;
using System.Reflection ;
2020-01-09 14:54:24 +00:00
using System.Runtime ;
2017-10-07 21:35:42 +00:00
using CSJ2K ;
using Nini.Config ;
using log4net ;
using Warp3D ;
using Mono.Addins ;
using OpenSim.Framework ;
using OpenSim.Region.Framework.Interfaces ;
using OpenSim.Region.Framework.Scenes ;
2020-01-09 14:54:24 +00:00
using OpenSim.Region.PhysicsModules.SharedBase ;
using OpenSim.Services.Interfaces ;
2017-10-07 21:35:42 +00:00
using OpenMetaverse ;
using OpenMetaverse.Assets ;
using OpenMetaverse.Imaging ;
using OpenMetaverse.Rendering ;
using OpenMetaverse.StructuredData ;
2020-01-09 14:54:24 +00:00
using WarpRenderer = Warp3D . Warp3D ;
using System.Drawing.Drawing2D ;
[assembly: Addin("Warp3DCachedImageModule", "1.1")]
2018-10-16 15:36:59 +00:00
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
2017-10-07 21:35:42 +00:00
namespace OpenSim.Region.CoreModules.World.Warp3DMap
{
2018-10-16 15:36:59 +00:00
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "Warp3DCachedImageModule")]
2017-10-07 21:35:42 +00:00
public class Warp3DImageModule : IMapImageGenerator , INonSharedRegionModule
{
2020-01-09 14:54:24 +00:00
private static readonly Color4 WATER_COLOR = new Color4 ( 29 , 72 , 96 , 216 ) ;
// private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 128);
2017-10-07 21:35:42 +00:00
private static readonly ILog m_log = LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
#pragma warning disable 414
2020-01-09 14:54:24 +00:00
private static string LogHeader = "[WARP 3D CACHED IMAGE MODULE]" ;
2017-10-07 21:35:42 +00:00
#pragma warning restore 414
2020-01-09 14:54:24 +00:00
private const float m_cameraHeight = 4096f ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
internal Scene m_scene ;
2017-10-07 21:35:42 +00:00
private IRendering m_primMesher ;
2020-01-09 14:54:24 +00:00
internal IJ2KDecoder m_imgDecoder ;
// caches per rendering
private Dictionary < string , warp_Texture > m_warpTextures = new Dictionary < string , warp_Texture > ( ) ;
private Dictionary < UUID , int > m_colors = new Dictionary < UUID , int > ( ) ;
2017-10-07 21:35:42 +00:00
private IConfigSource m_config ;
private bool m_drawPrimVolume = true ; // true if should render the prims on the tile
private bool m_textureTerrain = true ; // true if to create terrain splatting texture
2020-01-09 14:54:24 +00:00
private bool m_textureAverageTerrain = false ; // replace terrain textures by their average color
2017-10-07 21:35:42 +00:00
private bool m_texturePrims = true ; // true if should texture the rendered prims
private float m_texturePrimSize = 48f ; // size of prim before we consider texturing it
private bool m_renderMeshes = false ; // true if to render meshes rather than just bounding boxes
2020-01-09 14:54:24 +00:00
private float m_renderMinHeight = - 100f ;
private float m_renderMaxHeight = 4096f ;
private String m_cacheDirectory = "" ;
private bool m_enable_date = false ;
private bool m_enable_regionName = false ;
private bool m_enable_regionPosition = false ;
private bool m_enable_refreshEveryMonth = false ;
private bool m_enable_HostedBy = false ;
2018-10-16 15:36:59 +00:00
private String m_enable_HostedByText = "" ;
2020-01-09 14:54:24 +00:00
private bool m_Enabled = false ;
// private Bitmap lastImage = null;
2017-10-07 21:35:42 +00:00
private DateTime lastImageTime = DateTime . MinValue ;
#region Region Module interface
public void Initialise ( IConfigSource source )
{
m_config = source ;
string [ ] configSections = new string [ ] { "Map" , "Startup" } ;
if ( Util . GetConfigVarFromSections < string > (
2020-01-09 14:55:57 +00:00
m_config , "MapImageModule" , configSections , "MapImageModule" ) ! = "Warp3DCachedImageModule" )
2017-10-07 21:35:42 +00:00
return ;
m_Enabled = true ;
2020-01-09 14:54:24 +00:00
m_drawPrimVolume =
Util . GetConfigVarFromSections < bool > ( m_config , "DrawPrimOnMapTile" , configSections , m_drawPrimVolume ) ;
m_textureTerrain =
Util . GetConfigVarFromSections < bool > ( m_config , "TextureOnMapTile" , configSections , m_textureTerrain ) ;
m_textureAverageTerrain =
Util . GetConfigVarFromSections < bool > ( m_config , "AverageTextureColorOnMapTile" , configSections , m_textureAverageTerrain ) ;
if ( m_textureAverageTerrain )
m_textureTerrain = true ;
m_texturePrims =
Util . GetConfigVarFromSections < bool > ( m_config , "TexturePrims" , configSections , m_texturePrims ) ;
m_texturePrimSize =
Util . GetConfigVarFromSections < float > ( m_config , "TexturePrimSize" , configSections , m_texturePrimSize ) ;
m_renderMeshes =
Util . GetConfigVarFromSections < bool > ( m_config , "RenderMeshes" , configSections , m_renderMeshes ) ;
m_cacheDirectory =
Util . GetConfigVarFromSections < string > ( m_config , "CacheDirectory" , configSections , System . IO . Path . Combine ( new DirectoryInfo ( "." ) . FullName , "MapImageCache" ) ) ;
m_enable_date = Util . GetConfigVarFromSections < bool > ( m_config , "enableDate" , configSections , false ) ;
m_enable_regionName = Util . GetConfigVarFromSections < bool > ( m_config , "enableName" , configSections , false ) ;
m_enable_regionPosition = Util . GetConfigVarFromSections < bool > ( m_config , "enablePosition" , configSections , false ) ;
m_enable_refreshEveryMonth = Util . GetConfigVarFromSections < bool > ( m_config , "RefreshEveryMonth" , configSections , true ) ;
m_enable_HostedBy = Util . GetConfigVarFromSections < bool > ( m_config , "enableHostedBy" , configSections , false ) ;
2018-10-16 15:36:59 +00:00
m_enable_HostedByText = Util . GetConfigVarFromSections < String > ( m_config , "HosterText" , configSections , String . Empty ) ;
2020-01-09 14:54:24 +00:00
m_renderMaxHeight = Util . GetConfigVarFromSections < float > ( m_config , "RenderMaxHeight" , configSections , m_renderMaxHeight ) ;
m_renderMinHeight = Util . GetConfigVarFromSections < float > ( m_config , "RenderMinHeight" , configSections , m_renderMinHeight ) ;
if ( ! Directory . Exists ( m_cacheDirectory ) )
2017-10-07 21:35:42 +00:00
Directory . CreateDirectory ( m_cacheDirectory ) ;
2020-01-09 14:54:24 +00:00
if ( m_renderMaxHeight < 100f )
m_renderMaxHeight = 100f ;
else if ( m_renderMaxHeight > m_cameraHeight - 10f )
m_renderMaxHeight = m_cameraHeight - 10f ;
if ( m_renderMinHeight < - 100f )
m_renderMinHeight = - 100f ;
else if ( m_renderMinHeight > m_renderMaxHeight - 10f )
m_renderMinHeight = m_renderMaxHeight - 10f ;
2017-10-07 21:35:42 +00:00
}
public void AddRegion ( Scene scene )
{
if ( ! m_Enabled )
return ;
m_scene = scene ;
List < string > renderers = RenderingLoader . ListRenderers ( Util . ExecutingDirectory ( ) ) ;
if ( renderers . Count > 0 )
m_log . Info ( "[MAPTILE]: Loaded prim mesher " + renderers [ 0 ] ) ;
else
m_log . Info ( "[MAPTILE]: No prim mesher loaded, prim rendering will be disabled" ) ;
m_scene . RegisterModuleInterface < IMapImageGenerator > ( this ) ;
}
public void RegionLoaded ( Scene scene )
{
2020-01-09 14:54:24 +00:00
if ( ! m_Enabled )
return ;
m_imgDecoder = m_scene . RequestModuleInterface < IJ2KDecoder > ( ) ;
2017-10-07 21:35:42 +00:00
}
public void RemoveRegion ( Scene scene )
{
}
public void Close ( )
{
}
public string Name
{
2020-01-09 14:54:24 +00:00
get { return "Warp3DImageModule" ; }
2017-10-07 21:35:42 +00:00
}
public Type ReplaceableInterface
{
get { return null ; }
}
# endregion
#region IMapImageGenerator Members
2018-10-16 15:36:59 +00:00
2020-01-09 14:54:24 +00:00
private Vector3 cameraPos ;
private Vector3 cameraDir ;
private int viewWitdh = 256 ;
private int viewHeight = 256 ;
private float fov ;
private bool orto ;
public static string fillInt ( int _i , int _l )
{
String _return = _i . ToString ( ) ;
while ( _return . Length < _l )
{
_return = 0 + _return ;
}
return _return ;
}
public static int getCurrentUnixTime ( )
{
return ( Int32 ) ( DateTime . UtcNow . Subtract ( new DateTime ( 1970 , 1 , 1 ) ) ) . TotalSeconds ;
}
public static String unixTimeToDateString ( int unixTime )
{
DateTime unixStart = new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 , 0 , System . DateTimeKind . Utc ) ;
long unixTimeStampInTicks = ( long ) ( unixTime * TimeSpan . TicksPerSecond ) ;
DateTime _date = new DateTime ( unixStart . Ticks + unixTimeStampInTicks , System . DateTimeKind . Utc ) ;
return fillInt ( _date . Day , 2 ) + "." + fillInt ( _date . Month , 2 ) + "." + fillInt ( _date . Year , 4 ) + " " + fillInt ( _date . Hour , 2 ) + ":" + fillInt ( _date . Minute , 2 ) ;
}
private void writeDateOnMap ( ref Bitmap _map )
{
RectangleF rectf = new RectangleF ( 2 , 1 , 200 , 25 ) ;
Graphics g = Graphics . FromImage ( _map ) ;
g . SmoothingMode = SmoothingMode . AntiAlias ;
g . InterpolationMode = InterpolationMode . HighQualityBicubic ;
g . PixelOffsetMode = PixelOffsetMode . HighQuality ;
g . DrawString ( unixTimeToDateString ( getCurrentUnixTime ( ) ) , new Font ( "Arial" , 8 ) , Brushes . White , rectf ) ;
g . Flush ( ) ;
}
private void writeNameOnMap ( ref Bitmap _map )
{
RectangleF rectf = new RectangleF ( 2 , m_scene . RegionInfo . RegionSizeX - 15 , 200 , 25 ) ;
Graphics g = Graphics . FromImage ( _map ) ;
g . SmoothingMode = SmoothingMode . AntiAlias ;
g . InterpolationMode = InterpolationMode . HighQualityBicubic ;
g . PixelOffsetMode = PixelOffsetMode . HighQuality ;
g . DrawString ( m_scene . Name , new Font ( "Arial" , 8 ) , Brushes . White , rectf ) ;
g . Flush ( ) ;
}
private void writePositionOnMap ( ref Bitmap _map )
{
2020-05-16 15:37:18 +00:00
RectangleF rectf = new RectangleF ( m_scene . RegionInfo . RegionSizeY - 85 , m_scene . RegionInfo . RegionSizeX - 15 , 80 , 25 ) ;
2020-01-09 14:54:24 +00:00
Graphics g = Graphics . FromImage ( _map ) ;
g . SmoothingMode = SmoothingMode . AntiAlias ;
g . InterpolationMode = InterpolationMode . HighQualityBicubic ;
g . PixelOffsetMode = PixelOffsetMode . HighQuality ;
2020-05-16 15:37:18 +00:00
g . DrawString ( m_scene . RegionInfo . RegionLocX + ", " + m_scene . RegionInfo . RegionLocY , new Font ( "Arial" , 8 ) , Brushes . White , rectf ) ;
2020-01-09 14:54:24 +00:00
g . Flush ( ) ;
}
private void writeHostedByOnMap ( ref Bitmap _map )
{
RectangleF rectf = new RectangleF ( 2 , m_scene . RegionInfo . RegionSizeX - 15 , 200 , 25 ) ;
Graphics g = Graphics . FromImage ( _map ) ;
g . SmoothingMode = SmoothingMode . AntiAlias ;
g . InterpolationMode = InterpolationMode . HighQualityBicubic ;
g . PixelOffsetMode = PixelOffsetMode . HighQuality ;
g . DrawString ( m_enable_HostedByText , new Font ( "Arial" , 8 ) , Brushes . Gray , rectf ) ;
g . Flush ( ) ;
}
2018-10-16 15:36:59 +00:00
2020-01-09 14:54:24 +00:00
public Bitmap CreateMapTile ( )
{
2020-07-04 11:31:17 +00:00
if ( ( File . GetCreationTime ( System . IO . Path . Combine ( m_cacheDirectory , m_scene . RegionInfo . RegionID + "." + m_scene . RegionInfo . RegionSizeX + ".bmp" ) ) . Month ! = DateTime . Now . Month ) & & m_enable_refreshEveryMonth = = true )
File . Delete ( System . IO . Path . Combine ( m_cacheDirectory , m_scene . RegionInfo . RegionID + "." + m_scene . RegionInfo . RegionSizeX + ".bmp" ) ) ;
2020-01-09 14:54:24 +00:00
2020-07-04 11:31:17 +00:00
if ( File . Exists ( System . IO . Path . Combine ( m_cacheDirectory , m_scene . RegionInfo . RegionID + "." + m_scene . RegionInfo . RegionSizeX + ".bmp" ) ) )
2020-01-09 14:54:24 +00:00
{
2020-07-04 11:31:17 +00:00
return new Bitmap ( System . IO . Path . Combine ( m_cacheDirectory , m_scene . RegionInfo . RegionID + "." + m_scene . RegionInfo . RegionSizeX + ".bmp" ) ) ;
2020-01-09 14:54:24 +00:00
}
else
{
List < string > renderers = RenderingLoader . ListRenderers ( Util . ExecutingDirectory ( ) ) ;
if ( renderers . Count > 0 )
{
m_primMesher = RenderingLoader . LoadRenderer ( renderers [ 0 ] ) ;
}
cameraPos = new Vector3 (
( m_scene . RegionInfo . RegionSizeX ) * 0.5f ,
( m_scene . RegionInfo . RegionSizeY ) * 0.5f ,
m_cameraHeight ) ;
cameraDir = - Vector3 . UnitZ ;
viewWitdh = ( int ) m_scene . RegionInfo . RegionSizeX ;
viewHeight = ( int ) m_scene . RegionInfo . RegionSizeY ;
orto = true ;
// fov = warp_Math.rad2deg(2f * (float)Math.Atan2(viewWitdh, 4096f));
// orto = false;
Bitmap tile = GenImage ( ) ;
if ( m_enable_date )
writeDateOnMap ( ref tile ) ;
if ( m_enable_regionName )
writeNameOnMap ( ref tile ) ;
if ( m_enable_regionPosition )
writePositionOnMap ( ref tile ) ;
if ( m_enable_HostedBy )
writeHostedByOnMap ( ref tile ) ;
2020-07-04 11:31:17 +00:00
tile . Save ( System . IO . Path . Combine ( m_cacheDirectory , m_scene . RegionInfo . RegionID + "." + m_scene . RegionInfo . RegionSizeX + ".bmp" ) ) ;
2020-01-09 14:54:24 +00:00
// image may be reloaded elsewhere, so no compression format
string filename = "MAP-" + m_scene . RegionInfo . RegionID . ToString ( ) + ".png" ;
tile . Save ( filename , ImageFormat . Png ) ;
m_primMesher = null ;
return tile ;
}
2018-10-16 15:36:59 +00:00
}
2018-01-02 01:51:19 +00:00
2020-01-09 14:54:24 +00:00
public Bitmap CreateViewImage ( Vector3 camPos , Vector3 camDir , float pfov , int width , int height , bool useTextures )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
List < string > renderers = RenderingLoader . ListRenderers ( Util . ExecutingDirectory ( ) ) ;
if ( renderers . Count > 0 )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
m_primMesher = RenderingLoader . LoadRenderer ( renderers [ 0 ] ) ;
2017-10-07 21:35:42 +00:00
}
2017-10-14 20:51:24 +00:00
2020-01-09 14:54:24 +00:00
cameraPos = camPos ;
cameraDir = camDir ;
viewWitdh = width ;
viewHeight = height ;
fov = pfov ;
orto = false ;
2018-10-16 15:36:59 +00:00
2020-01-09 14:54:24 +00:00
Bitmap tile = GenImage ( ) ;
m_primMesher = null ;
return tile ;
2017-10-07 21:35:42 +00:00
}
2020-01-09 14:54:24 +00:00
private Bitmap GenImage ( )
2017-10-07 21:35:42 +00:00
{
m_colors . Clear ( ) ;
2020-01-09 14:54:24 +00:00
m_warpTextures . Clear ( ) ;
2017-10-07 21:35:42 +00:00
WarpRenderer renderer = new WarpRenderer ( ) ;
2020-01-09 14:54:24 +00:00
if ( ! renderer . CreateScene ( viewWitdh , viewHeight ) )
return new Bitmap ( viewWitdh , viewHeight ) ;
2017-10-07 21:35:42 +00:00
#region Camera
2020-01-09 14:54:24 +00:00
warp_Vector pos = ConvertVector ( cameraPos ) ;
warp_Vector lookat = warp_Vector . add ( pos , ConvertVector ( cameraDir ) ) ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
if ( orto )
renderer . Scene . defaultCamera . setOrthographic ( true , viewWitdh , viewHeight ) ;
2017-10-07 21:35:42 +00:00
else
renderer . Scene . defaultCamera . setFov ( fov ) ;
2020-01-09 14:54:24 +00:00
renderer . Scene . defaultCamera . setPos ( pos ) ;
renderer . Scene . defaultCamera . lookAt ( lookat ) ;
2017-10-07 21:35:42 +00:00
#endregion Camera
2020-01-09 14:54:24 +00:00
renderer . Scene . setAmbient ( warp_Color . getColor ( 192 , 191 , 173 ) ) ;
renderer . Scene . addLight ( "Light1" , new warp_Light ( new warp_Vector ( 0f , 1f , 8f ) , warp_Color . White , 0 , 320 , 40 ) ) ;
2017-10-07 21:35:42 +00:00
CreateWater ( renderer ) ;
2020-01-09 14:54:24 +00:00
CreateTerrain ( renderer ) ;
2017-10-07 21:35:42 +00:00
if ( m_drawPrimVolume )
2020-01-09 14:54:24 +00:00
CreateAllPrims ( renderer ) ;
2017-10-07 21:35:42 +00:00
renderer . Render ( ) ;
Bitmap bitmap = renderer . Scene . getImage ( ) ;
renderer . Scene . destroy ( ) ;
renderer . Reset ( ) ;
renderer = null ;
m_colors . Clear ( ) ;
2020-01-09 14:54:24 +00:00
m_warpTextures . Clear ( ) ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
GCSettings . LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode . CompactOnce ;
GC . Collect ( ) ;
GC . WaitForPendingFinalizers ( ) ;
GC . Collect ( ) ;
GCSettings . LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode . Default ;
2017-10-07 21:35:42 +00:00
return bitmap ;
}
public byte [ ] WriteJpeg2000Image ( )
{
try
{
using ( Bitmap mapbmp = CreateMapTile ( ) )
2020-01-09 14:54:24 +00:00
return OpenJPEG . EncodeFromImage ( mapbmp , false ) ;
2017-10-07 21:35:42 +00:00
}
catch ( Exception e )
{
// JPEG2000 encoder failed
2018-10-16 15:36:59 +00:00
m_log . Error ( "[WARP 3D IMAGE MODULE]: Failed generating terrain map: " , e ) ;
2017-10-07 21:35:42 +00:00
}
return null ;
}
# endregion
#region Rendering Methods
// Add a water plane to the renderer.
private void CreateWater ( WarpRenderer renderer )
{
float waterHeight = ( float ) m_scene . RegionInfo . RegionSettings . WaterHeight ;
2020-01-09 14:54:24 +00:00
renderer . AddPlane ( "Water" , m_scene . RegionInfo . RegionSizeX * 0.5f ) ;
renderer . Scene . sceneobject ( "Water" ) . setPos ( m_scene . RegionInfo . RegionSizeX * 0.5f ,
waterHeight ,
m_scene . RegionInfo . RegionSizeY * 0.5f ) ;
2017-10-07 22:40:03 +00:00
2020-01-09 14:54:24 +00:00
warp_Material waterMaterial = new warp_Material ( ConvertColor ( WATER_COLOR ) ) ;
renderer . Scene . addMaterial ( "WaterMat" , waterMaterial ) ;
renderer . SetObjectMaterial ( "Water" , "WaterMat" ) ;
2017-10-07 21:35:42 +00:00
}
// Add a terrain to the renderer.
2020-01-09 14:54:24 +00:00
// Note that we create a 'low resolution' 257x257 vertex terrain rather than trying for
2017-10-07 21:35:42 +00:00
// full resolution. This saves a lot of memory especially for very large regions.
2020-01-09 14:54:24 +00:00
private void CreateTerrain ( WarpRenderer renderer )
2017-10-07 21:35:42 +00:00
{
ITerrainChannel terrain = m_scene . Heightmap ;
float regionsx = m_scene . RegionInfo . RegionSizeX ;
float regionsy = m_scene . RegionInfo . RegionSizeY ;
// 'diff' is the difference in scale between the real region size and the size of terrain we're buiding
2020-01-09 14:54:24 +00:00
int bitWidth ;
int bitHeight ;
const double log2inv = 1.4426950408889634073599246810019 ;
bitWidth = ( int ) Math . Ceiling ( ( Math . Log ( terrain . Width ) * log2inv ) ) ;
bitHeight = ( int ) Math . Ceiling ( ( Math . Log ( terrain . Height ) * log2inv ) ) ;
if ( bitWidth > 8 ) // more than 256 is very heavy :(
bitWidth = 8 ;
if ( bitHeight > 8 )
bitHeight = 8 ;
int twidth = ( int ) Math . Pow ( 2 , bitWidth ) ;
int theight = ( int ) Math . Pow ( 2 , bitHeight ) ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
float diff = regionsx / twidth ;
int npointsx = ( int ) ( regionsx / diff ) ;
int npointsy = ( int ) ( regionsy / diff ) ;
float invsx = 1.0f / ( npointsx * diff ) ;
float invsy = 1.0f / ( npointsy * diff ) ;
npointsx + + ;
npointsy + + ;
2017-10-07 21:35:42 +00:00
// Create all the vertices for the terrain
warp_Object obj = new warp_Object ( ) ;
2020-01-09 14:54:24 +00:00
warp_Vector pos ;
float x , y ;
float tv ;
for ( y = 0 ; y < regionsy ; y + = diff )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
tv = y * invsy ;
for ( x = 0 ; x < regionsx ; x + = diff )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
pos = ConvertVector ( x , y , ( float ) terrain [ ( int ) x , ( int ) y ] ) ;
obj . addVertex ( new warp_Vertex ( pos , x * invsx , tv ) ) ;
2017-10-07 21:35:42 +00:00
}
2020-01-09 14:54:24 +00:00
pos = ConvertVector ( x , y , ( float ) terrain [ ( int ) ( x - diff ) , ( int ) y ] ) ;
obj . addVertex ( new warp_Vertex ( pos , 1.0f , tv ) ) ;
2017-10-07 21:35:42 +00:00
}
2020-01-09 14:54:24 +00:00
int lastY = ( int ) ( y - diff ) ;
for ( x = 0 ; x < regionsx ; x + = diff )
{
pos = ConvertVector ( x , y , ( float ) terrain [ ( int ) x , lastY ] ) ;
obj . addVertex ( new warp_Vertex ( pos , x * invsx , 1.0f ) ) ;
}
pos = ConvertVector ( x , y , ( float ) terrain [ ( int ) ( x - diff ) , lastY ] ) ;
obj . addVertex ( new warp_Vertex ( pos , 1.0f , 1.0f ) ) ;
// create triangles.
2017-10-07 21:35:42 +00:00
int limx = npointsx - 1 ;
int limy = npointsy - 1 ;
2020-01-09 14:54:24 +00:00
for ( int j = 0 ; j < limy ; j + + )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
for ( int i = 0 ; i < limx ; i + + )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
int v = j * npointsx + i ;
// Make two triangles for each of the squares in the grid of vertices
obj . addTriangle (
v ,
v + 1 ,
v + npointsx ) ;
obj . addTriangle (
v + npointsx + 1 ,
v + npointsx ,
v + 1 ) ;
2017-10-07 21:35:42 +00:00
}
}
renderer . Scene . addObject ( "Terrain" , obj ) ;
UUID [ ] textureIDs = new UUID [ 4 ] ;
float [ ] startHeights = new float [ 4 ] ;
float [ ] heightRanges = new float [ 4 ] ;
OpenSim . Framework . RegionSettings regionInfo = m_scene . RegionInfo . RegionSettings ;
textureIDs [ 0 ] = regionInfo . TerrainTexture1 ;
textureIDs [ 1 ] = regionInfo . TerrainTexture2 ;
textureIDs [ 2 ] = regionInfo . TerrainTexture3 ;
textureIDs [ 3 ] = regionInfo . TerrainTexture4 ;
startHeights [ 0 ] = ( float ) regionInfo . Elevation1SW ;
startHeights [ 1 ] = ( float ) regionInfo . Elevation1NW ;
startHeights [ 2 ] = ( float ) regionInfo . Elevation1SE ;
startHeights [ 3 ] = ( float ) regionInfo . Elevation1NE ;
heightRanges [ 0 ] = ( float ) regionInfo . Elevation2SW ;
heightRanges [ 1 ] = ( float ) regionInfo . Elevation2NW ;
heightRanges [ 2 ] = ( float ) regionInfo . Elevation2SE ;
heightRanges [ 3 ] = ( float ) regionInfo . Elevation2NE ;
warp_Texture texture ;
2020-01-09 14:54:24 +00:00
using ( Bitmap image = TerrainSplat . Splat ( terrain , textureIDs , startHeights , heightRanges ,
m_scene . RegionInfo . WorldLocX , m_scene . RegionInfo . WorldLocY ,
m_scene . AssetService , m_imgDecoder , m_textureTerrain , m_textureAverageTerrain ,
twidth , twidth ) )
2017-10-07 21:35:42 +00:00
texture = new warp_Texture ( image ) ;
warp_Material material = new warp_Material ( texture ) ;
2020-01-09 14:54:24 +00:00
renderer . Scene . addMaterial ( "TerrainMat" , material ) ;
renderer . SetObjectMaterial ( "Terrain" , "TerrainMat" ) ;
2017-10-07 21:35:42 +00:00
}
2020-01-09 14:54:24 +00:00
private void CreateAllPrims ( WarpRenderer renderer )
2017-10-07 21:35:42 +00:00
{
if ( m_primMesher = = null )
return ;
m_scene . ForEachSOG (
2020-01-09 14:54:24 +00:00
delegate ( SceneObjectGroup group )
2017-10-07 21:35:42 +00:00
{
foreach ( SceneObjectPart child in group . Parts )
2020-01-09 14:54:24 +00:00
CreatePrim ( renderer , child ) ;
2017-10-07 21:35:42 +00:00
}
) ;
}
2020-01-09 14:54:24 +00:00
private void UVPlanarMap ( Vertex v , Vector3 scale , out float tu , out float tv )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
Vector3 scaledPos = v . Position * scale ;
float d = v . Normal . X ;
if ( d > = 0.5f )
{
tu = 2f * scaledPos . Y ;
tv = scaledPos . X * v . Normal . Z - scaledPos . Z * v . Normal . X ;
}
else if ( d < = - 0.5f )
{
tu = - 2f * scaledPos . Y ;
tv = - scaledPos . X * v . Normal . Z + scaledPos . Z * v . Normal . X ;
}
else if ( v . Normal . Y > 0f )
{
tu = - 2f * scaledPos . X ;
tv = scaledPos . Y * v . Normal . Z - scaledPos . Z * v . Normal . Y ;
}
else
{
tu = 2f * scaledPos . X ;
tv = - scaledPos . Y * v . Normal . Z + scaledPos . Z * v . Normal . Y ;
}
tv * = 2f ;
}
2018-10-16 15:36:59 +00:00
2020-01-09 14:54:24 +00:00
private void CreatePrim ( WarpRenderer renderer , SceneObjectPart prim )
{
2017-10-07 21:35:42 +00:00
if ( ( PCode ) prim . Shape . PCode ! = PCode . Prim )
return ;
2020-01-09 14:54:24 +00:00
Vector3 ppos = prim . GetWorldPosition ( ) ;
if ( ppos . Z < m_renderMinHeight | | ppos . Z > m_renderMaxHeight )
2017-10-07 21:35:42 +00:00
return ;
2020-01-09 14:54:24 +00:00
warp_Vector primPos = ConvertVector ( ppos ) ;
warp_Quaternion primRot = ConvertQuaternion ( prim . GetWorldRotation ( ) ) ;
warp_Matrix m = warp_Matrix . quaternionMatrix ( primRot ) ;
float screenFactor = renderer . Scene . EstimateBoxProjectedArea ( primPos , ConvertVector ( prim . Scale ) , m ) ;
if ( screenFactor < 0 )
return ;
int p2 = ( int ) ( - ( float ) Math . Log ( screenFactor ) * 1.442695f * 0.5 - 1 ) ;
if ( p2 < 0 )
p2 = 0 ;
else if ( p2 > 3 )
p2 = 3 ;
DetailLevel lod = ( DetailLevel ) ( 3 - p2 ) ;
2017-10-07 21:35:42 +00:00
FacetedMesh renderMesh = null ;
Primitive omvPrim = prim . Shape . ToOmvPrimitive ( prim . OffsetPosition , prim . RotationOffset ) ;
if ( m_renderMeshes )
{
if ( omvPrim . Sculpt ! = null & & omvPrim . Sculpt . SculptTexture ! = UUID . Zero )
{
// Try fetchinng the asset
byte [ ] sculptAsset = m_scene . AssetService . GetData ( omvPrim . Sculpt . SculptTexture . ToString ( ) ) ;
if ( sculptAsset ! = null )
{
// Is it a mesh?
if ( omvPrim . Sculpt . Type = = SculptType . Mesh )
{
AssetMesh meshAsset = new AssetMesh ( omvPrim . Sculpt . SculptTexture , sculptAsset ) ;
2020-01-09 14:54:24 +00:00
FacetedMesh . TryDecodeFromAsset ( omvPrim , meshAsset , lod , out renderMesh ) ;
2017-10-07 21:35:42 +00:00
meshAsset = null ;
}
else // It's sculptie
{
2020-01-09 14:54:24 +00:00
if ( m_imgDecoder ! = null )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
Image sculpt = m_imgDecoder . DecodeToImage ( sculptAsset ) ;
if ( sculpt ! = null )
2018-10-16 15:36:59 +00:00
{
2020-01-09 14:54:24 +00:00
renderMesh = m_primMesher . GenerateFacetedSculptMesh ( omvPrim , ( Bitmap ) sculpt , lod ) ;
sculpt . Dispose ( ) ;
2017-10-07 21:35:42 +00:00
}
}
}
}
2020-01-09 14:54:24 +00:00
else
{
m_log . WarnFormat ( "[Warp3D] failed to get mesh or sculpt asset {0} of prim {1} at {2}" ,
omvPrim . Sculpt . SculptTexture . ToString ( ) , prim . Name , prim . GetWorldPosition ( ) . ToString ( ) ) ;
}
2017-10-07 21:35:42 +00:00
}
}
// If not a mesh or sculptie, try the regular mesher
if ( renderMesh = = null )
{
2020-01-09 14:54:24 +00:00
renderMesh = m_primMesher . GenerateFacetedMesh ( omvPrim , lod ) ;
2017-10-07 21:35:42 +00:00
}
if ( renderMesh = = null )
return ;
string primID = prim . UUID . ToString ( ) ;
// Create the prim faces
// TODO: Implement the useTextures flag behavior
for ( int i = 0 ; i < renderMesh . Faces . Count ; i + + )
{
Face face = renderMesh . Faces [ i ] ;
string meshName = primID + i . ToString ( ) ;
// Avoid adding duplicate meshes to the scene
if ( renderer . Scene . objectData . ContainsKey ( meshName ) )
continue ;
warp_Object faceObj = new warp_Object ( ) ;
2020-01-09 14:54:24 +00:00
Primitive . TextureEntryFace teFace = prim . Shape . Textures . GetFace ( ( uint ) i ) ;
Color4 faceColor = teFace . RGBA ;
if ( faceColor . A = = 0 )
continue ;
string materialName = String . Empty ;
if ( m_texturePrims )
{
// if(lod > DetailLevel.Low)
{
// materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID, lod == DetailLevel.Low);
materialName = GetOrCreateMaterial ( renderer , faceColor , teFace . TextureID , false , prim ) ;
if ( String . IsNullOrEmpty ( materialName ) )
continue ;
int c = renderer . Scene . material ( materialName ) . getColor ( ) ;
if ( ( c & warp_Color . MASKALPHA ) = = 0 )
continue ;
}
}
else
materialName = GetOrCreateMaterial ( renderer , faceColor ) ;
if ( renderer . Scene . material ( materialName ) . getTexture ( ) = = null )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
// uv map details dont not matter for color;
for ( int j = 0 ; j < face . Vertices . Count ; j + + )
{
Vertex v = face . Vertices [ j ] ;
warp_Vector pos = ConvertVector ( v . Position ) ;
warp_Vertex vert = new warp_Vertex ( pos , v . TexCoord . X , v . TexCoord . Y ) ;
faceObj . addVertex ( vert ) ;
}
}
else
{
float tu ;
float tv ;
float offsetu = teFace . OffsetU + 0.5f ;
float offsetv = teFace . OffsetV + 0.5f ;
float scaleu = teFace . RepeatU ;
float scalev = teFace . RepeatV ;
float rotation = teFace . Rotation ;
float rc = 0 ;
float rs = 0 ;
if ( rotation ! = 0 )
{
rc = ( float ) Math . Cos ( rotation ) ;
rs = ( float ) Math . Sin ( rotation ) ;
}
for ( int j = 0 ; j < face . Vertices . Count ; j + + )
{
warp_Vertex vert ;
Vertex v = face . Vertices [ j ] ;
warp_Vector pos = ConvertVector ( v . Position ) ;
if ( teFace . TexMapType = = MappingType . Planar )
UVPlanarMap ( v , prim . Scale , out tu , out tv ) ;
else
{
tu = v . TexCoord . X - 0.5f ;
tv = 0.5f - v . TexCoord . Y ;
}
if ( rotation ! = 0 )
{
float tur = tu * rc - tv * rs ;
float tvr = tu * rs + tv * rc ;
tur * = scaleu ;
tur + = offsetu ;
tvr * = scalev ;
tvr + = offsetv ;
vert = new warp_Vertex ( pos , tur , tvr ) ;
}
else
{
tu * = scaleu ;
tu + = offsetu ;
tv * = scalev ;
tv + = offsetv ;
vert = new warp_Vertex ( pos , tu , tv ) ;
}
faceObj . addVertex ( vert ) ;
}
2017-10-07 21:35:42 +00:00
}
for ( int j = 0 ; j < face . Indices . Count ; j + = 3 )
{
faceObj . addTriangle (
face . Indices [ j + 0 ] ,
face . Indices [ j + 1 ] ,
face . Indices [ j + 2 ] ) ;
}
2020-01-09 14:54:24 +00:00
faceObj . scaleSelf ( prim . Scale . X , prim . Scale . Z , prim . Scale . Y ) ;
2017-10-07 21:35:42 +00:00
faceObj . transform ( m ) ;
faceObj . setPos ( primPos ) ;
renderer . Scene . addObject ( meshName , faceObj ) ;
renderer . SetObjectMaterial ( meshName , materialName ) ;
}
}
2020-01-09 14:54:24 +00:00
private int GetFaceColor ( Primitive . TextureEntryFace face )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
int color ;
Color4 ctmp = Color4 . White ;
2017-10-07 21:35:42 +00:00
if ( face . TextureID = = UUID . Zero )
2020-01-09 14:54:24 +00:00
return warp_Color . White ;
2017-10-07 21:35:42 +00:00
if ( ! m_colors . TryGetValue ( face . TextureID , out color ) )
{
bool fetched = false ;
// Attempt to fetch the texture metadata
2020-01-09 14:54:24 +00:00
string cacheName = "MAPCLR" + face . TextureID . ToString ( ) ;
AssetBase metadata = m_scene . AssetService . GetCached ( cacheName ) ;
2017-10-07 21:35:42 +00:00
if ( metadata ! = null )
{
OSDMap map = null ;
try { map = OSDParser . Deserialize ( metadata . Data ) as OSDMap ; } catch { }
if ( map ! = null )
{
2020-01-09 14:54:24 +00:00
ctmp = map [ "X-RGBA" ] . AsColor4 ( ) ;
2017-10-07 21:35:42 +00:00
fetched = true ;
}
}
if ( ! fetched )
{
// Fetch the texture, decode and get the average color,
// then save it to a temporary metadata asset
AssetBase textureAsset = m_scene . AssetService . Get ( face . TextureID . ToString ( ) ) ;
if ( textureAsset ! = null )
{
int width , height ;
2020-01-09 14:54:24 +00:00
ctmp = GetAverageColor ( textureAsset . FullID , textureAsset . Data , out width , out height ) ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
OSDMap data = new OSDMap { { "X-RGBA" , OSD . FromColor4 ( ctmp ) } } ;
2017-10-07 21:35:42 +00:00
metadata = new AssetBase
{
Data = System . Text . Encoding . UTF8 . GetBytes ( OSDParser . SerializeJsonString ( data ) ) ,
2020-01-09 14:54:24 +00:00
Description = "Metadata for texture color" + face . TextureID . ToString ( ) ,
2017-10-07 21:35:42 +00:00
Flags = AssetFlags . Collectable ,
2020-01-09 14:54:24 +00:00
FullID = UUID . Zero ,
ID = cacheName ,
2017-10-07 21:35:42 +00:00
Local = true ,
Temporary = true ,
Name = String . Empty ,
Type = ( sbyte ) AssetType . Unknown
} ;
m_scene . AssetService . Store ( metadata ) ;
}
else
{
2020-01-09 14:54:24 +00:00
ctmp = new Color4 ( 0.5f , 0.5f , 0.5f , 1.0f ) ;
2017-10-07 21:35:42 +00:00
}
}
2020-01-09 14:54:24 +00:00
color = ConvertColor ( ctmp ) ;
2017-10-07 21:35:42 +00:00
m_colors [ face . TextureID ] = color ;
}
2020-01-09 14:54:24 +00:00
return color ;
2017-10-07 21:35:42 +00:00
}
private string GetOrCreateMaterial ( WarpRenderer renderer , Color4 color )
{
string name = color . ToString ( ) ;
warp_Material material = renderer . Scene . material ( name ) ;
if ( material ! = null )
return name ;
renderer . AddMaterial ( name , ConvertColor ( color ) ) ;
return name ;
}
2020-01-09 14:54:24 +00:00
public string GetOrCreateMaterial ( WarpRenderer renderer , Color4 faceColor , UUID textureID , bool useAverageTextureColor , SceneObjectPart sop )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
int color = ConvertColor ( faceColor ) ;
string idstr = textureID . ToString ( ) + color . ToString ( ) ;
string materialName = "MAPMAT" + idstr ;
if ( renderer . Scene . material ( materialName ) ! = null )
return materialName ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
warp_Material mat = new warp_Material ( ) ;
warp_Texture texture = GetTexture ( textureID , sop ) ;
if ( texture ! = null )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
if ( useAverageTextureColor )
color = warp_Color . multiply ( color , texture . averageColor ) ;
else
mat . setTexture ( texture ) ;
2017-10-07 21:35:42 +00:00
}
2020-01-09 14:54:24 +00:00
else
color = warp_Color . multiply ( color , warp_Color . Grey ) ;
mat . setColor ( color ) ;
renderer . Scene . addMaterial ( materialName , mat ) ;
2017-10-07 21:35:42 +00:00
return materialName ;
}
2020-01-09 14:54:24 +00:00
private warp_Texture GetTexture ( UUID id , SceneObjectPart sop )
2017-10-07 21:35:42 +00:00
{
warp_Texture ret = null ;
2020-01-09 14:54:24 +00:00
if ( id = = UUID . Zero )
return ret ;
if ( m_warpTextures . TryGetValue ( id . ToString ( ) , out ret ) )
return ret ;
2017-10-07 21:35:42 +00:00
byte [ ] asset = m_scene . AssetService . GetData ( id . ToString ( ) ) ;
if ( asset ! = null )
{
try
{
2020-01-09 14:54:24 +00:00
using ( Bitmap img = ( Bitmap ) m_imgDecoder . DecodeToImage ( asset ) )
ret = new warp_Texture ( img , 8 ) ; // reduce textures size to 256x256
2017-10-07 21:35:42 +00:00
}
catch ( Exception e )
{
2020-01-09 14:54:24 +00:00
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 ) ;
2017-10-07 21:35:42 +00:00
}
}
2020-01-09 14:54:24 +00:00
else
m_log . WarnFormat ( "[Warp3D]: missing texture {0} data for prim {1} at {2}" ,
id . ToString ( ) , sop . Name , sop . GetWorldPosition ( ) . ToString ( ) ) ;
2018-10-16 15:36:59 +00:00
2020-01-09 14:54:24 +00:00
m_warpTextures [ id . ToString ( ) ] = ret ;
2017-10-07 21:35:42 +00:00
return ret ;
}
#endregion Rendering Methods
#region Static Helpers
// Note: axis change.
private static warp_Vector ConvertVector ( float x , float y , float z )
{
return new warp_Vector ( x , z , y ) ;
}
private static warp_Vector ConvertVector ( Vector3 vector )
{
return new warp_Vector ( vector . X , vector . Z , vector . Y ) ;
}
private static warp_Quaternion ConvertQuaternion ( Quaternion quat )
{
return new warp_Quaternion ( quat . X , quat . Z , quat . Y , - quat . W ) ;
}
private static int ConvertColor ( Color4 color )
{
2020-01-09 14:54:24 +00:00
int c = warp_Color . getColor ( ( byte ) ( color . R * 255f ) , ( byte ) ( color . G * 255f ) , ( byte ) ( color . B * 255f ) , ( byte ) ( color . A * 255f ) ) ;
2017-10-07 21:35:42 +00:00
return c ;
}
private static Vector3 SurfaceNormal ( Vector3 c1 , Vector3 c2 , Vector3 c3 )
{
Vector3 edge1 = new Vector3 ( c2 . X - c1 . X , c2 . Y - c1 . Y , c2 . Z - c1 . Z ) ;
Vector3 edge2 = new Vector3 ( c3 . X - c1 . X , c3 . Y - c1 . Y , c3 . Z - c1 . Z ) ;
Vector3 normal = Vector3 . Cross ( edge1 , edge2 ) ;
normal . Normalize ( ) ;
return normal ;
}
2020-01-09 14:54:24 +00:00
public Color4 GetAverageColor ( UUID textureID , byte [ ] j2kData , out int width , out int height )
2017-10-07 21:35:42 +00:00
{
ulong r = 0 ;
ulong g = 0 ;
ulong b = 0 ;
ulong a = 0 ;
2020-01-09 14:54:24 +00:00
int pixelBytes ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
try
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
using ( MemoryStream stream = new MemoryStream ( j2kData ) )
using ( Bitmap bitmap = ( Bitmap ) J2kImage . FromStream ( stream ) )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
width = bitmap . Width ;
height = bitmap . Height ;
2018-10-16 15:36:59 +00:00
2020-01-09 14:54:24 +00:00
BitmapData bitmapData = bitmap . LockBits ( new Rectangle ( 0 , 0 , width , height ) , ImageLockMode . ReadOnly , bitmap . PixelFormat ) ;
pixelBytes = ( bitmap . PixelFormat = = PixelFormat . Format24bppRgb ) ? 3 : 4 ;
2018-10-16 15:36:59 +00:00
2020-01-09 14:54:24 +00:00
// Sum up the individual channels
unsafe
{
if ( pixelBytes = = 4 )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
for ( int y = 0 ; y < height ; y + + )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
byte * row = ( byte * ) bitmapData . Scan0 + ( y * bitmapData . Stride ) ;
for ( int x = 0 ; x < width ; x + + )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
b + = row [ x * pixelBytes + 0 ] ;
g + = row [ x * pixelBytes + 1 ] ;
r + = row [ x * pixelBytes + 2 ] ;
a + = row [ x * pixelBytes + 3 ] ;
2017-10-07 21:35:42 +00:00
}
}
2020-01-09 14:54:24 +00:00
}
else
{
for ( int y = 0 ; y < height ; y + + )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
byte * row = ( byte * ) bitmapData . Scan0 + ( y * bitmapData . Stride ) ;
for ( int x = 0 ; x < width ; x + + )
2017-10-07 21:35:42 +00:00
{
2020-01-09 14:54:24 +00:00
b + = row [ x * pixelBytes + 0 ] ;
g + = row [ x * pixelBytes + 1 ] ;
r + = row [ x * pixelBytes + 2 ] ;
2017-10-07 21:35:42 +00:00
}
}
}
}
2020-01-09 14:54:24 +00:00
}
// Get the averages for each channel
const decimal OO_255 = 1 m / 255 m ;
decimal totalPixels = ( decimal ) ( width * height ) ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
decimal rm = ( ( decimal ) r / totalPixels ) * OO_255 ;
decimal gm = ( ( decimal ) g / totalPixels ) * OO_255 ;
decimal bm = ( ( decimal ) b / totalPixels ) * OO_255 ;
decimal am = ( ( decimal ) a / totalPixels ) * OO_255 ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
if ( pixelBytes = = 3 )
am = 1 m ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
return new Color4 ( ( float ) rm , ( float ) gm , ( float ) bm , ( float ) am ) ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
}
catch ( Exception ex )
{
m_log . WarnFormat (
"[WARP 3D IMAGE MODULE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}" ,
textureID , j2kData . Length , ex . Message ) ;
2017-10-07 21:35:42 +00:00
2020-01-09 14:54:24 +00:00
width = 0 ;
height = 0 ;
return new Color4 ( 0.5f , 0.5f , 0.5f , 1.0f ) ;
2017-10-07 21:35:42 +00:00
}
}
#endregion Static Helpers
}
public static class ImageUtils
{
/// <summary>
/// Performs bilinear interpolation between four values
/// </summary>
/// <param name="v00">First, or top left value</param>
/// <param name="v01">Second, or top right value</param>
/// <param name="v10">Third, or bottom left value</param>
/// <param name="v11">Fourth, or bottom right value</param>
/// <param name="xPercent">Interpolation value on the X axis, between 0.0 and 1.0</param>
/// <param name="yPercent">Interpolation value on fht Y axis, between 0.0 and 1.0</param>
/// <returns>The bilinearly interpolated result</returns>
public static float Bilinear ( float v00 , float v01 , float v10 , float v11 , float xPercent , float yPercent )
{
return Utils . Lerp ( Utils . Lerp ( v00 , v01 , xPercent ) , Utils . Lerp ( v10 , v11 , xPercent ) , yPercent ) ;
}
}
2018-10-16 15:36:59 +00:00
}