From d2aecea2b30f992af2f1963f998d33e4e45f559b Mon Sep 17 00:00:00 2001 From: Auburn Date: Sun, 21 Apr 2024 20:14:21 +0100 Subject: [PATCH] Add native output bounding for various generator noise types --- .../FastNoise/Generators/BasicGenerators.h | 61 +++++++++----- .../FastNoise/Generators/BasicGenerators.inl | 22 +++-- include/FastNoise/Generators/Blends.h | 2 +- include/FastNoise/Generators/Cellular.h | 28 +++---- include/FastNoise/Generators/Cellular.inl | 83 +++++++++---------- include/FastNoise/Generators/Generator.h | 4 +- include/FastNoise/Generators/Perlin.h | 4 +- include/FastNoise/Generators/Perlin.inl | 23 +++-- include/FastNoise/Generators/Simplex.h | 12 +-- include/FastNoise/Generators/Simplex.inl | 38 ++++++--- include/FastNoise/Generators/Utils.inl | 41 ++++----- include/FastNoise/Generators/Value.h | 4 +- include/FastNoise/Generators/Value.inl | 17 ++-- 13 files changed, 192 insertions(+), 147 deletions(-) diff --git a/include/FastNoise/Generators/BasicGenerators.h b/include/FastNoise/Generators/BasicGenerators.h index 9799a44..40d30dc 100644 --- a/include/FastNoise/Generators/BasicGenerators.h +++ b/include/FastNoise/Generators/BasicGenerators.h @@ -28,6 +28,38 @@ namespace FastNoise }; #endif + template + class VariableRange : public virtual PARENT + { + public: + void SetOutputMin( float value ) + { + mRangeScale += mRangeMin - value; + mRangeMin = value; + } + + void SetOutputMax( float value ) + { + mRangeScale = ( value - mRangeMin ); + } + + protected: + float mRangeMin = -1; + float mRangeScale = 2; + }; + +#ifdef FASTNOISE_METADATA + template + struct MetadataT> : MetadataT + { + MetadataT() + { + this->AddVariable( { "Output Min", "Minimum bound of output range" }, -1.0f, &VariableRange::SetOutputMin ); + this->AddVariable( { "Output Max", "Maximum bound of output range" }, 1.0f, &VariableRange::SetOutputMax ); + } + }; +#endif + class Constant : public virtual Generator { public: @@ -53,7 +85,7 @@ namespace FastNoise }; #endif - class White : public virtual Generator + class White : public virtual VariableRange { public: const Metadata& GetMetadata() const override; @@ -61,7 +93,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; @@ -70,46 +102,33 @@ namespace FastNoise groups.push_back( "Basic Generators" ); description = - "White noise generator\n" - "Outputs between -1.0 and 1.0"; + "White noise generator"; } }; #endif - class Checkerboard : public virtual ScalableGenerator + class Checkerboard : public virtual VariableRange { public: const Metadata& GetMetadata() const override; - - void SetHigh( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mHigh, gen ); } - void SetHigh( float value ) { mHigh = value; } - void SetLow( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mLow, gen ); } - void SetLow( float value ) { mLow = value; } - - protected: - HybridSource mHigh = 1.0f; - HybridSource mLow = -1.0f; }; #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; MetadataT() { groups.push_back( "Basic Generators" ); - this->AddHybridSource( { "High", "Output for \"White\"" }, 1.0f, &Checkerboard::SetHigh, &Checkerboard::SetHigh ); - this->AddHybridSource( { "Low", "Output for \"Black\"" }, -1.0f, &Checkerboard::SetLow, &Checkerboard::SetLow ); - description = "Outputs checkerboard pattern"; } }; #endif - class SineWave : public virtual ScalableGenerator + class SineWave : public virtual VariableRange { public: const Metadata& GetMetadata() const override; @@ -117,7 +136,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; @@ -126,7 +145,7 @@ namespace FastNoise groups.push_back( "Basic Generators" ); description = - "Outputs between -1.0 and 1.0"; + "Outputs sine wave"; } }; #endif diff --git a/include/FastNoise/Generators/BasicGenerators.inl b/include/FastNoise/Generators/BasicGenerators.inl index 4119f6c..604d768 100644 --- a/include/FastNoise/Generators/BasicGenerators.inl +++ b/include/FastNoise/Generators/BasicGenerators.inl @@ -13,6 +13,16 @@ protected: } }; +template +class FastSIMD::DispatchClass, SIMD> : public virtual FastNoise::VariableRange, public FastSIMD::DispatchClass +{ +protected: + FS_FORCEINLINE float32v ScaleOutput( float32v value, float nativeMin, float nativeMax ) const + { + return FS::FMulAdd( float32v( 1.0f / ( nativeMax - nativeMin ) ) * float32v( this->mRangeScale ), value - float32v( nativeMin ), float32v( this->mRangeMin ) ); + } +}; + template class FastSIMD::DispatchClass final : public virtual FastNoise::Constant, public FastSIMD::DispatchClass { @@ -26,7 +36,7 @@ class FastSIMD::DispatchClass final : public virtual }; template -class FastSIMD::DispatchClass final : public virtual FastNoise::White, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::White, public FastSIMD::DispatchClass, SIMD> { FASTNOISE_IMPL_GEN_T; @@ -36,12 +46,12 @@ class FastSIMD::DispatchClass final : public virtual Fas size_t idx = 0; ((pos = FS::Cast( (FS::Cast( pos ) ^ (FS::Cast( pos ) >> 16)) * int32v( Primes::Lookup[idx++] ) )), ...); - return GetValueCoord( seed, FS::Cast( pos )... ); + return this->ScaleOutput( GetValueCoord( seed, FS::Cast( pos )... ), -kValueBounds, kValueBounds ); } }; template -class FastSIMD::DispatchClass final : public virtual FastNoise::Checkerboard, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::Checkerboard, public FastSIMD::DispatchClass, SIMD> { FASTNOISE_IMPL_GEN_T; @@ -52,12 +62,12 @@ class FastSIMD::DispatchClass final : public virt int32v value = (FS::Convert( pos ) ^ ...); - return float32v( 1.0f ) ^ FS::Cast( value << 31 ); + return this->ScaleOutput( FS::Cast( (value & int32v( 1 )) << 30 ), 0, 2 ); } }; template -class FastSIMD::DispatchClass final : public virtual FastNoise::SineWave, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::SineWave, public FastSIMD::DispatchClass, SIMD> { FASTNOISE_IMPL_GEN_T; @@ -66,7 +76,7 @@ class FastSIMD::DispatchClass final : public virtual { this->ScalePositions( pos... ); - return (FS::Sin( pos ) * ...); + return this->ScaleOutput( (FS::Sin( pos ) * ...), -1, 1 ); } }; diff --git a/include/FastNoise/Generators/Blends.h b/include/FastNoise/Generators/Blends.h index a5c1d2d..189b609 100644 --- a/include/FastNoise/Generators/Blends.h +++ b/include/FastNoise/Generators/Blends.h @@ -187,7 +187,7 @@ namespace FastNoise { groups.push_back( "Blends" ); this->AddGeneratorSource( "Value", &PowInt::SetValue ); - this->AddVariable( "Pow", 2, &PowInt::SetPow, 2, INT_MAX ); + this->AddVariable( "Pow", 2, &PowInt::SetPow, 2 ); description = "Faster than PowFloat node but only for int powers"; } diff --git a/include/FastNoise/Generators/Cellular.h b/include/FastNoise/Generators/Cellular.h index 5fad191..246e2c9 100644 --- a/include/FastNoise/Generators/Cellular.h +++ b/include/FastNoise/Generators/Cellular.h @@ -5,7 +5,8 @@ namespace FastNoise { - class Cellular : public virtual ScalableGenerator + template> + class Cellular : public virtual PARENT { public: void SetJitterModifier( SmartNodeArg<> gen ) { this->SetSourceMemberVariable( mJitterModifier, gen ); } @@ -18,20 +19,20 @@ namespace FastNoise }; #ifdef FASTNOISE_METADATA - template<> - struct MetadataT : MetadataT + template + struct MetadataT> : MetadataT { MetadataT() { - groups.push_back( "Coherent Noise" ); - this->AddHybridSource( { "Jitter Modifier", "Above 1.0 will cause grid artifacts" }, 1.0f, &Cellular::SetJitterModifier, &Cellular::SetJitterModifier ); + this->groups.push_back( "Coherent Noise" ); + this->AddHybridSource( { "Jitter Modifier", "Above 1.0 will cause grid artifacts" }, 1.0f, &Cellular::SetJitterModifier, &Cellular::SetJitterModifier ); this->AddVariableEnum( { "Distance Function", "How distance to closest cells is calculated\nHybrid is EuclideanSquared + Manhattan" }, - DistanceFunction::EuclideanSquared, &Cellular::SetDistanceFunction, kDistanceFunction_Strings ); + DistanceFunction::EuclideanSquared, &Cellular::SetDistanceFunction, kDistanceFunction_Strings ); } }; #endif - class CellularValue : public virtual Cellular + class CellularValue : public virtual Cellular<> { public: const Metadata& GetMetadata() const override; @@ -46,7 +47,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; @@ -56,13 +57,12 @@ namespace FastNoise description = "Returns value of Nth closest cell\n" - "Value is generated using white noise\n" - "Output is bounded -1 : 1"; + "Value is generated using white noise"; } }; #endif - class CellularDistance : public virtual Cellular + class CellularDistance : public virtual Cellular<> { public: const Metadata& GetMetadata() const override; @@ -90,7 +90,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; @@ -108,7 +108,7 @@ namespace FastNoise }; #endif - class CellularLookup : public virtual Cellular + class CellularLookup : public virtual Cellular { public: const Metadata& GetMetadata() const override; @@ -121,7 +121,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; diff --git a/include/FastNoise/Generators/Cellular.inl b/include/FastNoise/Generators/Cellular.inl index f3fc94f..e7bc34b 100644 --- a/include/FastNoise/Generators/Cellular.inl +++ b/include/FastNoise/Generators/Cellular.inl @@ -4,26 +4,25 @@ #include "Cellular.h" #include "Utils.inl" -template -class FastSIMD::DispatchClass : public virtual FastNoise::Cellular, public FastSIMD::DispatchClass +template +class FastSIMD::DispatchClass, SIMD> : public virtual FastNoise::Cellular, public FastSIMD::DispatchClass { protected: - const float kJitter2D = 0.437016f; - const float kJitter3D = 0.396144f; - const float kJitter4D = 0.366025f; - const float kJitterIdx23 = 0.190983f; + static constexpr float kJitter2D = 0.437016f; + static constexpr float kJitter3D = 0.396144f; + static constexpr float kJitter4D = 0.366025f; + static constexpr float kJitterIdx23 = 0.190983f; }; template -class FastSIMD::DispatchClass final : public virtual FastNoise::CellularValue, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::CellularValue, public FastSIMD::DispatchClass, SIMD> { float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const { float32v jitter = float32v( this->kJitter2D ) * this->GetSourceValue( mJitterModifier, seed, x, y ); - std::array value; + std::array valueHash; std::array distance; - value.fill( float32v( INFINITY ) ); distance.fill( float32v( INFINITY ) ); this->ScalePositions( x, y ); @@ -51,7 +50,7 @@ class FastSIMD::DispatchClass final : public vir xd = FS::FMulAdd( xd, invMag, xcf ); yd = FS::FMulAdd( yd, invMag, ycf ); - float32v newCellValue = float32v( (float)(1.0 / INT_MAX) ) * FS::Convert( hash ); + int32v newCellValueHash = hash; float32v newDistance = CalcDistance( mDistanceFunction, xd, yd ); for( int i = 0; ; i++ ) @@ -59,10 +58,10 @@ class FastSIMD::DispatchClass final : public vir mask32v closer = newDistance < distance[i]; float32v localDistance = distance[i]; - float32v localCellValue = value[i]; + int32v localCellValueHash = valueHash[i]; distance[i] = FS::Select( closer, newDistance, distance[i] ); - value[i] = FS::Select( closer, newCellValue, value[i] ); + valueHash[i] = FS::Select( closer, newCellValueHash, valueHash[i] ); if( i > mValueIndex ) { @@ -70,7 +69,7 @@ class FastSIMD::DispatchClass final : public vir } newDistance = FS::Select( closer, localDistance, newDistance ); - newCellValue = FS::Select( closer, localCellValue, newCellValue ); + newCellValueHash = FS::Select( closer, localCellValueHash, newCellValueHash ); } ycf += float32v( 1 ); @@ -80,16 +79,15 @@ class FastSIMD::DispatchClass final : public vir xc += int32v( Primes::X ); } - return value[mValueIndex]; + return this->ScaleOutput( FS::Convert( valueHash[mValueIndex] ), -kValueBounds, kValueBounds ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const { float32v jitter = float32v( this->kJitter3D ) * this->GetSourceValue( mJitterModifier, seed, x, y, z ); - std::array value; + std::array valueHash; std::array distance; - value.fill( float32v( INFINITY ) ); distance.fill( float32v( INFINITY ) ); this->ScalePositions( x, y, z ); @@ -125,8 +123,8 @@ class FastSIMD::DispatchClass final : public vir xd = FS::FMulAdd( xd, invMag, xcf ); yd = FS::FMulAdd( yd, invMag, ycf ); zd = FS::FMulAdd( zd, invMag, zcf ); - - float32v newCellValue = float32v( (float)(1.0 / INT_MAX) ) * FS::Convert( hash ); + + int32v newCellValueHash = hash; float32v newDistance = CalcDistance( mDistanceFunction, xd, yd, zd ); for( int i = 0; ; i++ ) @@ -134,10 +132,10 @@ class FastSIMD::DispatchClass final : public vir mask32v closer = newDistance < distance[i]; float32v localDistance = distance[i]; - float32v localCellValue = value[i]; + int32v localCellValueHash = valueHash[i]; distance[i] = FS::Select( closer, newDistance, distance[i] ); - value[i] = FS::Select( closer, newCellValue, value[i] ); + valueHash[i] = FS::Select( closer, newCellValueHash, valueHash[i] ); if( i > mValueIndex ) { @@ -145,7 +143,7 @@ class FastSIMD::DispatchClass final : public vir } newDistance = FS::Select( closer, localDistance, newDistance ); - newCellValue = FS::Select( closer, localCellValue, newCellValue ); + newCellValueHash = FS::Select( closer, localCellValueHash, newCellValueHash ); } zcf += float32v( 1 ); @@ -157,17 +155,16 @@ class FastSIMD::DispatchClass final : public vir xcf += float32v( 1 ); xc += int32v( Primes::X ); } - - return value[mValueIndex]; + + return this->ScaleOutput( FS::Convert( valueHash[mValueIndex] ), -kValueBounds, kValueBounds ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z , float32v w ) const { float32v jitter = float32v( this->kJitter4D ) * this->GetSourceValue( mJitterModifier, seed, x, y, z, w ); - std::array value; + std::array valueHash; std::array distance; - value.fill( float32v( INFINITY ) ); distance.fill( float32v( INFINITY ) ); this->ScalePositions( x, y, z, w ); @@ -213,7 +210,7 @@ class FastSIMD::DispatchClass final : public vir zd = FS::FMulAdd( zd, invMag, zcf ); wd = FS::FMulAdd( wd, invMag, wcf ); - float32v newCellValue = float32v( (float)(1.0 / INT_MAX) ) * FS::Convert( hash ); + int32v newCellValueHash = hash; float32v newDistance = CalcDistance( mDistanceFunction, xd, yd, zd, wd ); for( int i = 0; ; i++ ) @@ -221,10 +218,10 @@ class FastSIMD::DispatchClass final : public vir mask32v closer = newDistance < distance[i]; float32v localDistance = distance[i]; - float32v localCellValue = value[i]; + int32v localCellValueHash = valueHash[i]; distance[i] = FS::Select( closer, newDistance, distance[i] ); - value[i] = FS::Select( closer, newCellValue, value[i] ); + valueHash[i] = FS::Select( closer, newCellValueHash, valueHash[i] ); if( i > mValueIndex ) { @@ -232,7 +229,7 @@ class FastSIMD::DispatchClass final : public vir } newDistance = FS::Select( closer, localDistance, newDistance ); - newCellValue = FS::Select( closer, localCellValue, newCellValue ); + newCellValueHash = FS::Select( closer, localCellValueHash, newCellValueHash ); } wcf += float32v( 1 ); @@ -247,13 +244,13 @@ class FastSIMD::DispatchClass final : public vir xcf += float32v( 1 ); xc += int32v( Primes::X ); } - - return value[mValueIndex]; + + return this->ScaleOutput( FS::Convert( valueHash[mValueIndex] ), -kValueBounds, kValueBounds ); } }; template -class FastSIMD::DispatchClass final : public virtual FastNoise::CellularDistance, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::CellularDistance, public FastSIMD::DispatchClass, SIMD> { float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const { @@ -303,7 +300,7 @@ class FastSIMD::DispatchClass final : public xc += int32v( Primes::X ); } - return GetReturn( distance ); + return GetReturn( distance, 1 + this->kJitter2D ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const @@ -366,7 +363,7 @@ class FastSIMD::DispatchClass final : public xc += int32v( Primes::X ); } - return GetReturn( distance ); + return GetReturn( distance, 1 + this->kJitter3D ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const @@ -441,10 +438,10 @@ class FastSIMD::DispatchClass final : public xc += int32v( Primes::X ); } - return GetReturn( distance ); + return GetReturn( distance, 1 + this->kJitter4D ); } - FS_FORCEINLINE float32v GetReturn( std::array& distance ) const + FS_FORCEINLINE float32v GetReturn( std::array& distance, float maxDist ) const { if( mDistanceFunction == FastNoise::DistanceFunction::Euclidean ) { @@ -452,35 +449,37 @@ class FastSIMD::DispatchClass final : public distance[mDistanceIndex1] *= FS::InvSqrt( distance[mDistanceIndex1] ); } + maxDist *= maxDist; + switch( mReturnType ) { default: case ReturnType::Index0: { - return distance[mDistanceIndex0]; + return this->ScaleOutput( distance[mDistanceIndex0], 0, maxDist ); } case ReturnType::Index0Add1: { - return distance[mDistanceIndex0] + distance[mDistanceIndex1]; + return this->ScaleOutput( distance[mDistanceIndex0] + distance[mDistanceIndex1], 0, maxDist * 2 ); } case ReturnType::Index0Sub1: { - return distance[mDistanceIndex0] - distance[mDistanceIndex1]; + return this->ScaleOutput( FS::Abs( distance[mDistanceIndex0] - distance[mDistanceIndex1] ), 0, maxDist ); } case ReturnType::Index0Mul1: { - return distance[mDistanceIndex0] * distance[mDistanceIndex1]; + return this->ScaleOutput( distance[mDistanceIndex0] * distance[mDistanceIndex1], 0, maxDist * maxDist ); } case ReturnType::Index0Div1: { - return distance[mDistanceIndex0] * FS::Reciprocal( distance[mDistanceIndex1] ); + return this->ScaleOutput( distance[mDistanceIndex0] * FS::Reciprocal( distance[mDistanceIndex1] ), 0, maxDist ); } } } }; template -class FastSIMD::DispatchClass final : public virtual FastNoise::CellularLookup, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::CellularLookup, public FastSIMD::DispatchClass, SIMD> { float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const { diff --git a/include/FastNoise/Generators/Generator.h b/include/FastNoise/Generators/Generator.h index c59fb86..cafe297 100644 --- a/include/FastNoise/Generators/Generator.h +++ b/include/FastNoise/Generators/Generator.h @@ -223,8 +223,8 @@ namespace FastNoise memberVariables.push_back( member ); } - template>> - void AddVariable( NameDesc nameDesc, T defaultV, void ( U::*func )( T ), T minV = 0, T maxV = 0, float uiDragSpeed = std::is_same_v ? Metadata::kDefaultUiDragSpeedFloat : Metadata::kDefaultUiDragSpeedInt ) + template>> + void AddVariable( NameDesc nameDesc, T defaultV, V ( U::*func )( T ), T minV = 0, T maxV = 0, float uiDragSpeed = std::is_same_v ? Metadata::kDefaultUiDragSpeedFloat : Metadata::kDefaultUiDragSpeedInt ) { MemberVariable member; member.name = nameDesc.name; diff --git a/include/FastNoise/Generators/Perlin.h b/include/FastNoise/Generators/Perlin.h index 8096537..cf4832e 100644 --- a/include/FastNoise/Generators/Perlin.h +++ b/include/FastNoise/Generators/Perlin.h @@ -3,7 +3,7 @@ namespace FastNoise { - class Perlin : public virtual ScalableGenerator + class Perlin : public virtual VariableRange { public: const Metadata& GetMetadata() const override; @@ -11,7 +11,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; diff --git a/include/FastNoise/Generators/Perlin.inl b/include/FastNoise/Generators/Perlin.inl index b9a3190..6f19b47 100644 --- a/include/FastNoise/Generators/Perlin.inl +++ b/include/FastNoise/Generators/Perlin.inl @@ -2,7 +2,7 @@ #include "Utils.inl" template -class FastSIMD::DispatchClass final : public virtual FastNoise::Perlin, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::Perlin, public FastSIMD::DispatchClass, SIMD> { float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const { this->ScalePositions( x, y ); @@ -23,9 +23,12 @@ class FastSIMD::DispatchClass final : public virtual Fa xs = InterpQuintic( xs ); ys = InterpQuintic( ys ); - return float32v( 0.579106986522674560546875f ) * Lerp( + constexpr float kBounding = 0.579106986522674560546875f; + + return this->ScaleOutput( Lerp( Lerp( GetGradientDot( HashPrimes( seed, x0, y0 ), xf0, yf0 ), GetGradientDot( HashPrimes( seed, x1, y0 ), xf1, yf0 ), xs ), - Lerp( GetGradientDot( HashPrimes( seed, x0, y1 ), xf0, yf1 ), GetGradientDot( HashPrimes( seed, x1, y1 ), xf1, yf1 ), xs ), ys ); + Lerp( GetGradientDot( HashPrimes( seed, x0, y1 ), xf0, yf1 ), GetGradientDot( HashPrimes( seed, x1, y1 ), xf1, yf1 ), xs ), ys ), + -1 / kBounding, 1 / kBounding ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const @@ -54,12 +57,15 @@ class FastSIMD::DispatchClass final : public virtual Fa ys = InterpQuintic( ys ); zs = InterpQuintic( zs ); - return float32v( 0.964921414852142333984375f ) * Lerp( Lerp( + constexpr float kBounding = 0.964921414852142333984375f; + + return this->ScaleOutput( Lerp( Lerp( Lerp( GetGradientDot( HashPrimes( seed, x0, y0, z0 ), xf0, yf0, zf0 ), GetGradientDot( HashPrimes( seed, x1, y0, z0 ), xf1, yf0, zf0 ), xs ), Lerp( GetGradientDot( HashPrimes( seed, x0, y1, z0 ), xf0, yf1, zf0 ), GetGradientDot( HashPrimes( seed, x1, y1, z0 ), xf1, yf1, zf0 ), xs ), ys ), Lerp( Lerp( GetGradientDot( HashPrimes( seed, x0, y0, z1 ), xf0, yf0, zf1 ), GetGradientDot( HashPrimes( seed, x1, y0, z1 ), xf1, yf0, zf1 ), xs ), - Lerp( GetGradientDot( HashPrimes( seed, x0, y1, z1 ), xf0, yf1, zf1 ), GetGradientDot( HashPrimes( seed, x1, y1, z1 ), xf1, yf1, zf1 ), xs ), ys ), zs ); + Lerp( GetGradientDot( HashPrimes( seed, x0, y1, z1 ), xf0, yf1, zf1 ), GetGradientDot( HashPrimes( seed, x1, y1, z1 ), xf1, yf1, zf1 ), xs ), ys ), zs ), + -1 / kBounding, 1 / kBounding ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const @@ -94,7 +100,9 @@ class FastSIMD::DispatchClass final : public virtual Fa zs = InterpQuintic( zs ); ws = InterpQuintic( ws ); - return float32v( 0.964921414852142333984375f ) * Lerp( Lerp( Lerp( + constexpr float kBounding = 0.964921414852142333984375f; + + return this->ScaleOutput( Lerp( Lerp( Lerp( Lerp( GetGradientDot( HashPrimes( seed, x0, y0, z0, w0 ), xf0, yf0, zf0, wf0 ), GetGradientDot( HashPrimes( seed, x1, y0, z0, w0 ), xf1, yf0, zf0, wf0 ), xs ), Lerp( GetGradientDot( HashPrimes( seed, x0, y1, z0, w0 ), xf0, yf1, zf0, wf0 ), GetGradientDot( HashPrimes( seed, x1, y1, z0, w0 ), xf1, yf1, zf0, wf0 ), xs ), ys ), Lerp( @@ -105,6 +113,7 @@ class FastSIMD::DispatchClass final : public virtual Fa Lerp( GetGradientDot( HashPrimes( seed, x0, y1, z0, w1 ), xf0, yf1, zf0, wf1 ), GetGradientDot( HashPrimes( seed, x1, y1, z0, w1 ), xf1, yf1, zf0, wf1 ), xs ), ys ), Lerp( Lerp( GetGradientDot( HashPrimes( seed, x0, y0, z1, w1 ), xf0, yf0, zf1, wf1 ), GetGradientDot( HashPrimes( seed, x1, y0, z1, w1 ), xf1, yf0, zf1, wf1 ), xs ), - Lerp( GetGradientDot( HashPrimes( seed, x0, y1, z1, w1 ), xf0, yf1, zf1, wf1 ), GetGradientDot( HashPrimes( seed, x1, y1, z1, w1 ), xf1, yf1, zf1, wf1 ), xs ), ys ), zs ), ws ); + Lerp( GetGradientDot( HashPrimes( seed, x0, y1, z1, w1 ), xf0, yf1, zf1, wf1 ), GetGradientDot( HashPrimes( seed, x1, y1, z1, w1 ), xf1, yf1, zf1, wf1 ), xs ), ys ), zs ), ws ), + -1 / kBounding, 1 / kBounding ); } }; diff --git a/include/FastNoise/Generators/Simplex.h b/include/FastNoise/Generators/Simplex.h index 00fd17e..cbed810 100644 --- a/include/FastNoise/Generators/Simplex.h +++ b/include/FastNoise/Generators/Simplex.h @@ -3,7 +3,7 @@ namespace FastNoise { - class Simplex : public virtual ScalableGenerator + class Simplex : public virtual VariableRange { public: const Metadata& GetMetadata() const override; @@ -11,7 +11,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; @@ -26,7 +26,7 @@ namespace FastNoise }; #endif - class OpenSimplex2 : public virtual ScalableGenerator + class OpenSimplex2 : public virtual VariableRange { public: const Metadata& GetMetadata() const override; @@ -34,7 +34,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; @@ -49,7 +49,7 @@ namespace FastNoise }; #endif - class OpenSimplex2S : public virtual ScalableGenerator + class OpenSimplex2S : public virtual VariableRange { public: const Metadata& GetMetadata() const override; @@ -57,7 +57,7 @@ namespace FastNoise #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; diff --git a/include/FastNoise/Generators/Simplex.inl b/include/FastNoise/Generators/Simplex.inl index cab39e3..3209d60 100644 --- a/include/FastNoise/Generators/Simplex.inl +++ b/include/FastNoise/Generators/Simplex.inl @@ -2,7 +2,7 @@ #include "Utils.inl" template -class FastSIMD::DispatchClass final : public virtual FastNoise::Simplex, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::Simplex, public FastSIMD::DispatchClass, SIMD> { float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const { @@ -48,7 +48,10 @@ class FastSIMD::DispatchClass final : public virtual F float32v n1 = GetGradientDot( HashPrimes( seed, FS::MaskedAdd( i1, i, int32v( Primes::X ) ), FS::InvMaskedAdd( i1, j, int32v( Primes::Y ) ) ), x1, y1 ); float32v n2 = GetGradientDot( HashPrimes( seed, i + int32v( Primes::X ), j + int32v( Primes::Y ) ), x2, y2 ); - return float32v( 38.283687591552734375f ) * FS::FMulAdd( n0, t0, FS::FMulAdd( n1, t1, n2 * t2 ) ); + constexpr float kBounding = 38.283687591552734375f; + + return this->ScaleOutput( FS::FMulAdd( n0, t0, FS::FMulAdd( n1, t1, n2 * t2 ) ), + -1 / kBounding, 1 / kBounding ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const @@ -120,8 +123,11 @@ class FastSIMD::DispatchClass final : public virtual F float32v n1 = GetGradientDot( HashPrimes( seed, FS::MaskedAdd( i1, i, int32v( Primes::X ) ), FS::MaskedAdd( j1, j, int32v( Primes::Y ) ), FS::MaskedAdd( k1, k, int32v( Primes::Z ) ) ), x1, y1, z1 ); float32v n2 = GetGradientDot( HashPrimes( seed, FS::MaskedAdd( i2, i, int32v( Primes::X ) ), FS::MaskedAdd( j2, j, int32v( Primes::Y ) ), FS::InvMaskedAdd( k2, k, int32v( Primes::Z ) ) ), x2, y2, z2 ); float32v n3 = GetGradientDot( HashPrimes( seed, i + int32v( Primes::X ), j + int32v( Primes::Y ), k + int32v( Primes::Z ) ), x3, y3, z3 ); + + constexpr float kBounding = 32.69428253173828125f; - return float32v( 32.69428253173828125f ) * FS::FMulAdd( n0, t0, FS::FMulAdd( n1, t1, FS::FMulAdd( n2, t2, n3 * t3 ) ) ); + return this->ScaleOutput( FS::FMulAdd( n0, t0, FS::FMulAdd( n1, t1, FS::FMulAdd( n2, t2, n3 * t3 ) ) ), + -1 / kBounding, 1 / kBounding ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const @@ -255,12 +261,15 @@ class FastSIMD::DispatchClass final : public virtual F FS::MaskedAdd( l3, l, int32v( Primes::W ) ) ), x3, y3, z3, w3 ); float32v n4 = GetGradientDot( HashPrimes( seed, i + int32v( Primes::X ), j + int32v( Primes::Y ), k + int32v( Primes::Z ), l + int32v( Primes::W ) ), x4, y4, z4, w4 ); - return float32v( 27.f ) * FS::FMulAdd( n0, t0, FS::FMulAdd( n1, t1, FS::FMulAdd( n2, t2, FS::FMulAdd( n3, t3, n4 * t4 ) ) ) ); + constexpr float kBounding = 27.f; + + return this->ScaleOutput( FS::FMulAdd( n0, t0, FS::FMulAdd( n1, t1, FS::FMulAdd( n2, t2, FS::FMulAdd( n3, t3, n4 * t4 ) ) ) ), + -1 / kBounding, 1 / kBounding ); } }; template -class FastSIMD::DispatchClass final : public virtual FastNoise::OpenSimplex2, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::OpenSimplex2, public FastSIMD::DispatchClass, SIMD> { float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const { @@ -305,7 +314,10 @@ class FastSIMD::DispatchClass final : public virt float32v n1 = GetGradientDotFancy( HashPrimes( seed, FS::MaskedAdd( i1, i, int32v( Primes::X ) ), FS::InvMaskedAdd( i1, j, int32v( Primes::Y ) ) ), x1, y1 ); float32v n2 = GetGradientDotFancy( HashPrimes( seed, i + int32v( Primes::X ), j + int32v( Primes::Y ) ), x2, y2 ); - return float32v( 49.918426513671875f ) * FS::FMulAdd( n0, t0, FS::FMulAdd( n1, t1, n2 * t2 ) ); + constexpr float kBounding = 49.918426513671875f; + + return this->ScaleOutput( FS::FMulAdd( n0, t0, FS::FMulAdd( n1, t1, n2 * t2 ) ), + -1 / kBounding, 1 / kBounding ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const @@ -371,12 +383,14 @@ class FastSIMD::DispatchClass final : public virt seed = ~seed; } - return float32v( 32.69428253173828125f ) * val; + constexpr float kBounding = 32.69428253173828125f; + + return this->ScaleOutput( val, -1 / kBounding, 1 / kBounding ); } }; template -class FastSIMD::DispatchClass final : public virtual FastNoise::OpenSimplex2S, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::OpenSimplex2S, public FastSIMD::DispatchClass, SIMD> { float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const { @@ -440,8 +454,10 @@ class FastSIMD::DispatchClass final : public vir float32v a3 = FS::Max( FS::FNMulAdd( xi3, xi3, FS::FNMulAdd( yi3, yi3, float32v( 2.0f / 3.0f ) ) ), float32v( 0 ) ); a3 *= a3; a3 *= a3; value = FS::FMulAdd( a3, v3, value ); + + constexpr float kBounding = 9.28993664146183f; - return float32v( 9.28993664146183f ) * value; + return this->ScaleOutput( value, -1 / kBounding, 1 / kBounding ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const @@ -520,8 +536,10 @@ class FastSIMD::DispatchClass final : public vir seed = ~seed; } + + constexpr float kBounding = 144.736422163332608f; - return float32v( 144.736422163332608f ) * value; + return this->ScaleOutput( value, -1 / kBounding, 1 / kBounding ); } }; diff --git a/include/FastNoise/Generators/Utils.inl b/include/FastNoise/Generators/Utils.inl index ef86929..5e64786 100644 --- a/include/FastNoise/Generators/Utils.inl +++ b/include/FastNoise/Generators/Utils.inl @@ -13,8 +13,9 @@ namespace FastNoise static constexpr int Lookup[] = { X,Y,Z,W }; } - static constexpr float ROOT2 = 1.4142135623730950488f; - static constexpr float ROOT3 = 1.7320508075688772935f; + static constexpr float kValueBounds = 2147483648.f; + static constexpr float kRoot2 = 1.4142135623730950488f; + static constexpr float kRoot3 = 1.7320508075688772935f; template FS_FORCEINLINE static float32v GetGradientDotFancy( int32v hash, float32v fX, float32v fY ) @@ -23,15 +24,15 @@ namespace FastNoise if constexpr( SIMD & FastSIMD::FeatureFlag::AVX512_F ) { - float32v gX = FS::NativeExec( FS_BIND_INTRINSIC( _mm512_permutexvar_ps ), index, FS::Constant( ROOT3, ROOT3, 2, 2, 1, -1, 0, 0, -ROOT3, -ROOT3, -2, -2, -1, 1, 0, 0 ) ); - float32v gY = FS::NativeExec( FS_BIND_INTRINSIC( _mm512_permutexvar_ps ), index, FS::Constant( 1, -1, 0, 0, ROOT3, ROOT3, 2, 2, -1, 1, 0, 0, -ROOT3, -ROOT3, -2, -2 ) ); + float32v gX = FS::NativeExec( FS_BIND_INTRINSIC( _mm512_permutexvar_ps ), index, FS::Constant( kRoot3, kRoot3, 2, 2, 1, -1, 0, 0, -kRoot3, -kRoot3, -2, -2, -1, 1, 0, 0 ) ); + float32v gY = FS::NativeExec( FS_BIND_INTRINSIC( _mm512_permutexvar_ps ), index, FS::Constant( 1, -1, 0, 0, kRoot3, kRoot3, 2, 2, -1, 1, 0, 0, -kRoot3, -kRoot3, -2, -2 ) ); return FS::FMulAdd( gX, fX, fY * gY ); } else if constexpr( SIMD & FastSIMD::FeatureFlag::AVX2 ) { - float32v gX = FS::NativeExec( FS_BIND_INTRINSIC( _mm256_permutevar8x32_ps ), FS::Constant( ROOT3, ROOT3, 2, 2, 1, -1, 0, 0 ), index ); - float32v gY = FS::NativeExec( FS_BIND_INTRINSIC( _mm256_permutevar8x32_ps ), FS::Constant( 1, -1, 0, 0, ROOT3, ROOT3, 2, 2 ), index ); + float32v gX = FS::NativeExec( FS_BIND_INTRINSIC( _mm256_permutevar8x32_ps ), FS::Constant( kRoot3, kRoot3, 2, 2, 1, -1, 0, 0 ), index ); + float32v gY = FS::NativeExec( FS_BIND_INTRINSIC( _mm256_permutevar8x32_ps ), FS::Constant( 1, -1, 0, 0, kRoot3, kRoot3, 2, 2 ), index ); // Bit-8 = Flip sign of a + b return FS::FMulAdd( gX, fX, fY * gY ) ^ FS::Cast( ( index >> 3 ) << 31 ); @@ -66,7 +67,7 @@ namespace FastNoise // Bit-2 = Mul a by 2 or Root3 mask32v bit2 = ( index & int32v( 2 ) ) == int32v( 0 ); - a *= FS::Select( bit2, float32v( 2 ), float32v( ROOT3 ) ); + a *= FS::Select( bit2, float32v( 2 ), float32v( kRoot3 ) ); // b zero value if a mul 2 float32v c = FS::MaskedAdd( bit2, a, b ); @@ -83,15 +84,15 @@ namespace FastNoise if constexpr( SIMD & FastSIMD::FeatureFlag::AVX512_F ) { - float32v gX = FS::NativeExec( FS_BIND_INTRINSIC( _mm512_permutexvar_ps ), hash, FS::Constant( 1 + ROOT2, -1 - ROOT2, 1 + ROOT2, -1 - ROOT2, 1, -1, 1, -1, 1 + ROOT2, -1 - ROOT2, 1 + ROOT2, -1 - ROOT2, 1, -1, 1, -1 ) ); - float32v gY = FS::NativeExec( FS_BIND_INTRINSIC( _mm512_permutexvar_ps ), hash, FS::Constant( 1, 1, -1, -1, 1 + ROOT2, 1 + ROOT2, -1 - ROOT2, -1 - ROOT2, 1, 1, -1, -1, 1 + ROOT2, 1 + ROOT2, -1 - ROOT2, -1 - ROOT2 ) ); + float32v gX = FS::NativeExec( FS_BIND_INTRINSIC( _mm512_permutexvar_ps ), hash, FS::Constant( 1 + kRoot2, -1 - kRoot2, 1 + kRoot2, -1 - kRoot2, 1, -1, 1, -1, 1 + kRoot2, -1 - kRoot2, 1 + kRoot2, -1 - kRoot2, 1, -1, 1, -1 ) ); + float32v gY = FS::NativeExec( FS_BIND_INTRINSIC( _mm512_permutexvar_ps ), hash, FS::Constant( 1, 1, -1, -1, 1 + kRoot2, 1 + kRoot2, -1 - kRoot2, -1 - kRoot2, 1, 1, -1, -1, 1 + kRoot2, 1 + kRoot2, -1 - kRoot2, -1 - kRoot2 ) ); return FS::FMulAdd( gX, fX, fY * gY ); } else if constexpr( SIMD & FastSIMD::FeatureFlag::AVX2 ) { - float32v gX = FS::NativeExec( FS_BIND_INTRINSIC( _mm256_permutevar8x32_ps ), FS::Constant( 1 + ROOT2, -1 - ROOT2, 1 + ROOT2, -1 - ROOT2, 1, -1, 1, -1 ), hash ); - float32v gY = FS::NativeExec( FS_BIND_INTRINSIC( _mm256_permutevar8x32_ps ), FS::Constant( 1, 1, -1, -1, 1 + ROOT2, 1 + ROOT2, -1 - ROOT2, -1 - ROOT2 ), hash ); + float32v gX = FS::NativeExec( FS_BIND_INTRINSIC( _mm256_permutevar8x32_ps ), FS::Constant( 1 + kRoot2, -1 - kRoot2, 1 + kRoot2, -1 - kRoot2, 1, -1, 1, -1 ), hash ); + float32v gY = FS::NativeExec( FS_BIND_INTRINSIC( _mm256_permutevar8x32_ps ), FS::Constant( 1, 1, -1, -1, 1 + kRoot2, 1 + kRoot2, -1 - kRoot2, -1 - kRoot2 ), hash ); return FS::FMulAdd( gX, fX, fY * gY ); } @@ -114,7 +115,7 @@ namespace FastNoise float32v a = FS::Select( bit4Mask, fY, fX ); float32v b = FS::Select( bit4Mask, fX, fY ); - return FS::FMulAdd( float32v( 1.0f + ROOT2 ), a, b ); + return FS::FMulAdd( float32v( 1.0f + kRoot2 ), a, b ); } } @@ -220,22 +221,8 @@ namespace FastNoise int32v hash = seed; hash ^= (primedPos ^ ...); -#if 1 hash *= hash * int32v( 0x27d4eb2d ); - return FS::Convert( hash ) * float32v( -1.0f / (float)INT_MIN ); - -#else // More accurate bounding but slower - int32v zeroCase = hash >> 8; - hash *= hash * int32v( 0x27d4eb2d ); - - int32v floatBits = hash & int32v( 0x7FFFFF ); //fp32 fractional bits - floatBits |= int32v( 0x3F800000 ); // fp32 1.0 - float32v f32 = FS::Cast( floatBits ) - float32v( 0.9999999f ); // bring range to 0.0000001 - 1.0 - - float32v sign = FS::Cast( hash & int32v( -2147483648 ) ); - - return FS::InvMasked( zeroCase == int32v( 0x7FFFFF ), f32 | sign ); -#endif + return FS::Convert( hash ); } FS_FORCEINLINE static float32v Lerp( float32v a, float32v b, float32v t ) diff --git a/include/FastNoise/Generators/Value.h b/include/FastNoise/Generators/Value.h index 87b5560..2392f2f 100644 --- a/include/FastNoise/Generators/Value.h +++ b/include/FastNoise/Generators/Value.h @@ -3,14 +3,14 @@ namespace FastNoise { - class Value : public virtual ScalableGenerator + class Value : public virtual VariableRange { public: const Metadata& GetMetadata() const override; }; #ifdef FASTNOISE_METADATA template<> - struct MetadataT : MetadataT + struct MetadataT : MetadataT> { SmartNode<> CreateNode( FastSIMD::FeatureSet ) const override; diff --git a/include/FastNoise/Generators/Value.inl b/include/FastNoise/Generators/Value.inl index 0d162b5..3792186 100644 --- a/include/FastNoise/Generators/Value.inl +++ b/include/FastNoise/Generators/Value.inl @@ -2,7 +2,7 @@ #include "Utils.inl" template -class FastSIMD::DispatchClass final : public virtual FastNoise::Value, public FastSIMD::DispatchClass +class FastSIMD::DispatchClass final : public virtual FastNoise::Value, public FastSIMD::DispatchClass, SIMD> { float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y ) const { @@ -19,9 +19,10 @@ class FastSIMD::DispatchClass final : public virtual Fas xs = InterpHermite( x - xs ); ys = InterpHermite( y - ys ); - return Lerp( + return this->ScaleOutput( Lerp( Lerp( GetValueCoord( seed, x0, y0 ), GetValueCoord( seed, x1, y0 ), xs ), - Lerp( GetValueCoord( seed, x0, y1 ), GetValueCoord( seed, x1, y1 ), xs ), ys ); + Lerp( GetValueCoord( seed, x0, y1 ), GetValueCoord( seed, x1, y1 ), xs ), ys ), + -kValueBounds, kValueBounds ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z ) const @@ -43,12 +44,13 @@ class FastSIMD::DispatchClass final : public virtual Fas ys = InterpHermite( y - ys ); zs = InterpHermite( z - zs ); - return Lerp( Lerp( + return this->ScaleOutput( Lerp( Lerp( Lerp( GetValueCoord( seed, x0, y0, z0 ), GetValueCoord( seed, x1, y0, z0 ), xs ), Lerp( GetValueCoord( seed, x0, y1, z0 ), GetValueCoord( seed, x1, y1, z0 ), xs ), ys ), Lerp( Lerp( GetValueCoord( seed, x0, y0, z1 ), GetValueCoord( seed, x1, y0, z1 ), xs ), - Lerp( GetValueCoord( seed, x0, y1, z1 ), GetValueCoord( seed, x1, y1, z1 ), xs ), ys ), zs ); + Lerp( GetValueCoord( seed, x0, y1, z1 ), GetValueCoord( seed, x1, y1, z1 ), xs ), ys ), zs ), + -kValueBounds, kValueBounds ); } float32v FS_VECTORCALL Gen( int32v seed, float32v x, float32v y, float32v z, float32v w ) const @@ -74,7 +76,7 @@ class FastSIMD::DispatchClass final : public virtual Fas zs = InterpHermite( z - zs ); ws = InterpHermite( w - ws ); - return Lerp( Lerp( Lerp( + return this->ScaleOutput( Lerp( Lerp( Lerp( Lerp( GetValueCoord( seed, x0, y0, z0, w0 ), GetValueCoord( seed, x1, y0, z0, w0 ), xs ), Lerp( GetValueCoord( seed, x0, y1, z0, w0 ), GetValueCoord( seed, x1, y1, z0, w0 ), xs ), ys ), Lerp( @@ -85,6 +87,7 @@ class FastSIMD::DispatchClass final : public virtual Fas Lerp( GetValueCoord( seed, x0, y1, z0, w1 ), GetValueCoord( seed, x1, y1, z0, w1 ), xs ), ys ), Lerp( Lerp( GetValueCoord( seed, x0, y0, z1, w1 ), GetValueCoord( seed, x1, y0, z1, w1 ), xs ), - Lerp( GetValueCoord( seed, x0, y1, z1, w1 ), GetValueCoord( seed, x1, y1, z1, w1 ), xs ), ys ), zs ), ws ); + Lerp( GetValueCoord( seed, x0, y1, z1, w1 ), GetValueCoord( seed, x1, y1, z1, w1 ), xs ), ys ), zs ), ws ), + -kValueBounds, kValueBounds ); } };