summaryrefslogtreecommitdiff
path: root/bitmap/float_bm4.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /bitmap/float_bm4.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'bitmap/float_bm4.cpp')
-rw-r--r--bitmap/float_bm4.cpp350
1 files changed, 350 insertions, 0 deletions
diff --git a/bitmap/float_bm4.cpp b/bitmap/float_bm4.cpp
new file mode 100644
index 0000000..7130b27
--- /dev/null
+++ b/bitmap/float_bm4.cpp
@@ -0,0 +1,350 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include <tier0/platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include "bitmap/float_bm.h"
+#include "vstdlib/vstdlib.h"
+#include "raytrace.h"
+#include "mathlib/bumpvects.h"
+#include "mathlib/halton.h"
+#include "tier0/threadtools.h"
+#include "tier0/progressbar.h"
+
+// In order to handle intersections with wrapped copies, we repeat the bitmap triangles this many
+// times
+#define NREPS_TILE 1
+extern int n_intersection_calculations;
+
+
+
+struct SSBumpCalculationContext // what each thread needs to see
+{
+ RayTracingEnvironment *m_pRtEnv;
+ FloatBitMap_t *ret_bm; // the bitmnap we are building
+ FloatBitMap_t const *src_bm;
+ int nrays_to_trace_per_pixel;
+ float bump_scale;
+ Vector *trace_directions; // light source directions to trace
+ Vector *normals;
+ int min_y; // range of scanlines to computer for
+ int max_y;
+ uint32 m_nOptionFlags;
+ int thread_number;
+};
+
+
+static unsigned SSBumpCalculationThreadFN( void * ctx1 )
+{
+ SSBumpCalculationContext *ctx = ( SSBumpCalculationContext * ) ctx1;
+
+ RayStream ray_trace_stream_ctx;
+
+ RayTracingSingleResult * rslts = new
+ RayTracingSingleResult[ctx->ret_bm->Width * ctx->nrays_to_trace_per_pixel];
+
+
+ for( int y = ctx->min_y; y <= ctx->max_y; y++ )
+ {
+ if ( ctx->thread_number == 0 )
+ ReportProgress("Computing output",(1+ctx->max_y-ctx->min_y),y-ctx->min_y);
+ for( int r = 0; r < ctx->nrays_to_trace_per_pixel; r++ )
+ {
+ for( int x = 0; x < ctx->ret_bm->Width; x++ )
+ {
+ Vector surf_pnt( x, y, ctx->bump_scale * ctx->src_bm->Pixel( x, y, 3 ) );
+ // move the ray origin up a hair
+ surf_pnt.z += 0.55;
+ Vector trace_end = surf_pnt;
+ Vector trace_dir = ctx->trace_directions[ r ];
+ trace_dir *= ( 1 + NREPS_TILE * 2 ) * max( ctx->src_bm->Width, ctx->src_bm->Height );
+ trace_end += trace_dir;
+ ctx->m_pRtEnv->AddToRayStream( ray_trace_stream_ctx, surf_pnt, trace_end,
+ & ( rslts[ r + ctx->nrays_to_trace_per_pixel * ( x )] ));
+ }
+ }
+ if ( ctx->nrays_to_trace_per_pixel )
+ ctx->m_pRtEnv->FinishRayStream( ray_trace_stream_ctx );
+ // now, all ray tracing results are in the results buffer. Determine the visible self-shadowed
+ // bump map lighting at each vertex in each basis direction
+ for( int x = 0; x < ctx->src_bm->Width; x++ )
+ {
+ int nNumChannels = ( ctx->m_nOptionFlags & SSBUMP_OPTION_NONDIRECTIONAL ) ? 1 : 3;
+ for( int c = 0; c < nNumChannels; c++ )
+ {
+ float sum_dots = 0;
+ float sum_possible_dots = 0;
+ Vector ldir = g_localBumpBasis[c];
+ float ndotl = DotProduct( ldir, ctx->normals[x + y * ctx->src_bm->Width] );
+ if ( ndotl < 0 )
+ ctx->ret_bm->Pixel( x, y, c ) = 0;
+ else
+ {
+ if ( ctx->nrays_to_trace_per_pixel )
+ {
+ RayTracingSingleResult *this_rslt =
+ rslts + ctx->nrays_to_trace_per_pixel * ( x );
+ for( int r = 0; r < ctx->nrays_to_trace_per_pixel; r++ )
+ {
+ float dot;
+ if ( ctx->m_nOptionFlags & SSBUMP_OPTION_NONDIRECTIONAL )
+ dot = ctx->trace_directions[r].z;
+ else
+ dot = DotProduct( ldir, ctx->trace_directions[r] );
+ if ( dot > 0 )
+ {
+ sum_possible_dots += dot;
+ if ( this_rslt[r].HitID == - 1 )
+ sum_dots += dot;
+ }
+ }
+ }
+ else
+ {
+ sum_dots = sum_possible_dots = 1.0;
+ }
+ ctx->ret_bm->Pixel( x, y, c ) = ( ndotl * sum_dots ) / sum_possible_dots;
+ }
+ }
+ if ( ctx->m_nOptionFlags & SSBUMP_OPTION_NONDIRECTIONAL )
+ {
+ ctx->ret_bm->Pixel( x, y, 1 ) = ctx->ret_bm->Pixel( x, y, 0 ); // copy height
+ ctx->ret_bm->Pixel( x, y, 2 ) = ctx->ret_bm->Pixel( x, y, 0 ); // copy height
+ ctx->ret_bm->Pixel( x, y, 3 ) = ctx->ret_bm->Pixel( x, y, 0 ); // copy height
+ }
+ else
+ {
+ ctx->ret_bm->Pixel( x, y, 3 ) = ctx->src_bm->Pixel( x, y, 3 ); // copy height
+ }
+ }
+ }
+ delete[] rslts;
+ return 0;
+}
+
+void FloatBitMap_t::ComputeVertexPositionsAndNormals( float flHeightScale, Vector **ppPosOut, Vector **ppNormalOut ) const
+{
+ Vector *verts = new Vector[Width * Height];
+ // first, calculate vertex positions
+ for( int y = 0; y < Height; y++ )
+ for( int x = 0; x < Width; x++ )
+ {
+ Vector * out = verts + x + y * Width;
+ out->x = x;
+ out->y = y;
+ out->z = flHeightScale * Pixel( x, y, 3 );
+ }
+
+ Vector *normals = new Vector[Width * Height];
+ // now, calculate normals, smoothed
+ for( int y = 0; y < Height; y++ )
+ for( int x = 0; x < Width; x++ )
+ {
+ // now, calculcate average normal
+ Vector avg_normal( 0, 0, 0 );
+ for( int xofs =- 1;xofs <= 1;xofs++ )
+ for( int yofs =- 1;yofs <= 1;yofs++ )
+ {
+ int x0 = ( x + xofs );
+ if ( x0 < 0 )
+ x0 += Width;
+ int y0 = ( y + yofs );
+ if ( y0 < 0 )
+ y0 += Height;
+ x0 = x0 % Width;
+ y0 = y0 % Height;
+ int x1 = ( x0 + 1 ) % Width;
+ int y1 = ( y0 + 1 ) % Height;
+ // now, form the two triangles from this vertex
+ Vector p0 = verts[x0 + y0 * Width];
+ Vector e1 = verts[x1 + y0 * Width];
+ e1 -= p0;
+ Vector e2 = verts[x0 + y1 * Width];
+ e2 -= p0;
+ Vector n1;
+ CrossProduct( e1, e2, n1 );
+ if ( n1.z < 0 )
+ n1.Negate();
+ e1 = verts[x + y1 * Width];
+ e1 -= p0;
+ e2 = verts[x1 + y1 * Width];
+ e2 -= p0;
+ Vector n2;
+ CrossProduct( e1, e2, n2 );
+ if ( n2.z < 0 )
+ n2.Negate();
+ n1.NormalizeInPlace();
+ n2.NormalizeInPlace();
+ avg_normal += n1;
+ avg_normal += n2;
+ }
+ avg_normal.NormalizeInPlace();
+ normals[x + y * Width]= avg_normal;
+ }
+ *ppPosOut = verts;
+ *ppNormalOut = normals;
+}
+
+FloatBitMap_t *FloatBitMap_t::ComputeSelfShadowedBumpmapFromHeightInAlphaChannel(
+ float bump_scale, int nrays_to_trace_per_pixel,
+ uint32 nOptionFlags ) const
+{
+
+ // first, add all the triangles from the height map to the "world".
+ // we will make multiple copies to handle wrapping
+ int tcnt = 1;
+
+ Vector * verts;
+ Vector * normals;
+ ComputeVertexPositionsAndNormals( bump_scale, & verts, & normals );
+
+ RayTracingEnvironment rtEnv;
+ rtEnv.Flags |= RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS; // save some ram
+
+ if ( nrays_to_trace_per_pixel )
+ {
+ rtEnv.MakeRoomForTriangles( ( 1 + 2 * NREPS_TILE ) * ( 1 + 2 * NREPS_TILE ) * 2 * Height * Width );
+
+ // now, add a whole mess of triangles to trace against
+ for( int tilex =- NREPS_TILE; tilex <= NREPS_TILE; tilex++ )
+ for( int tiley =- NREPS_TILE; tiley <= NREPS_TILE; tiley++ )
+ {
+ int min_x = 0;
+ int max_x = Width - 1;
+ int min_y = 0;
+ int max_y = Height - 1;
+ if ( tilex < 0 )
+ min_x = Width / 2;
+ if ( tilex > 0 )
+ max_x = Width / 2;
+ if ( tiley < 0 )
+ min_y = Height / 2;
+ if ( tiley > 0 )
+ max_y = Height / 2;
+ for( int y = min_y; y <= max_y; y++ )
+ for( int x = min_x; x <= max_x; x++ )
+ {
+ Vector ofs( tilex * Width, tiley * Height, 0 );
+ int x1 = ( x + 1 ) % Width;
+ int y1 = ( y + 1 ) % Height;
+ Vector v0 = verts[x + y * Width];
+ Vector v1 = verts[x1 + y * Width];
+ Vector v2 = verts[x1 + y1 * Width];
+ Vector v3 = verts[x + y1 * Width];
+ v0.x = x; v0.y = y;
+ v1.x = x + 1; v1.y = y;
+ v2.x = x + 1; v2.y = y + 1;
+ v3.x = x; v3.y = y + 1;
+ v0 += ofs; v1 += ofs; v2 += ofs; v3 += ofs;
+ rtEnv.AddTriangle( tcnt++, v0, v1, v2, Vector( 1, 1, 1 ) );
+ rtEnv.AddTriangle( tcnt++, v0, v3, v2, Vector( 1, 1, 1 ) );
+ }
+ }
+ //printf("added %d triangles\n",tcnt-1);
+ ReportProgress("Creating kd-tree",0,0);
+ rtEnv.SetupAccelerationStructure();
+ // ok, now we have built a structure for ray intersection. we will take advantage
+ // of the SSE ray tracing code by intersecting rays as a batch.
+ }
+
+ // We need to calculate for each vertex (i.e. pixel) of the heightmap, how "much" of the world
+ // it can see in each basis direction. we will do this by sampling a sphere of rays around the
+ // vertex, and using dot-product weighting to measure the lighting contribution in each basis
+ // direction. note that the surface normal is not used here. The surface normal will end up
+ // being reflected in the result because of rays being blocked when they try to pass through
+ // the planes of the triangles touching the vertex.
+
+ // note that there is no reason inter-bounced lighting could not be folded into this
+ // calculation.
+
+ FloatBitMap_t * ret = new FloatBitMap_t( Width, Height );
+
+
+ Vector *trace_directions=new Vector[nrays_to_trace_per_pixel];
+ DirectionalSampler_t my_sphere_sampler;
+ for( int r=0; r < nrays_to_trace_per_pixel; r++)
+ {
+ Vector trace_dir=my_sphere_sampler.NextValue();
+// trace_dir=Vector(1,0,0);
+ trace_dir.z=fabs(trace_dir.z); // upwards facing only
+ trace_directions[ r ]= trace_dir;
+ }
+
+ volatile SSBumpCalculationContext ctxs[32];
+ ctxs[0].m_pRtEnv =& rtEnv;
+ ctxs[0].ret_bm = ret;
+ ctxs[0].src_bm = this;
+ ctxs[0].nrays_to_trace_per_pixel = nrays_to_trace_per_pixel;
+ ctxs[0].bump_scale = bump_scale;
+ ctxs[0].trace_directions = trace_directions;
+ ctxs[0].normals = normals;
+ ctxs[0].min_y = 0;
+ ctxs[0].max_y = Height - 1;
+ ctxs[0].m_nOptionFlags = nOptionFlags;
+ int nthreads = min( 32, (int)GetCPUInformation()->m_nPhysicalProcessors );
+
+ ThreadHandle_t waithandles[32];
+ int starty = 0;
+ int ystep = Height / nthreads;
+ for( int t = 0;t < nthreads; t++ )
+ {
+ if ( t )
+ memcpy( (void * ) ( & ctxs[t] ), ( void * ) & ctxs[0], sizeof( ctxs[0] ));
+ ctxs[t].thread_number = t;
+ ctxs[t].min_y = starty;
+ if ( t != nthreads - 1 )
+ ctxs[t].max_y = min( Height - 1, starty + ystep - 1 );
+ else
+ ctxs[t].max_y = Height - 1;
+ waithandles[t]= CreateSimpleThread( SSBumpCalculationThreadFN, ( SSBumpCalculationContext * ) & ctxs[t] );
+ starty += ystep;
+ }
+ for(int t=0;t<nthreads;t++)
+ {
+ ThreadJoin( waithandles[t] );
+ }
+ if ( nOptionFlags & SSBUMP_MOD2X_DETAIL_TEXTURE )
+ {
+ const float flOutputScale = 0.5 * ( 1.0 / .57735026 ); // normalize so that a flat normal yields 0.5
+ // scale output weights by color channel
+ for( int nY = 0; nY < Height; nY++ )
+ for( int nX = 0; nX < Width; nX++ )
+ {
+ float flScale = flOutputScale * (2.0/3.0) * ( Pixel( nX, nY, 0 ) + Pixel( nX, nY, 1 ) + Pixel( nX, nY, 2 ) );
+ ret->Pixel( nX, nY, 0 ) *= flScale;
+ ret->Pixel( nX, nY, 1 ) *= flScale;
+ ret->Pixel( nX, nY, 2 ) *= flScale;
+ }
+ }
+
+ delete[] verts;
+ delete[] trace_directions;
+ delete[] normals;
+ return ret; // destructor will clean up rtenv
+}
+
+// generate a conventional normal map from a source with height stored in alpha.
+FloatBitMap_t *FloatBitMap_t::ComputeBumpmapFromHeightInAlphaChannel( float bump_scale ) const
+{
+ Vector *verts;
+ Vector *normals;
+ ComputeVertexPositionsAndNormals( bump_scale, &verts, &normals );
+ FloatBitMap_t *ret=new FloatBitMap_t( Width, Height );
+ for( int y = 0; y < Height; y++ )
+ for( int x = 0; x < Width; x++ )
+ {
+ Vector const & N = normals[ x + y * Width ];
+ ret->Pixel( x, y, 0 ) = 0.5+ 0.5 * N.x;
+ ret->Pixel( x, y, 1 ) = 0.5+ 0.5 * N.y;
+ ret->Pixel( x, y, 2 ) = 0.5+ 0.5 * N.z;
+ ret->Pixel( x, y, 3 ) = Pixel( x, y, 3 );
+ }
+ return ret;
+}
+