Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
Thanks, I'm glad to hear it :)

I've eliminated the shadow issue. I think the takeaway here is "CinemachineSmoothPath does not like being scaled". I had a global .25x scaling factor on the entire cinematic, as a way to bring the giant snowflake model down to a size where it wasn't being swallowed by fog. But that meant that I had positions that were, like, 20k units away from the origin. I pushed the scaling factor down to just the snowflake, and then fixed the path nodes, and now there's no shadow jitter. Very weird. I think the planes are also moving a bit more smoothly, though there's still jitter there. We'll see if it's still bugging me tomorrow, when I have fresh eyes.

Adbot
ADBOT LOVES YOU

Doc Block
Apr 15, 2003
Fun Shoe
Floating-point precision issues caused by large distances, maybe?

jizzy sillage
Aug 13, 2006

yeah if you're 20,000 units away from origin you're gonna start seeing fpp jitter

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
Yeah, but it was more than just that, because if I turned off the Cinemachine stuff, then the jitter went away. So I think there were some extra calculations being done by Cinemachine's positioning/aiming code, that were causing noticeable loss of precision.

The fix is the same in any case

Raenir Salazar
Nov 5, 2010

College Slice
Is Cinemachine a unity plugin for making cinematic shots like that?

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe

Raenir Salazar posted:

Is Cinemachine a unity plugin for making cinematic shots like that?

Cinemachine is a general-purpose camera control system. You have your standard Unity camera, you stick a "Cinemachine brain" on it, then you create "virtual cameras" (empties with CinemachineVirtualCamera components). The highest-priority virtual camera's settings (transform, field of view, near and far clip planes, etc.) are copied to the real camera. You can change priority to switch viewpoints and settings, with a variety of different transitions --- no crossfades, though, which is a shame but understandable given the cost and complexity of rendering two cameras simultaneously. Then the virtual cameras have various settings to determine how they move, how they decide what to look at, etc.

That said, I would not use the camera movement/look-at parts of Cinemachine for gameplay personally, because, while it gives you a fair amount of control, it does not give you absolute control over your camera. Sometimes you need to be able to say "the camera does this, right now", and Cinemachine doesn't let you do that. I still use the virtual cameras in gameplay, but only so I can swap between them with the priority system. For example, I have one virtual camera that is basically stuck on a circular track around the player, and another one that sits inside the player's ship to represent the binoculars view. When I want to switch from orbit to binoculars, I just bump the binoculars' priority.

For cinematics, where you have total control over the scene, though? Go nuts. This is a pretty simple cinematic -- just a single shot, the camera's following a path, while looking at an object that is following a different path. The planes are similarly on paths. Incidentally, CinemachineSmoothPath is a very useful component for basically any time you want to define a 3D curve that passes through a set of points. I use it all the time for all kinds of things that don't necessarily have anything to do with cameras.

Raenir Salazar
Nov 5, 2010

College Slice
Neat! I'll have to look into it when I finally work on my RPG project.

In other news I succeeded in implemented theConnected-Component Labeling algorithm; It feels to me that it isn't inherently different from Sebastian Lague's flood fill algorithm See here but it works.



At first I thought it didn't succeed when the eastern continent was all black but it turns out it was just randomly that colour, I made it so Random was set according to DateTime.Now and I was able to verify it was working fine.

I can probably optimize it further which would hopefully skip over a lot of already scanned items buuuut I went and timed it for the 8k by 4k version and I was waiting a solid three minutes for it to load.



I think this might be part of why I am looking for a Scanline based implementation as I suspect pushing and poping the queue is probably more operations than looping through a row? I think there might be a CCL algorithm variant that does it in a scanline so I'll check that out; but if its still over a minute of twiddling my thumbs I either need to multithread this or put it to the GPU.

Or make it async so it just gradually loads in and I just add like a loading symbol while its coming in. :shrug:

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?
Nicely done.
Edit: Oh I see now this isn't your code. I don't know how closely you copied it, but I'll leave these notes here
Looking at that code you could probably speed it up by merging GetRegionTiles into GetRegions, or at least share mapFlags between them. I see two issues with splitting it up like this:
1) GetRegionTiles allocates it's own mapFlags every run. It would be more efficient to just share a single mapFlags and use a different flag for each region (1,2,3...)
2) Right now in GetRegions takes the output of GetRegionTiles and then marks mapFlags[x,y] = 1; for each tile. If mapFlags was shared this loop could be removed entirely.

If that's not fast enough there are almost certainly faster algorithms out there (the wikipedia "two pass" algorithm looks like it's faster and doesn't use queues).

HappyHippo fucked around with this message at 16:27 on Oct 21, 2021

Raenir Salazar
Nov 5, 2010

College Slice
I'm scared to show my code but here goes, I only kinda borrowed the naming convention, I'm just remarking that logically speaking the algorithms don't seem too different, I hadn't used Sebastian Lague's code for this in a long while except as a very rough guideline for interesting processes. I followed the outline here Here for "One component at a time" CCL.



quote:

1. Start from the first pixel in the image. Set current label to 1. Go to (2).
2. If this pixel is a foreground pixel and it is not already labelled, give it the current label and add it as the first element in a queue, then go to (3). If it is a background pixel or it was already labelled, then repeat (2) for the next pixel in the image.
3. Pop out an element from the queue, and look at its neighbours (based on any type of connectivity). If a neighbour is a foreground pixel and is not already labelled, give it the current label and add it to the queue. Repeat (3) until there are no more elements in the queue.
4. Go to (2) for the next pixel in the image and increment current label by 1.

Code:
code:
// somewhere higher in the code
public struct Coord
{
    public int x;
    public int y;

    public Coord(int InX, int InY)
    {
        x = InX;
        y = InY;
    }
}

    public List<List<Coord>> GetRegions(Color foreground, Texture2D InMap)
    {
        List<List<Coord>> regions = new List<List<Coord>>();        
        
        Texture2D paintedRegionTex = new Texture2D(InMap.width, InMap.height, TextureFormat.RGBA32, false);
        // to output the results to a texture
        NativeArray<Color32> pixelsToPaint = paintedRegionTex.GetRawTextureData<Color32>();
        // xferring the pixels from the texture2d to an array for faster look up
        // as I understand GetPixel(x,y) to be slow?
        NativeArray<Color32> map = InMap.GetRawTextureData<Color32>();        
        

        int maxWidth = InMap.width;
        int maxHeight = InMap.height;
        // for recording our labels
        int[,] labelMap = new int[maxWidth, maxHeight];

        int currentLabel = 0;

        for (int i = 0; i < maxWidth; ++i)
        {
            for (int j = 0; j < maxHeight; ++j)
            {
                // Unreal Engine style for overly long functions
                List<Coord> candidateRegion =
                    GetRegion
                    (
                        new Coord(i, j),
                        map,
                        pixelsToPaint,
                        foreground,
                        ref labelMap,
                        ref currentLabel,
                        maxWidth,
                        maxHeight
                    );

                if (candidateRegion.Count > 0)
                {
                    regions.Add(candidateRegion);
                }
            }
        }

        paintedRegionTex.Apply();

        TextureGenerator.SaveTextureAsPNG(paintedRegionTex, "PaintedRegionMap");

        Debug.Log("Regions: " + regions.Count);

        return regions;
    }
    private List<Coord> GetRegion
    (
        Coord start, 
        NativeArray<Color32> InMap,
        NativeArray<Color32> OutMap,
        Color InForegroundColour,
        ref int[,] InLabelMap,
        ref int currentLabel,
        int InWidth,
        int InHeight
    )
    {
        Queue<Coord> coordQ = new Queue<Coord>();
        // the coords comprising our region/landmass/area
        List<Coord> regionCoords = new List<Coord>();
        // making sure colours are random each time
        Random.InitState((int)System.DateTime.Now.Ticks);
        Color rColour = Random.ColorHSV();
        rColour = new Color(rColour.r, rColour.g, rColour.b, 1);
        
        // I could probably loop through an array of delta's and make this more concise
        if 
        (
            InMap[start.y * InWidth + start.x] == InForegroundColour &&
            InLabelMap[start.x, start.y] == 0
        )
        {
            InLabelMap[start.x, start.y] = currentLabel;
            coordQ.Enqueue(start);
            currentLabel++;
        }

        while (coordQ.Count > 0)
        {
            Coord current = coordQ.Dequeue();
            regionCoords.Add(current);
            OutMap[current.y * InWidth + current.x] = rColour;

            // left
            if 
            (
                IsInMapRange(current.x - 1, current.y, InWidth, InHeight) &&
                InMap[current.y * InWidth + (current.x - 1)] == InForegroundColour &&
                InLabelMap[current.x - 1, current.y] == 0
            )
            {
                InLabelMap[current.x - 1, current.y] = currentLabel;
                coordQ.Enqueue(new Coord(current.x - 1, current.y));
            }
            // right
            if
            (
                IsInMapRange(current.x + 1, current.y, InWidth, InHeight) &&
                InMap[current.y * InWidth + (current.x + 1)] == InForegroundColour &&
                InLabelMap[current.x + 1, current.y] == 0
            )
            {
                InLabelMap[current.x + 1, current.y] = currentLabel;
                coordQ.Enqueue(new Coord(current.x + 1, current.y));
            }
            // top
            if
            (
                IsInMapRange(current.x, current.y + 1, InWidth, InHeight) &&
                InMap[(current.y + 1) * InWidth + current.x] == InForegroundColour &&
                InLabelMap[current.x, current.y + 1] == 0
            )
            {
                InLabelMap[current.x, current.y + 1] = currentLabel;
                coordQ.Enqueue(new Coord(current.x, current.y + 1));
            }
            // bottom
            if
            (
                IsInMapRange(current.x, current.y - 1, InWidth, InHeight) &&
                InMap[(current.y - 1) * InWidth + current.x] == InForegroundColour &&
                InLabelMap[current.x, current.y - 1] == 0
            )
            {
                InLabelMap[current.x, current.y - 1] = currentLabel;
                coordQ.Enqueue(new Coord(current.x, current.y - 1));
            }
        }

        return regionCoords;
    }
A couple of things.

1. The algorithm iterates over a lot of empty space; which will be worse if I were to invert it for water areas since its basically waisting a lot of iterations.
Hence my earlier idea to just do one pass for all tiles and queue'ing up all tiles that don't match to ignore duplicates without incurring additional costs.

2. I'm not sure if a Queue is more expensive than just iterating over a row or column; but changing this to a scanline implementation could be faster than using the Queue?

3. I feel like the checks on the cardinal directions might also be costly that might be averted in a scanline implementation but I'm not sure how expensive it actually is.

4. I think I'm creating and ultimately not using a LOT of lists which might be why its pretty slow as thats 32 MILLION unused lists being made in the 8k by 4k usage.

5. Could move the Random initialization out of GetRegion.

My intuition is that optimizing it might help a lot but probably not enough to make it a tolerable wait to compute it.

xgalaxy
Jan 27, 2004
i write code
Interest thing I didn't know until yesterday:

Supergiant Games Hades was rewritten in C++ during the middle of early access from their C#/XNA code base they've been using since Bastion.
I thought that was a bold decision considering the game was out in early access for some time.

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?

Raenir Salazar posted:

I'm scared to show my code but here goes, I only kinda borrowed the naming convention, I'm just remarking that logically speaking the algorithms don't seem too different, I hadn't used Sebastian Lague's code for this in a long while except as a very rough guideline for interesting processes. I followed the outline here Here for "One component at a time" CCL.

Code:
code:
// somewhere higher in the code
public struct Coord
{
    public int x;
    public int y;

    public Coord(int InX, int InY)
    {
        x = InX;
        y = InY;
    }
}

    public List<List<Coord>> GetRegions(Color foreground, Texture2D InMap)
    {
        List<List<Coord>> regions = new List<List<Coord>>();        
        
        Texture2D paintedRegionTex = new Texture2D(InMap.width, InMap.height, TextureFormat.RGBA32, false);
        // to output the results to a texture
        NativeArray<Color32> pixelsToPaint = paintedRegionTex.GetRawTextureData<Color32>();
        // xferring the pixels from the texture2d to an array for faster look up
        // as I understand GetPixel(x,y) to be slow?
        NativeArray<Color32> map = InMap.GetRawTextureData<Color32>();        
        

        int maxWidth = InMap.width;
        int maxHeight = InMap.height;
        // for recording our labels
        int[,] labelMap = new int[maxWidth, maxHeight];

        int currentLabel = 0;

        for (int i = 0; i < maxWidth; ++i)
        {
            for (int j = 0; j < maxHeight; ++j)
            {
                // Unreal Engine style for overly long functions
                List<Coord> candidateRegion =
                    GetRegion
                    (
                        new Coord(i, j),
                        map,
                        pixelsToPaint,
                        foreground,
                        ref labelMap,
                        ref currentLabel,
                        maxWidth,
                        maxHeight
                    );

                if (candidateRegion.Count > 0)
                {
                    regions.Add(candidateRegion);
                }
            }
        }

        paintedRegionTex.Apply();

        TextureGenerator.SaveTextureAsPNG(paintedRegionTex, "PaintedRegionMap");

        Debug.Log("Regions: " + regions.Count);

        return regions;
    }
    private List<Coord> GetRegion
    (
        Coord start, 
        NativeArray<Color32> InMap,
        NativeArray<Color32> OutMap,
        Color InForegroundColour,
        ref int[,] InLabelMap,
        ref int currentLabel,
        int InWidth,
        int InHeight
    )
    {
        Queue<Coord> coordQ = new Queue<Coord>();
        // the coords comprising our region/landmass/area
        List<Coord> regionCoords = new List<Coord>();
        // making sure colours are random each time
        Random.InitState((int)System.DateTime.Now.Ticks);
        Color rColour = Random.ColorHSV();
        rColour = new Color(rColour.r, rColour.g, rColour.b, 1);
        
        // I could probably loop through an array of delta's and make this more concise
        if 
        (
            InMap[start.y * InWidth + start.x] == InForegroundColour &&
            InLabelMap[start.x, start.y] == 0
        )
        {
            InLabelMap[start.x, start.y] = currentLabel;
            coordQ.Enqueue(start);
            currentLabel++;
        }

        while (coordQ.Count > 0)
        {
            Coord current = coordQ.Dequeue();
            regionCoords.Add(current);
            OutMap[current.y * InWidth + current.x] = rColour;

            // left
            if 
            (
                IsInMapRange(current.x - 1, current.y, InWidth, InHeight) &&
                InMap[current.y * InWidth + (current.x - 1)] == InForegroundColour &&
                InLabelMap[current.x - 1, current.y] == 0
            )
            {
                InLabelMap[current.x - 1, current.y] = currentLabel;
                coordQ.Enqueue(new Coord(current.x - 1, current.y));
            }
            // right
            if
            (
                IsInMapRange(current.x + 1, current.y, InWidth, InHeight) &&
                InMap[current.y * InWidth + (current.x + 1)] == InForegroundColour &&
                InLabelMap[current.x + 1, current.y] == 0
            )
            {
                InLabelMap[current.x + 1, current.y] = currentLabel;
                coordQ.Enqueue(new Coord(current.x + 1, current.y));
            }
            // top
            if
            (
                IsInMapRange(current.x, current.y + 1, InWidth, InHeight) &&
                InMap[(current.y + 1) * InWidth + current.x] == InForegroundColour &&
                InLabelMap[current.x, current.y + 1] == 0
            )
            {
                InLabelMap[current.x, current.y + 1] = currentLabel;
                coordQ.Enqueue(new Coord(current.x, current.y + 1));
            }
            // bottom
            if
            (
                IsInMapRange(current.x, current.y - 1, InWidth, InHeight) &&
                InMap[(current.y - 1) * InWidth + current.x] == InForegroundColour &&
                InLabelMap[current.x, current.y - 1] == 0
            )
            {
                InLabelMap[current.x, current.y - 1] = currentLabel;
                coordQ.Enqueue(new Coord(current.x, current.y - 1));
            }
        }

        return regionCoords;
    }
A couple of things.

1. The algorithm iterates over a lot of empty space; which will be worse if I were to invert it for water areas since its basically waisting a lot of iterations.
Hence my earlier idea to just do one pass for all tiles and queue'ing up all tiles that don't match to ignore duplicates without incurring additional costs.

2. I'm not sure if a Queue is more expensive than just iterating over a row or column; but changing this to a scanline implementation could be faster than using the Queue?

3. I feel like the checks on the cardinal directions might also be costly that might be averted in a scanline implementation but I'm not sure how expensive it actually is.

4. I think I'm creating and ultimately not using a LOT of lists which might be why its pretty slow as thats 32 MILLION unused lists being made in the 8k by 4k usage.

5. Could move the Random initialization out of GetRegion.

My intuition is that optimizing it might help a lot but probably not enough to make it a tolerable wait to compute it.

I think one quick thing might be to move this check:
code:
if 
        (
            InMap[start.y * InWidth + start.x] == InForegroundColour &&
            InLabelMap[start.x, start.y] == 0
        )
out of GetRegion and into the main loop of GetRegions. This check only passes once per region, which from your images looks to be on the order of 20 or so. As you say, you're making 32 million calls to GetRegion, the vast majority of which do nothing, setting up the queues, lists, and running the random number generator all to be thrown away except those 20 or so times they aren't. Put the check in GetRegions and avoid the function call.

Raenir Salazar
Nov 5, 2010

College Slice
Good thinking! I'll give it a shot after work. :)

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?
Two more thoughts:
1) This algorithm works with either a queue or a stack. Profile with both to see if one is faster.
2) In C#, Queue and Stack are built on top of arrays. When you add an item, if there's capacity in the internal array, it's O(1). But if the internal array is out of capacity, a new larger array is allocated, everything is copied, and then the item is added, which is O(n). The capacity is only decreased if you explicitly call TrimExcess. Since your Queue/Stack begins/ends empty, I'd just share one across the calls to GetRegion, to avoid having to redo the expensive reallocations each call.

baby puzzle
Jun 3, 2011

I'll Sequence your Storm.


edit: fixed the img link

baby puzzle fucked around with this message at 02:36 on Oct 22, 2021

Raenir Salazar
Nov 5, 2010

College Slice

The first half had me confused for a moment but it clicked for me with the second half. :)

TIP
Mar 21, 2006

Your move, creep.



Raenir Salazar posted:

The first half of that paragraph made me think things. The second half made me go d'aww.

I mean the first half impression I got could still be true, I haven't googled them yet.

I was really fighting with whether to make the obvious joke lol

Raenir Salazar
Nov 5, 2010

College Slice

Tip posted:

I was really fighting with whether to make the obvious joke lol

Bah!

e: This is what I get for failing to commit and my posting cowardice. :D

Raenir Salazar fucked around with this message at 01:50 on Oct 22, 2021

TIP
Mar 21, 2006

Your move, creep.



guess I wasn't the only one :v:

baby puzzle
Jun 3, 2011

I'll Sequence your Storm.
😤 but also 🤣

Also sorry for Imgur link but I’m too old to know how to post images on mobile

baby puzzle fucked around with this message at 01:51 on Oct 22, 2021

Raenir Salazar
Nov 5, 2010

College Slice
But seriously though who is Daniel Lemon, what game did they make? I'm googling and there is a Disney game developer by that name on Linkedin but nothing else that comes up.

baby puzzle posted:

😤 but also 🤣

Also sorry for Imgur link but I’m too old to know how to post images on mobile

IIRC it should've auto-embedded. :shrug:

giogadi
Oct 27, 2009

xgalaxy posted:

Interest thing I didn't know until yesterday:

Supergiant Games Hades was rewritten in C++ during the middle of early access from their C#/XNA code base they've been using since Bastion.
I thought that was a bold decision considering the game was out in early access for some time.

Do you have a source for this? That sounds absolutely nuts and I’d love to hear more about it. I wonder if “rewriting in c++” means they built an entirely new engine or if they just used native bindings to an existing one

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe

Raenir Salazar posted:

But seriously though who is Daniel Lemon, what game did they make? I'm googling and there is a Disney game developer by that name on Linkedin but nothing else that comes up.

Daniel Lemon is baby puzzle, the person who posted the link, and the creator of SEQUENCE STORM

Raenir Salazar
Nov 5, 2010

College Slice

TooMuchAbstraction posted:

Daniel Lemon is baby puzzle, the person who posted the link, and the creator of SEQUENCE STORM

AAaaaaaaaaah!!!!! I thought baby puzzle was the other person! :D

baby puzzle
Jun 3, 2011

I'll Sequence your Storm.
omg i always thought I'd feel comfortable tying this account to me irl, but seeing it in person is another thing.

Raenir Salazar
Nov 5, 2010

College Slice
What we're learning here though is you need to do more marketing, as your name does not bring up your game when I googled it. :D

baby puzzle
Jun 3, 2011

I'll Sequence your Storm.
I'm in that Gen X category that learned the hard way, early, not to tie real life to anything online. I think that has held me back as far as marketing.

Raenir Salazar
Nov 5, 2010

College Slice
Mood.

xgalaxy
Jan 27, 2004
i write code

giogadi posted:

Do you have a source for this? That sounds absolutely nuts and I’d love to hear more about it. I wonder if “rewriting in c++” means they built an entirely new engine or if they just used native bindings to an existing one

It was in the noclip documentary series about the release of Hades.
They built a new engine on top of The Forge (which is more of a renderer and not a complete engine).
See: https://github.com/ConfettiFX/The-Forge#supergiant-games-hades

Interestingly enough Bethesda's Starfield is also built on top of The Forge.

xgalaxy fucked around with this message at 03:26 on Oct 22, 2021

Raenir Salazar
Nov 5, 2010

College Slice

HappyHippo posted:

I think one quick thing might be to move this check:
code:
        if 
        (
            InMap[start.y * InWidth + start.x] == InForegroundColour &&
            InLabelMap[start.x, start.y] == 0
        )
out of GetRegion and into the main loop of GetRegions. This check only passes once per region, which from your images looks to be on the order of 20 or so. As you say, you're making 32 million calls to GetRegion, the vast majority of which do nothing, setting up the queues, lists, and running the random number generator all to be thrown away except those 20 or so times they aren't. Put the check in GetRegions and avoid the function call.



HappyHippo posted:

Two more thoughts:
1) This algorithm works with either a queue or a stack. Profile with both to see if one is faster.
2) In C#, Queue and Stack are built on top of arrays. When you add an item, if there's capacity in the internal array, it's O(1). But if the internal array is out of capacity, a new larger array is allocated, everything is copied, and then the item is added, which is O(n). The capacity is only decreased if you explicitly call TrimExcess. Since your Queue/Stack begins/ends empty, I'd just share one across the calls to GetRegion, to avoid having to redo the expensive reallocations each call.

Alrighty so I implemented these changes; predictably the first suggestion I think had the largest performance increase. Going from a 1 minute 30ish seconds to just 9 seconds for all land tiles. However inverting it and doing water tiles is still around 30 seconds. So I can expect it on the high side to take around 40 to 42 seconds for the whole map.

Passing in the Queue to use and switching to a Stack in total resulted in saving about 1 second off the total time; fairly significant all things considered but not the biggest dent in terms of the total amount of time.

I don't think my random colour thing takes a lot of time; and didn't really see much of a difference removing it.

I also tried to use structs created by value instead of instantiated using "new" but didn't seem to have a performance difference, total time varied between 28 and 32 seconds regardless.

Here's the water map for funsies:



I think the next change is to try iterating over the entire map using the Stack to track when the processing hits a tile that's not in the same label/colour so instead of iterating over already iterated tiles and skipping them with an if; it should just jump to unprocessed tiles which might be a bit faster but there's still millions of tiles to process and presumably starting at 0,0 and processing the water I imagine was the largest chunk, while skipping over the land tiles probably didn't take long at all since processing land tiles originally was only like 9 seconds so skipping over like 20 million tiles was probably fast; so skipping over 10 million is like twice as fast since it should only take half the time?

My guess is it probably still takes around 35 seconds to do the whole 8k by 4k texture, possibly up to 45s with my proposed change to just do it all at once.

The next step after that if that's the case is to try the two pass algorithm; which in the "faster" variation of it is basically a scanline based algorithm.

After that I probably either need to rely on multithreading or the GPU; but multithreading might be sufficient because if I can get it down to like 10s then having like 8 threads will have it done in like 2 seconds.

more falafel please
Feb 26, 2005

forums poster

xgalaxy posted:

Interest thing I didn't know until yesterday:

Supergiant Games Hades was rewritten in C++ during the middle of early access from their C#/XNA code base they've been using since Bastion.
I thought that was a bold decision considering the game was out in early access for some time.

I had to do this in reverse once, take a game written in an in-house C++ engine with a lot of middleware and lua scripted gameplay that was written for Xbox360 and port it to C#/XNA to run on Windows Phone 7. Project budget was two programmers and eventually one tech artist, it took us 18 months. I learned a lot.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Raenir Salazar posted:

code:
                IsInMapRange(current.x + 1, current.y, InWidth, InHeight) &&
Doing this for every point in every direction is probably adding a second or two to your time - a function call with four parameters and four comparisons inside it is a whole bunch of unnecessary stuff, given that you know the point you were previously at was in the range, and you're only changing one thing.

i.e. For 'up' you could replace this with current.y-1>=0, for 'down' replace it with current.y+1<InHeight, etc.

But scanlines would save you a lot more by making it so you're doing approximately one check per pixel, where right now you're doing four checks every pixel (i.e. you go up, then you immediately do all the checks for "should I go down again", where with scanlines you know you just came from there and don't need to do that.)

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?

Raenir Salazar posted:

Going from a 1 minute 30ish seconds to just 9 seconds for all land tiles.

Nice!

quote:

I also tried to use structs created by value instead of instantiated using "new" but didn't seem to have a performance difference, total time varied between 28 and 32 seconds regardless.
IIRC using new doesn't change whether it's a value type or a reference type. That's determined by whether it's a struct or class.

Also roomforthetuna's suggestion is a good catch, 75% of your bounds checks are unnecessary.

HappyHippo fucked around with this message at 14:58 on Oct 22, 2021

Polio Vax Scene
Apr 5, 2009



more falafel please posted:

I had to do this in reverse once, take a game written in an in-house C++ engine with a lot of middleware and lua scripted gameplay that was written for Xbox360 and port it to C#/XNA to run on Windows Phone 7. Project budget was two programmers and eventually one tech artist, it took us 18 months. I learned a lot.

What did you end up doing as an alternative to the lua? I'm currently struggling to find a decent scripting interface for C#.

chglcu
May 17, 2007

I'm so bored with the USA.
Depending on your target platforms, or if you even need to modify them at runtime on all platforms, you could probably just use C# instead of using a whole separate language and runtime compile on the platforms that support editing.

e: Also depends on how much you trust the people doing the scripting. Might not want to give random players the ability to run arbitrary code.

chglcu fucked around with this message at 15:08 on Oct 22, 2021

Mr Shiny Pants
Nov 12, 2012

Polio Vax Scene posted:

What did you end up doing as an alternative to the lua? I'm currently struggling to find a decent scripting interface for C#.

I've been wanting to use F# for scripting in another project. You can compile FSX files into code during runtime and extend you programs this way.
Don't know if it works the way I imagine it will, but it should be doable.

Like this: https://stackoverflow.com/questions/6859534/how-to-run-a-f-script-in-the-context-of-c-sharp-application

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
Do we have a discord for this thread? Feels like we should.

SQL is such a pain in the rear end when you mix it with Python but at least I'm getting the hang of things.

KillHour
Oct 28, 2007


D34THROW posted:

Do we have a discord for this thread? Feels like we should.

SQL is such a pain in the rear end when you mix it with Python but at least I'm getting the hang of things.

You'd think it wouldn't be that bad since ODBC + Python is like the Official Data Scientist Starter Pack.

xzzy
Mar 5, 2009

SQL is a pain everywhere because it's string building and making strings safely is a very not fun part of programming.

KillHour
Oct 28, 2007


xzzy posted:

SQL is a pain everywhere because it's string building and making strings safely is a very not fun part of programming.

That is true. I work for a database company and we "solved" the issue by creating a translation layer. The upside is that it works really well but the downside is it's yet another thing to learn.

code:
select UserLocation, count(QnATitle) as questionCount
from Samplestack.Contributors
inner join Samplestack.QnA on Asker=ContributorUserName
group by UserLocation
limit 50
is equivalent to

code:
op.fromView('Samplestack', 'Contributors')
  .joinInner(op.fromView('Samplestack','QnA'), op.on('Asker','ContributorUserName'))
  .groupBy('UserLocation', op.count('questionCount', 'QnATitle'))
  .limit(50)
  .result();
It ONLY works in our DB though and, again, it's all custom/proprietary.

Adbot
ADBOT LOVES YOU

more falafel please
Feb 26, 2005

forums poster

Polio Vax Scene posted:

What did you end up doing as an alternative to the lua? I'm currently struggling to find a decent scripting interface for C#.

WP7 doesn't allow *any* native code, so we couldn't use libraries that weren't pure C#. We found a physics middleware that fit the bill (original engine used Bullet, and this was similar enough), but we didn't find a way to do the Lua runtime (there actually was a straight port of the Lua interpreter to C#, but it was ungodly slow and leaked references like a sieve, which means 100ms hitches more than 1x/second).

The good news was they had a strictly enforced Hungarian-style naming scheme for Lua scripts which meant I could use an awful lot of regex to get 90% of the Lua code translated to C# in a couple days, and then spent about a month fixing up everything else. I had also worked on the prequel to this game, so I was very familiar with the way their gameplay script system worked.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply