From 464dcc3764f42fd7620bc261b844bf79cfd07768 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Tue, 14 May 2013 21:11:40 +0000 Subject: [PATCH] Noise3D generator is now using linear upscaling Measured 30% performance increase. git-svn-id: http://mc-server.googlecode.com/svn/trunk@1482 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Generating/BioGen.cpp | 12 +- source/Generating/CompoGen.cpp | 4 +- source/Generating/DistortedHeightmap.cpp | 6 +- source/Generating/HeiGen.cpp | 2 +- source/Generating/Noise3DGenerator.cpp | 94 ++++++++----- source/Generating/Noise3DGenerator.h | 13 +- source/Generating/StructGen.cpp | 4 +- source/LinearUpscale.h | 162 ++++++++++++++++++++++- 8 files changed, 241 insertions(+), 56 deletions(-) diff --git a/source/Generating/BioGen.cpp b/source/Generating/BioGen.cpp index 1afa93e07..4345852c6 100644 --- a/source/Generating/BioGen.cpp +++ b/source/Generating/BioGen.cpp @@ -340,8 +340,8 @@ void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::B Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z]); } - ArrayLinearUpscale2DInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); - ArrayLinearUpscale2DInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); for (int z = 0; z < cChunkDef::Width; z++) { @@ -447,8 +447,8 @@ void cBioGenMultiStepMap::DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cC { Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z], DistortSize); } - ArrayLinearUpscale2DInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); - ArrayLinearUpscale2DInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); // Prepare a 9x9 area of neighboring cell seeds // (assuming that 7x7 cell area is larger than a chunk being generated) @@ -621,8 +621,8 @@ void cBioGenMultiStepMap::BuildTemperatureHumidityMaps(int a_ChunkX, int a_Chunk HumidityMap[x + 17 * z] = NoiseH; } // for x } // for z - ArrayLinearUpscale2DInPlace(TemperatureMap, 17, 17, 8, 8); - ArrayLinearUpscale2DInPlace(HumidityMap, 17, 17, 8, 8); + LinearUpscale2DArrayInPlace(TemperatureMap, 17, 17, 8, 8); + LinearUpscale2DArrayInPlace(HumidityMap, 17, 17, 8, 8); // Re-map into integral values in [0 .. 255] range: for (int idx = 0; idx < ARRAYCOUNT(a_TemperatureMap); idx++) diff --git a/source/Generating/CompoGen.cpp b/source/Generating/CompoGen.cpp index 432616a36..b99919e0d 100644 --- a/source/Generating/CompoGen.cpp +++ b/source/Generating/CompoGen.cpp @@ -442,7 +442,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc) m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) / 256; } // for x, z - FloorLo[] - ArrayLinearUpscale2DInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); + LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); // Interpolate segments: for (int Segment = 0; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) @@ -455,7 +455,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc) m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / 256; } // for x, z - FloorLo[] - ArrayLinearUpscale2DInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); + LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); // Interpolate between FloorLo and FloorHi: for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) diff --git a/source/Generating/DistortedHeightmap.cpp b/source/Generating/DistortedHeightmap.cpp index b90a30e45..9d60c0a92 100644 --- a/source/Generating/DistortedHeightmap.cpp +++ b/source/Generating/DistortedHeightmap.cpp @@ -158,7 +158,7 @@ void cDistortedHeightmap::GenerateHeightArray(void) CurFloor[idx + x * INTERPOL_X] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5; } // for x } // for z - ArrayLinearUpscale2DInPlace(CurFloor, 17, 17, INTERPOL_X, INTERPOL_Z); + LinearUpscale2DArrayInPlace(CurFloor, 17, 17, INTERPOL_X, INTERPOL_Z); } // for y // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis @@ -381,8 +381,8 @@ void cDistortedHeightmap::UpdateDistortAmps(void) GetDistortAmpsAt(Biomes, x, z, m_DistortAmpX[x + 17 * z], m_DistortAmpZ[x + 17 * z]); } } - ArrayLinearUpscale2DInPlace(m_DistortAmpX, 17, 17, STEPX, STEPZ); - ArrayLinearUpscale2DInPlace(m_DistortAmpZ, 17, 17, STEPX, STEPZ); + LinearUpscale2DArrayInPlace(m_DistortAmpX, 17, 17, STEPX, STEPZ); + LinearUpscale2DArrayInPlace(m_DistortAmpZ, 17, 17, STEPX, STEPZ); } diff --git a/source/Generating/HeiGen.cpp b/source/Generating/HeiGen.cpp index 61f8b4cd7..2aa942c73 100644 --- a/source/Generating/HeiGen.cpp +++ b/source/Generating/HeiGen.cpp @@ -266,7 +266,7 @@ void cHeiGenBiomal::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMa Height[x + 17 * z] = GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes); } } - ArrayLinearUpscale2DInPlace(Height, 17, 17, STEPX, STEPZ); + LinearUpscale2DArrayInPlace(Height, 17, 17, STEPX, STEPZ); // Copy into the heightmap for (int z = 0; z < cChunkDef::Width; z++) diff --git a/source/Generating/Noise3DGenerator.cpp b/source/Generating/Noise3DGenerator.cpp index dbb577827..42a0d70d9 100644 --- a/source/Generating/Noise3DGenerator.cpp +++ b/source/Generating/Noise3DGenerator.cpp @@ -31,7 +31,7 @@ void Debug3DNoise(NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_Size unsigned char buf[BUF_SIZE]; for (int x = 0; x < a_SizeX; x++) { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); } f1.Write(buf, a_SizeX); } // for y @@ -52,7 +52,7 @@ void Debug3DNoise(NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_Size unsigned char buf[BUF_SIZE]; for (int x = 0; x < a_SizeX; x++) { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); } f2.Write(buf, a_SizeX); } // for z @@ -69,6 +69,37 @@ void Debug3DNoise(NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_Size +/* +// Perform an automatic test of upscaling upon program start (use breakpoints to debug): + +class Test +{ +public: + Test(void) + { + DoTest1(); + } + + + void DoTest1(void) + { + float In[3 * 3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + Debug3DNoise(In, 3, 3, 3, "Upscale in"); + float Out[17 * 33 * 35]; + LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17); + Debug3DNoise(Out, 17, 33, 35, "Upscale test"); + } +} gTest; +//*/ + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cNoise3DGenerator: @@ -190,15 +221,15 @@ void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::Bi void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) { - NOISE_DATATYPE Noise[cChunkDef::Width * cChunkDef::Height * cChunkDef::Width]; + NOISE_DATATYPE Noise[17 * 257 * 17]; GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise); // Output noise into chunk: - int idx = 0; for (int z = 0; z < cChunkDef::Width; z++) { for (int y = 0; y < cChunkDef::Height; y++) { + int idx = z * 17 * 257 + y * 17; for (int x = 0; x < cChunkDef::Width; x++) { NOISE_DATATYPE n = Noise[idx++]; @@ -226,8 +257,8 @@ void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_Ch void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise) { - NOISE_DATATYPE NoiseO[DIMX * DIMY * DIMZ]; // Output for the Perlin noise - NOISE_DATATYPE NoiseW[DIMX * DIMY * DIMZ]; // Workspace that the noise calculation can use and trash + NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise + NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed" NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX; @@ -237,51 +268,42 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT NOISE_DATATYPE StartY = 0; NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY; - m_Perlin.Generate3D(NoiseO, DIMX, DIMY, DIMZ, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW); + m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW); - // DEBUG: Debug3DNoise(NoiseO, DIMX, DIMY, DIMZ, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ)); - - // Linearly interpolate the Perlin noise into full-blown chunk dimensions: - LinearInterpolate3DArray( - NoiseO, DIMX, DIMY, DIMZ, - a_OutNoise, cChunkDef::Width, cChunkDef::Height, cChunkDef::Width - ); - - // DEBUG: Debug3DNoise(a_OutNoise, cChunkDef::Width, cChunkDef::Height, cChunkDef::Width, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ)); - - // Modify the noise to account for the wanted elevation: + // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ)); // Precalculate a "height" array: - NOISE_DATATYPE Test1 = 0; - NOISE_DATATYPE HeightS[DIMX * DIMZ]; // Output for the cubic noise heightmap ("source") - NOISE_DATATYPE Test2 = 0; - NOISE_DATATYPE Height[cChunkDef::Width * cChunkDef::Width]; // Lerp-ed heightmap [x + Width * z] - m_Cubic.Generate2D(HeightS, DIMX, DIMZ, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25); - LinearInterpolate2DArray( - HeightS, DIMX, DIMZ, - Height, cChunkDef::Width, cChunkDef::Width - ); + NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source") + m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25); for (int i = 0; i < ARRAYCOUNT(Height); i++) { Height[i] = abs(Height[i]) * m_HeightAmplification + 1; } - // Modify noise by height data - for (int y = 0; y < cChunkDef::Height; y++) + // Modify the noise by height data: + for (int y = 0; y < DIM_Y; y++) { - NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20; + NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20; AddHeight *= AddHeight * AddHeight; - for (int z = 0; z < cChunkDef::Width; z++) + for (int z = 0; z < DIM_Z; z++) { - NOISE_DATATYPE * CurRow = &(a_OutNoise[y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height]); - for (int x = 0; x < cChunkDef::Width; x++) + NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]); + for (int x = 0; x < DIM_X; x++) { - CurRow[x] += AddHeight / Height[x + cChunkDef::Width * z]; + CurRow[x] += AddHeight / Height[x + DIM_X * z]; } } } + + // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ)); - // DEBUG: Debug3DNoise(a_OutNoise, cChunkDef::Width, cChunkDef::Height, cChunkDef::Width, Printf("Chunk_%d_%d", a_ChunkX, a_ChunkZ); + // Upscale the Perlin noise into full-blown chunk dimensions: + LinearUpscale3DArray( + NoiseO, DIM_X, DIM_Y, DIM_Z, + a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z + ); + + // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ)); } @@ -444,7 +466,7 @@ void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ) } } // Linear-interpolate this XZ floor: - ArrayLinearUpscale2DInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z); + LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z); } // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis diff --git a/source/Generating/Noise3DGenerator.h b/source/Generating/Noise3DGenerator.h index 57cf024f8..b23e8645b 100644 --- a/source/Generating/Noise3DGenerator.h +++ b/source/Generating/Noise3DGenerator.h @@ -29,10 +29,15 @@ public: virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; protected: - // Linear interpolation dimensions: - static const int DIMX = 5; - static const int DIMY = 65; - static const int DIMZ = 5; + // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: + static const int UPSCALE_X = 8; + static const int UPSCALE_Y = 4; + static const int UPSCALE_Z = 8; + + // Linear interpolation buffer dimensions, calculated from the step sizes: + static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; + static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; + static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition cCubicNoise m_Cubic; // The noise used for heightmap directing diff --git a/source/Generating/StructGen.cpp b/source/Generating/StructGen.cpp index 505b4fa7a..2180261aa 100644 --- a/source/Generating/StructGen.cpp +++ b/source/Generating/StructGen.cpp @@ -559,7 +559,7 @@ void cStructGenDirectOverhangs::GenStructures(cChunkDesc & a_ChunkDesc) m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) / 256; } // for x, z - FloorLo[] - ArrayLinearUpscale2DInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); + LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); // Interpolate segments: for (int Segment = BaseY; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) @@ -572,7 +572,7 @@ void cStructGenDirectOverhangs::GenStructures(cChunkDesc & a_ChunkDesc) m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / 256; } // for x, z - FloorLo[] - ArrayLinearUpscale2DInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); + LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); // Interpolate between FloorLo and FloorHi: for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) diff --git a/source/LinearUpscale.h b/source/LinearUpscale.h index f8ba45350..60aa005bd 100644 --- a/source/LinearUpscale.h +++ b/source/LinearUpscale.h @@ -26,8 +26,11 @@ Regular upscaling takes two arrays and "moves" the input from src to dst; src is -/// Linearly interpolates values in the array between the equidistant anchor points; universal data type -template void ArrayLinearUpscale2DInPlace( +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works in-place (input is already present at the correct output coords) +*/ +template void LinearUpscale2DArrayInPlace( TYPE * a_Array, int a_SizeX, int a_SizeY, // Dimensions of the array int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction @@ -74,3 +77,158 @@ template void ArrayLinearUpscale2DInPlace( +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works on two arrays, input is packed and output is to be completely constructed. +*/ +template void LinearUpscale2DArray( + TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY + int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array + TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) + int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction +) +{ + ASSERT(a_Src != NULL); + ASSERT(a_Dst != NULL); + ASSERT(a_SrcSizeX > 0); + ASSERT(a_SrcSizeY > 0); + ASSERT(a_UpscaleX > 0); + ASSERT(a_UpscaleY > 0); + + // First interpolate columns (same-Y) where the anchor points are: + int idx = 0; + for (int y = 0; y < a_SrcSizeY; y++) + { + int DstY = y * a_UpscaleY; + for (int x = 0; x < a_SrcSizeX; x++) + { + int DstX = x * a_UpscaleX; + TYPE StartValue = a_Src[idx]; // [x, y] + TYPE EndValue = a_Src[idx + a_SrcSizeX]; // [x, y + 1] + TYPE Diff = EndValue - StartValue; + for (int CellY = 0; CellY <= a_UpscaleY; CellY++) + { + a_Dst[DstX + (DstY + CellY) * a_SizeY] = StartValue + Diff * CellY / a_AnchorStepY; + } // for CellY + } // for x + } // for y + + // Now interpolate in rows (same-X), each row already has valid values in the anchor columns + int DstSizeY = a_SizeY * a_UpscaleY; + int DstSizeX = a_SizeX * a_UpscaleX; + for (int y = 0; y < DstSizeY; y++) + { + int Idx = y * DstSizeX; + for (int x = 0; x < a_SizeX; x++) + { + TYPE StartValue = a_Dst[Idx]; // [x, y] in the src coords + TYPE EndValue = a_Dst[Idx + a_UpscaleX]; // [x + 1, y] in the src coords + TYPE Diff = EndValue - StartValue; + for (int CellX = 0; CellX <= a_UpscaleX; CellX++) + { + a_Dst[Idx + CellX] = StartValue + CellX * Diff / a_UpscaleX; + } // for CellY + Idx += a_UpscaleX; + } + } +} + + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works on two arrays, input is packed and output is to be completely constructed. +*/ +template void LinearUpscale3DArray( + TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array + TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1) + int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction +) +{ + // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes + // Feel free to enlarge them if needed, but keep in mind that they're on the stack + const int MAX_UPSCALE_X = 128; + const int MAX_UPSCALE_Y = 128; + const int MAX_UPSCALE_Z = 128; + + ASSERT(a_Src != NULL); + ASSERT(a_Dst != NULL); + ASSERT(a_SrcSizeX > 0); + ASSERT(a_SrcSizeY > 0); + ASSERT(a_SrcSizeZ > 0); + ASSERT(a_UpscaleX > 0); + ASSERT(a_UpscaleY > 0); + ASSERT(a_UpscaleZ > 0); + ASSERT(a_UpscaleX <= MAX_UPSCALE_X); + ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); + ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z); + + // Pre-calculate the upscaling ratios: + TYPE RatioX[MAX_UPSCALE_X]; + TYPE RatioY[MAX_UPSCALE_Y]; + TYPE RatioZ[MAX_UPSCALE_Y]; + for (int x = 0; x <= a_UpscaleX; x++) + { + RatioX[x] = (TYPE)x / a_UpscaleX; + } + for (int y = 0; y <= a_UpscaleY; y++) + { + RatioY[y] = (TYPE)y / a_UpscaleY; + } + for (int z = 0; z <= a_UpscaleZ; z++) + { + RatioZ[z] = (TYPE)z / a_UpscaleZ; + } + + // Interpolate each XYZ cell: + int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; + int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; + int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1; + for (int z = 0; z < (a_SrcSizeZ - 1); z++) + { + int DstZ = z * a_UpscaleZ; + for (int y = 0; y < (a_SrcSizeY - 1); y++) + { + int DstY = y * a_UpscaleY; + int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY; + for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) + { + int DstX = x * a_UpscaleX; + TYPE LoXLoYLoZ = a_Src[idx]; + TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY]; + TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX]; + TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; + TYPE HiXLoYLoZ = a_Src[idx + 1]; + TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY]; + TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX]; + TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; + for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++) + { + TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ]; + TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ]; + TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ]; + TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ]; + for (int CellY = 0; CellY <= a_UpscaleY; CellY++) + { + int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX; + ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ); + TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY]; + TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY]; + for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) + { + a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; + } + } // for CellY + } // for CellZ + } // for x + } // for y + } // for z +} + + + + +