Tileable / "infinite" Perlin Noise in Blitzmax?

BlitzMax Forums/BlitzMax Beginners Area/Tileable / "infinite" Perlin Noise in Blitzmax?

Mongoose(Posted 2013) [#1]
Hi all,

I've been using Blitz3D on and off for the past few years, and recently started playing with noise theory- which lead me to make a small terrain generator (No, i'm not making minecraft! simply a random environment game) Here's a screenshot of how it works- as you can notice, it's divided up into grids:



However, this comes at a price, as I used NoiseGens by MusiciansKool (http://www.blitzbasic.com/Community/posts.php?topic=90465) to lazily generate each grids data, then advance the noise function based on the grid patches location. If you are curious, the call to the DLL/Userlib looks like this- X and Y are the grids internal data, while it's multiplied by the patch poly count, giving you a perfect square:

Noise#=Perlin#(NoiseID, x+(terrain\x*TerrainPolyCount),y-(terrain\y*TerrainPolyCount),1,terrain\Frequency,terrain\Lacunarity,terrain\Persistence,terrain\Octaves,terrain\Quality)

So I'm hinging on a decision to buy BlitzMax and convert all of this to XORS3D, because sadly Blitz3D's outdated engine is slogging through how many polys i really want to be pushing with this generator, but i'm stuck trying to think about how I would have such complex noise generation within Blitzmax. NoiseGen offers a lot of different noise techniques with minimal effort, and I feel lazy for avoiding learning how to implement these functions properly- I just want to drive the car, not build it from scratch!

I looked at some noise functions, but none of the could be "continued" in such a fashion that you can keep generating from the same seed and the results are consistent and can be tiled. Am I missing something with how these functions work? Did I not dig far enough?

I've tried porting the NoiseGens code, but know NOTHING about FreeBasic (which i think is what the source MusiciansKool provided is written in- .bas? Anyone?) so i tried wrapping the DLL by simply doing Proc calls to it. It initializes the seed and will generate exactly ONE result before crashing with an exception.

So I feel like i'm in over my head. Does anyone have any pointers on what I should do?


Banshee(Posted 2013) [#2]
Moving to BlitzMax will be a whole lot of learning, even though it is still Blitz. When I made the jump myself some years ago it took me a bit to get my head around it - well worth it of course, and I shudder at the thought of using Blitz3D again now.

My point is, there will be so much learning to do in the migration that time and projects will pass before you are ready to do a finished game again, i'd do a simple project in 2D first before tackling 3D - learn as if from day 1 but have the added benefit of your experience with Blitz3D to breeze through it, rather than go for the big complex project first.

In theory though, it's just a dll you mention right? So accessing it would be easy.


Yasha(Posted 2013) [#3]
Have you tried integrating this with Krischan's BlitzTiles engine? It has a built-in LOD system based on quadtrees that ought to improve the performance of large terrains a great deal. It can apparently handle very large maps with excellent performance at the moment.

Because... if you're being weighed down by the complexity of models, it's only partially Blitz3D's problem: any engine you replace it with will only be able to significantly improve on that issue if it has a LOD system built-in (I think doing it completely automatically requires DirectX11, so won't feature in Xors3D - although my understanding of this is rather limited). Polygons have to be rendered, or dropped from rendering, one way or another.


The DLL you linked is a B3D userlib, so the functions in it use the _stdcall convention and therefore I think you need to specify "Win32" or something like that when calling functions from BlitzMax, because I believe it defaults to cdecl, which might result in you corrupting the stack. Just guessing here.


Mongoose(Posted 2013) [#4]
Banshee- I've done plenty of small projects, and tinkered with the Blitzmax demo, just never took the leap to buy it since I never had a reason to. But this thing I'm creating here is quickly outgrowing Blitz3D, and sadly- I think it's about time I place it on the shelf for good.

Yasha- Sadly, It appears the download for BlitzTiles is defunc :( anyone have a copy? I think i could make it work with how I'm generating tiles- Since it just takes heightmap data. Do mind that I am using meshes instead of Blitz3Ds built-in terrain. I've seen other systems that used Terrain instead of mesh, but never had a chance to grab BlitzTiles; Guess this is what I get for not being on the community board!

I have SOME LOD in, however- I'd really like the boost you get from DirectX9, as well as some of the advanced features such as shaders and a little more overhead in rendering (though from my tests, it still suffers from the overall "large batch" bottleneck- screeching to around the same FPS as Blitz3D with the same amount of entities to render, so isn't exactly a magic bullet if you want MORE entities, just higher detail. :( ).

Lastly, I found the DECLS generator somewhere on the board and ran the Noisegen declarations through it, and out popped:

Strict
Import Pub.Win32

Private
Local lib%

Public

lib = LoadLibraryA("NoiseGens.dll")

Global Init_Noise%(seed%) = GetProcAddress(lib, "CreateNoiseGenerator@4")
Global CreateNoiseGenerator%(seed%) = GetProcAddress(lib, "CreateNoiseGenerator@4")
Global Free_Noise%(Noise_Gen_Pointer%) = GetProcAddress(lib, "FreeNoiseGenerator@4")
Global FreeNoiseGenerator%(Noise_Gen_Pointer%) = GetProcAddress(lib, "FreeNoiseGenerator@4")
Global Noise#(Noise_Gen_Pointer%,x#,y#,z#,Quality%) = GetProcAddress(lib, "Noise@20")
Global pNoise#(Noise_Gen_Pointer%,x#,y#,z#,px%,py%,pz%,Quality%) = GetProcAddress(lib, "PNoise@32")
Global sNoise#(Noise_Gen_Pointer%,x#,y#,z#) = GetProcAddress(lib, "SNoise@16")
Global Voronoi#(Noise_Gen_Pointer%,x#,y#,z#,Displacement#,Frequency#,Seed%,EnableDistance%,Quality) = GetProcAddress(lib, "Voronoi@36")
Global LinearInterpolation#(Number1#,Number2#,Displacement#) = GetProcAddress(lib, "LinearInterpolation@12")
Global GetMin#(Value1#,Value2#) = GetProcAddress(lib, "GetMin@8")
Global GetMax#(Value1#,Value2#) = GetProcAddress(lib, "GetMax@8")
Global CurveNoise#(NewValue#,OldValue#,Increments#) = GetProcAddress(lib, "CurveNoise@12")
Global WrapNoise#(Value#,Lo#,High#) = GetProcAddress(lib, "WrapNoise@12")
Global SCurve3#(Value#) = GetProcAddress(lib, "SuperCurve3@4")
Global ClampNoise#(Value#,Lo#,High#) = GetProcAddress(lib, "ClampNoise@12")

lib = 0


Init_Noise() returns a pointer just fine, but it's when you go beyond and use something such as Noise() to generate, that it crashes. I'm assuming Noise() uses some sort of internal array- according to it's FreeBasic function:

Declare FUNCTION Noise alias "Noise" (this AS NoiseGen PTR, x AS SINGLE, y AS SINGLE, z AS SINGLE,Quality as integer) AS SINGLE 

FUNCTION Noise(this as NoiseGen ptr, x AS SINGLE, y AS SINGLE, z AS SINGLE,Quality as integer) AS SINGLE export
    x = 1000 + x: y = 1000 + y: z = 1000 + z
    dim Tlim as integer = limit - 1
	dim x1 as integer = INT(x)
	dim y1 as integer = INT(y)
	dim z1 as integer = INT(z)
	x = x - x1
	y = y - y1
	z = z - z1
    x1 = ((x1+1) and Tlim)
    y1 = ((y1+1) and Tlim)
    z1 = ((z1+1) and Tlim)
    dim as single u,v,w
    select case as const Quality
        case 0
            u = x
            v = y
            w = z
        case 1
            u = Fader((x*fadelimit))
            v = Fader((y*fadelimit))
            w = Fader((z*fadelimit))
        case 2
            u = fade(x)
            v = fade(y)
            w = fade(z)
        case 3
            u = scurve3(x)
            v = scurve3(y)
            w = scurve3(z)
        case 4
            u = scurve5(x)
            v = scurve5(y)
            w = scurve5(z)
    end select
	dim  a as integer = (*this).PermutationBank(x1)+y1
	dim aa as integer = (*this).PermutationBank(a )+z1
	dim ab as integer = (*this).PermutationBank(a+1)+z1
	dim  b as integer = (*this).PermutationBank(x1+1)+y1
	dim ba as integer = (*this).PermutationBank(b)+z1
	dim bb as integer = (*this).PermutationBank(b+1)+z1
	
	dim g1 as single = GradientBank(bb+1)
	dim g2 as single = GradientBank(ab+1)
	dim g3 as single = GradientBank(ba+1)
	dim g4 as single = GradientBank(aa+1)
	dim g5 as single = GradientBank(bb)
	dim g6 as single = GradientBank(ab)
	dim g7 as single = GradientBank(ba)
	dim g8 as single = GradientBank(aa)
	dim l1 as single = lerp(u, g2, g1)
	dim l2 as single = lerp(u, g4, g3)
	dim l3 as single = lerp(v, l2, l1)
	dim l4 as single = lerp(u, g6, g5)
	dim l5 as single = lerp(u, g8, g7)
	dim l6 as single = lerp(v, l5, l4)
	dim l7 as single = lerp(w, l6, l3)
	RETURN l7
END FUNCTION


I figured it was from the _stdcall convention mucking things up and corrupting something internally, but at this point, I haven't went any further into investigating it.


TAS(Posted 2013) [#5]
It's pretty easy to generate your own noise functions. To make it reproducible just record the seed used and reset the seed value to it each time you redraw the image.