mirror of
https://github.com/The-Powder-Toy/The-Powder-Toy.git
synced 2025-01-16 22:08:28 +01:00
Change gravity timing and add support for saving it
Timing is now such that the input to the gravity process (the point masses and the mask derived from gravity walls) is passed in BeforeSim and the output of the gravity process (the 2D force field) is taken also in BeforeSim, at the same time. This means that input generated by particles in frame n is passed to the gravity process at the beginning of frame n+1, the corresponding output is available by the beginning of frame n+2, and so it is visible to particles in frame n+2. Thus, gravity is now properly and predictably one frame late, though, sadly, it's displayed two frames late. This is in contrast with the case of not being late at all, which would be the case if input generated in frame n produced visible output by frame n+1, and also in contrast with what we've had until now, which was that gravity was *at least* one frame late, but it could be more out of sync than that if the gravity process for some reason took more time to produce output. Further, both input and output now make it into snapshots and saves, which fixes one half of what causes the phenomenon wherein beams of PHOT under the effect of gravity wiggle for a few frames before stabilizing after a save is loaded. The other half is that velocities are saved at very a low precision, so overall, the effect of this change on this phenomenon is negligible. It is not crucial that a client understands this new piece of info, as clients have been fine not getting gravity data from saves for years. Thus, its presence does not restrict the save to client versions newer than the one that adds this feature. An interesting question this brings up is whether settings, crucially, the enabled state of Newtonian gravity, should be considered simulation state and be also included in saves. Gravity data is now also included in the output of sim.hash, so Newtonian gravity no longer has to be disabled in order to ensure local determinism. The diff is large because I gave up on being non-invasive and renamed important structures that lots of elements use. gravp, which was functionally just hypot(gravx, gravy), was removed, because it was only ever used to display a single value in the HUD. I've also taken a few detours and made changes that really should have been in separate commits, see below, but oh well. This commit also includes a complete rework of the gravity wall area of effect discovery algorithm. The old implementation was extremely broken even beyond the usual C99-isms. Also fix Simulation.NewtonianGravity being read as an integer from powder.pref, even though it's a boolean and is in fact saved as a boolean. It's pure luck that it's worked fine until now. Also introduce PlaneBase for use with PlaneAdapter. PlaneBase is a very budget version of std::span from C++20, so budget in fact that it doesn't even store span size; this is fine because PlaneAdapter knows the span size anyway. std::basic_string_view worked for char types but this new code deals with floats. Also include blockAir data in saves unconditionally. It used to be included only if ensureDeterminism was enabled, like rngState and frameCount, but those last two are conditionally included only because some assumptions are broken if they are included, such as that of expecting a saved simulation to take different paths of evolution after each reload. blockAir has no such effect. Also remove sim.resetGravityField aka tpt.reset_gravity_field because it was agreed that it's really weird that the output of the gravity process can be changed at all from Lua, not to mention that it can only be reset to zero. No scripts to my knowledge ever used these functions.
This commit is contained in:
parent
0345dcf508
commit
7e9d9686dd
10
src/Misc.cpp
10
src/Misc.cpp
@ -76,16 +76,6 @@ void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v)//convert 0-255 RGB value
|
||||
}
|
||||
}
|
||||
|
||||
void membwand(void * destv, void * srcv, size_t destsize, size_t srcsize)
|
||||
{
|
||||
size_t i;
|
||||
unsigned char * dest = (unsigned char*)destv;
|
||||
unsigned char * src = (unsigned char*)srcv;
|
||||
for(i = 0; i < destsize; i++){
|
||||
dest[i] = dest[i] & src[i%srcsize];
|
||||
}
|
||||
}
|
||||
|
||||
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size)
|
||||
{
|
||||
return str.size() == size && !memcmp(str.data(), data, size);
|
||||
|
@ -79,7 +79,6 @@ inline float restrict_flt(float f, float min, float max)
|
||||
|
||||
void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b);
|
||||
void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v);
|
||||
void membwand(void * dest, void * src, size_t destsize, size_t srcsize);
|
||||
|
||||
class ByteString;
|
||||
|
||||
|
@ -189,6 +189,10 @@ void GameSave::setSize(Vec2<int> newBlockSize)
|
||||
ambientHeat = PlaneAdapter<std::vector<float>>(blockSize, 0.0f);
|
||||
blockAir = PlaneAdapter<std::vector<unsigned char>>(blockSize, 0);
|
||||
blockAirh = PlaneAdapter<std::vector<unsigned char>>(blockSize, 0);
|
||||
gravMass = PlaneAdapter<std::vector<float>>(blockSize, 0.f);
|
||||
gravMask = PlaneAdapter<std::vector<uint32_t>>(blockSize, UINT32_C(0xFFFFFFFF));
|
||||
gravForceX = PlaneAdapter<std::vector<float>>(blockSize, 0.f);
|
||||
gravForceY = PlaneAdapter<std::vector<float>>(blockSize, 0.f);
|
||||
}
|
||||
|
||||
std::pair<bool, std::vector<char>> GameSave::Serialise() const
|
||||
@ -305,6 +309,10 @@ void GameSave::Transform(Mat2<int> transform, Vec2<int> nudge)
|
||||
PlaneAdapter<std::vector<float>> newAmbientHeat(newBlockS, 0.0f);
|
||||
PlaneAdapter<std::vector<unsigned char>> newBlockAir(newBlockS, 0);
|
||||
PlaneAdapter<std::vector<unsigned char>> newBlockAirh(newBlockS, 0);
|
||||
PlaneAdapter<std::vector<float>> newGravMass(newBlockS, 0.f);
|
||||
PlaneAdapter<std::vector<uint32_t>> newGravMask(newBlockS, UINT32_C(0xFFFFFFFF));
|
||||
PlaneAdapter<std::vector<float>> newGravForceX(newBlockS, 0.f);
|
||||
PlaneAdapter<std::vector<float>> newGravForceY(newBlockS, 0.f);
|
||||
for (auto bpos : blockSize.OriginRect())
|
||||
{
|
||||
auto newBpos = transform * bpos + btranslate;
|
||||
@ -328,6 +336,10 @@ void GameSave::Transform(Mat2<int> transform, Vec2<int> nudge)
|
||||
newAmbientHeat[newBpos] = ambientHeat[bpos];
|
||||
newBlockAir[newBpos] = blockAir[bpos];
|
||||
newBlockAirh[newBpos] = blockAirh[bpos];
|
||||
newGravMass[newBpos] = gravMass[bpos];
|
||||
newGravMask[newBpos] = gravMask[bpos];
|
||||
newGravForceX[newBpos] = gravForceX[bpos];
|
||||
newGravForceY[newBpos] = gravForceY[bpos];
|
||||
}
|
||||
blockMap = std::move(newBlockMap);
|
||||
fanVelX = std::move(newFanVelX);
|
||||
@ -338,6 +350,10 @@ void GameSave::Transform(Mat2<int> transform, Vec2<int> nudge)
|
||||
ambientHeat = std::move(newAmbientHeat);
|
||||
blockAir = std::move(newBlockAir);
|
||||
blockAirh = std::move(newBlockAirh);
|
||||
gravMass = std::move(newGravMass);
|
||||
gravMask = std::move(newGravMask);
|
||||
gravForceX = std::move(newGravForceX);
|
||||
gravForceY = std::move(newGravForceY);
|
||||
|
||||
blockSize = newBlockS;
|
||||
}
|
||||
@ -424,9 +440,9 @@ void GameSave::readOPS(const std::vector<char> &data)
|
||||
Renderer::PopulateTables();
|
||||
|
||||
unsigned char *inputData = (unsigned char*)&data[0], *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *soapLinkData = NULL;
|
||||
unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL, *blockAirData = nullptr;
|
||||
unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL, *blockAirData = nullptr, *gravityData = nullptr;
|
||||
unsigned int inputDataLen = data.size(), bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen;
|
||||
unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen, blockAirDataLen;
|
||||
unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen, blockAirDataLen, gravityDataLen;
|
||||
unsigned partsCount = 0;
|
||||
unsigned int savedVersion = inputData[4];
|
||||
version = { savedVersion, 0 };
|
||||
@ -537,6 +553,7 @@ void GameSave::readOPS(const std::vector<char> &data)
|
||||
CheckBsonFieldUser(iter, "vyMap", &vyData, &vyDataLen);
|
||||
CheckBsonFieldUser(iter, "ambientMap", &ambientData, &ambientDataLen);
|
||||
CheckBsonFieldUser(iter, "blockAir", &blockAirData, &blockAirDataLen);
|
||||
CheckBsonFieldUser(iter, "gravity", &gravityData, &gravityDataLen);
|
||||
CheckBsonFieldUser(iter, "fanMap", &fanData, &fanDataLen);
|
||||
CheckBsonFieldUser(iter, "soapLinks", &soapLinkData, &soapLinkDataLen);
|
||||
CheckBsonFieldBool(iter, "legacyEnable", &legacyEnable);
|
||||
@ -762,8 +779,7 @@ void GameSave::readOPS(const std::vector<char> &data)
|
||||
//Read wall and fan data
|
||||
if(wallData)
|
||||
{
|
||||
// TODO: use PlaneAdapter<std::span<unsigned char>> once we're C++20
|
||||
auto wallDataPlane = PlaneAdapter<const std::basic_string_view<unsigned char>>(blockS, std::in_place, wallData, blockS.X * blockS.Y);
|
||||
auto wallDataPlane = PlaneAdapter<PlaneBase<const unsigned char>>(blockS, std::in_place, wallData);
|
||||
unsigned int j = 0;
|
||||
if (blockS.X * blockS.Y > int(wallDataLen))
|
||||
throw ParseException(ParseException::Corrupt, "Not enough wall data");
|
||||
@ -875,17 +891,36 @@ void GameSave::readOPS(const std::vector<char> &data)
|
||||
{
|
||||
if (blockS.X * blockS.Y * 2 > int(blockAirDataLen))
|
||||
throw ParseException(ParseException::Corrupt, "Not enough block air data");
|
||||
// TODO: use PlaneAdapter<std::span<unsigned char>> once we're C++20
|
||||
auto blockAirDataPlane = PlaneAdapter<const std::basic_string_view<unsigned char>>(blockS, std::in_place, blockAirData, blockS.X * blockS.Y);
|
||||
auto blockAirhDataPlane = PlaneAdapter<const std::basic_string_view<unsigned char>>(blockS, std::in_place, blockAirData + blockS.X * blockS.Y, blockS.X * blockS.Y);
|
||||
auto blockAirDataPlane = PlaneAdapter<PlaneBase<const unsigned char>>(blockS, std::in_place, blockAirData);
|
||||
auto blockAirhDataPlane = PlaneAdapter<PlaneBase<const unsigned char>>(blockS, std::in_place, blockAirData + blockS.X * blockS.Y);
|
||||
for (auto bpos : blockS.OriginRect().Range<LEFT_TO_RIGHT, TOP_TO_BOTTOM>())
|
||||
{
|
||||
blockAir[blockP + bpos] = blockAirDataPlane[bpos];
|
||||
blockAir [blockP + bpos] = blockAirDataPlane [bpos];
|
||||
blockAirh[blockP + bpos] = blockAirhDataPlane[bpos];
|
||||
}
|
||||
hasBlockAirMaps = true;
|
||||
}
|
||||
|
||||
if (gravityData)
|
||||
{
|
||||
if (blockS.X * blockS.Y * 4 > int(gravityDataLen))
|
||||
{
|
||||
throw ParseException(ParseException::Corrupt, "Not enough gravity data");
|
||||
}
|
||||
auto massDataPlane = PlaneAdapter<PlaneBase<const float >>(blockS, std::in_place, reinterpret_cast<const float *>(gravityData ));
|
||||
auto maskDataPlane = PlaneAdapter<PlaneBase<const uint32_t>>(blockS, std::in_place, reinterpret_cast<const uint32_t *>(gravityData + blockS.X * blockS.Y * sizeof(float)));
|
||||
auto forceXDataPlane = PlaneAdapter<PlaneBase<const float >>(blockS, std::in_place, reinterpret_cast<const float *>(gravityData + 2 * blockS.X * blockS.Y * sizeof(float)));
|
||||
auto forceYDataPlane = PlaneAdapter<PlaneBase<const float >>(blockS, std::in_place, reinterpret_cast<const float *>(gravityData + 3 * blockS.X * blockS.Y * sizeof(float)));
|
||||
for (auto bpos : blockS.OriginRect().Range<LEFT_TO_RIGHT, TOP_TO_BOTTOM>())
|
||||
{
|
||||
gravMass [blockP + bpos] = massDataPlane [bpos];
|
||||
gravMask [blockP + bpos] = maskDataPlane [bpos];
|
||||
gravForceX[blockP + bpos] = forceXDataPlane[bpos];
|
||||
gravForceY[blockP + bpos] = forceYDataPlane[bpos];
|
||||
}
|
||||
hasGravityMaps = true;
|
||||
}
|
||||
|
||||
//Read particle data
|
||||
if (partsData && partsPosData)
|
||||
{
|
||||
@ -1441,8 +1476,7 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
|
||||
}
|
||||
for (auto bpos : RectSized(blockP, blockS).Range<TOP_TO_BOTTOM, LEFT_TO_RIGHT>())
|
||||
{
|
||||
// TODO: use PlaneAdapter<std::span<unsigned char>> once we're C++20
|
||||
auto dataPlane = PlaneAdapter<const std::basic_string_view<unsigned char>>(blockS, std::in_place, data, blockS.X * blockS.Y);
|
||||
auto dataPlane = PlaneAdapter<PlaneBase<const unsigned char>>(blockS, std::in_place, data);
|
||||
if (dataPlane[bpos - blockP]==4||(ver>=44 && dataPlane[bpos - blockP]==O_WL_FAN))
|
||||
{
|
||||
if (p >= dataLength)
|
||||
@ -1452,8 +1486,7 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
|
||||
}
|
||||
for (auto bpos : RectSized(blockP, blockS).Range<TOP_TO_BOTTOM, LEFT_TO_RIGHT>())
|
||||
{
|
||||
// TODO: use PlaneAdapter<std::span<unsigned char>> once we're C++20
|
||||
auto dataPlane = PlaneAdapter<const std::basic_string_view<unsigned char>>(blockS, std::in_place, data, blockS.X * blockS.Y);
|
||||
auto dataPlane = PlaneAdapter<PlaneBase<const unsigned char>>(blockS, std::in_place, data);
|
||||
if (dataPlane[bpos - blockP]==4||(ver>=44 && dataPlane[bpos - blockP]==O_WL_FAN))
|
||||
{
|
||||
if (p >= dataLength)
|
||||
@ -1569,8 +1602,7 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: use PlaneAdapter<std::span<unsigned char>> once we're C++20
|
||||
auto dataPlanePty = PlaneAdapter<const std::basic_string_view<unsigned char>>(partS, std::in_place, data + pty, partS.X * partS.Y);
|
||||
auto dataPlanePty = PlaneAdapter<PlaneBase<const unsigned char>>(partS, std::in_place, data + pty);
|
||||
if (ver>=53) {
|
||||
for (auto pos : partS.OriginRect().Range<TOP_TO_BOTTOM, LEFT_TO_RIGHT>())
|
||||
{
|
||||
@ -1947,8 +1979,17 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
|
||||
std::vector<unsigned char> vxData(blockSize.X*blockSize.Y*2);
|
||||
std::vector<unsigned char> vyData(blockSize.X*blockSize.Y*2);
|
||||
std::vector<unsigned char> ambientData(blockSize.X*blockSize.Y*2, 0);
|
||||
// TODO: have a separate vector with two PlaneAdapter<std::span<unsigned char>>s over it once we're C++20
|
||||
PlaneAdapter<std::vector<unsigned char>> blockAirData({ blockSize.X, blockSize.Y * 2 });
|
||||
|
||||
std::vector<unsigned char> blockAirData(blockSize.X * blockSize.Y * 2);
|
||||
PlaneAdapter<PlaneBase<unsigned char>> blockAirDataPlane (blockSize, std::in_place, &blockAirData[0] );
|
||||
PlaneAdapter<PlaneBase<unsigned char>> blockAirhDataPlane(blockSize, std::in_place, &blockAirData[0] + blockSize.X * blockSize.Y);
|
||||
|
||||
std::vector<unsigned char> gravityData(blockSize.X * blockSize.Y * 4 * sizeof(float));
|
||||
PlaneAdapter<PlaneBase<float >> massDataPlane (blockSize, std::in_place, reinterpret_cast<float *>(&gravityData[0] ));
|
||||
PlaneAdapter<PlaneBase<uint32_t>> maskDataPlane (blockSize, std::in_place, reinterpret_cast<uint32_t *>(&gravityData[0] + blockSize.X * blockSize.Y * sizeof(float)));
|
||||
PlaneAdapter<PlaneBase<float >> forceXDataPlane(blockSize, std::in_place, reinterpret_cast<float *>(&gravityData[0] + 2 * blockSize.X * blockSize.Y * sizeof(float)));
|
||||
PlaneAdapter<PlaneBase<float >> forceYDataPlane(blockSize, std::in_place, reinterpret_cast<float *>(&gravityData[0] + 3 * blockSize.X * blockSize.Y * sizeof(float)));
|
||||
|
||||
unsigned int wallDataLen = blockSize.X*blockSize.Y, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0;
|
||||
|
||||
for (auto bpos : RectSized(blockP, blockS).Range<LEFT_TO_RIGHT, TOP_TO_BOTTOM>())
|
||||
@ -1972,8 +2013,13 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
|
||||
vyData[vyDataLen++] = (unsigned char)((int)(velY*128)&0xFF);
|
||||
vyData[vyDataLen++] = (unsigned char)((int)(velY*128)>>8);
|
||||
|
||||
blockAirData[bpos - blockP] = blockAir[bpos];
|
||||
blockAirData[(bpos - blockP) + Vec2{ 0, blockS.Y }] = blockAirh[bpos];
|
||||
blockAirDataPlane [bpos - blockP] = blockAir [bpos];
|
||||
blockAirhDataPlane[bpos - blockP] = blockAirh[bpos];
|
||||
|
||||
massDataPlane [bpos - blockP] = gravMass [bpos];
|
||||
maskDataPlane [bpos - blockP] = gravMask [bpos];
|
||||
forceXDataPlane[bpos - blockP] = gravForceX[bpos];
|
||||
forceYDataPlane[bpos - blockP] = gravForceY[bpos];
|
||||
}
|
||||
|
||||
if (hasAmbientHeat)
|
||||
@ -2522,14 +2568,18 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
|
||||
bson_append_binary(&b, "ambientMap", (char)BSON_BIN_USER, (const char*)&ambientData[0], ambientDataLen);
|
||||
if (soapLinkDataLen)
|
||||
bson_append_binary(&b, "soapLinks", (char)BSON_BIN_USER, (const char *)&soapLinkData[0], soapLinkDataLen);
|
||||
bson_append_binary(&b, "blockAir", (char)BSON_BIN_USER, (const char *)blockAirData.data(), blockAirData.size());
|
||||
if (ensureDeterminism)
|
||||
{
|
||||
bson_append_bool(&b, "ensureDeterminism", ensureDeterminism);
|
||||
bson_append_binary(&b, "blockAir", (char)BSON_BIN_USER, (const char *)blockAirData.data(), blockAirData.Size().X * blockAirData.Size().Y);
|
||||
bson_append_long(&b, "frameCount", int64_t(frameCount));
|
||||
bson_append_binary(&b, "rngState", (char)BSON_BIN_USER, (const char *)&rngState, sizeof(rngState));
|
||||
RESTRICTVERSION(98, 0);
|
||||
}
|
||||
if (gravityEnable)
|
||||
{
|
||||
bson_append_binary(&b, "gravity", (char)BSON_BIN_USER, (const char *)gravityData.data(), gravityData.size());
|
||||
}
|
||||
unsigned int signsCount = 0;
|
||||
for (size_t i = 0; i < signs.size(); i++)
|
||||
{
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "simulation/Sign.h"
|
||||
#include "simulation/Particle.h"
|
||||
#include "simulation/MissingElements.h"
|
||||
#include "simulation/gravity/GravityData.h"
|
||||
#include "Misc.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <vector>
|
||||
@ -71,7 +72,8 @@ public:
|
||||
Version<2> version{};
|
||||
bool hasPressure = false;
|
||||
bool hasAmbientHeat = false;
|
||||
bool hasBlockAirMaps = false; // only written by readOPS, never read
|
||||
bool hasBlockAirMaps = false;
|
||||
bool hasGravityMaps = false;
|
||||
bool ensureDeterminism = false; // only taken seriously by serializeOPS; readOPS may set this even if the save does not have everything required for determinism
|
||||
bool hasRngState = false; // only written by readOPS, never read
|
||||
RNG::State rngState;
|
||||
@ -89,6 +91,10 @@ public:
|
||||
PlaneAdapter<std::vector<float>> ambientHeat;
|
||||
PlaneAdapter<std::vector<unsigned char>> blockAir;
|
||||
PlaneAdapter<std::vector<unsigned char>> blockAirh;
|
||||
PlaneAdapter<std::vector<float>> gravMass;
|
||||
PlaneAdapter<std::vector<uint32_t>> gravMask;
|
||||
PlaneAdapter<std::vector<float>> gravForceX;
|
||||
PlaneAdapter<std::vector<float>> gravForceY;
|
||||
|
||||
//Simulation Options
|
||||
bool waterEEnabled = false;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
@ -8,6 +9,42 @@
|
||||
|
||||
#include "common/Vec2.h"
|
||||
|
||||
// TODO: std::span once we're C++20
|
||||
template<class Item>
|
||||
struct PlaneBase
|
||||
{
|
||||
Item *base;
|
||||
|
||||
PlaneBase(Item *newBase) : base(newBase)
|
||||
{
|
||||
}
|
||||
|
||||
Item *begin()
|
||||
{
|
||||
return base;
|
||||
}
|
||||
|
||||
const Item *begin() const
|
||||
{
|
||||
return base;
|
||||
}
|
||||
|
||||
Item &operator [](size_t index)
|
||||
{
|
||||
return *(base + index);
|
||||
}
|
||||
|
||||
const Item &operator [](size_t index) const
|
||||
{
|
||||
return *(base + index);
|
||||
}
|
||||
|
||||
const Item *data() const
|
||||
{
|
||||
return base;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr size_t DynamicExtent = std::numeric_limits<size_t>::max();
|
||||
|
||||
template<size_t Extent>
|
||||
|
@ -814,22 +814,18 @@ void Renderer::draw_other() // EMP effect
|
||||
|
||||
void Renderer::draw_grav_zones()
|
||||
{
|
||||
if(!gravityZonesEnabled)
|
||||
return;
|
||||
|
||||
int x, y, i, j;
|
||||
for (y=0; y<YCELLS; y++)
|
||||
if (!gravityZonesEnabled)
|
||||
{
|
||||
for (x=0; x<XCELLS; x++)
|
||||
return;
|
||||
}
|
||||
for (auto p : CELLS.OriginRect())
|
||||
{
|
||||
if (sim->gravIn.mask[p])
|
||||
{
|
||||
if(sim->grav->gravmask[y*XCELLS+x])
|
||||
auto np = p * CELL;
|
||||
for (auto o : Vec2{ CELL, CELL }.OriginRect())
|
||||
{
|
||||
for (j=0; j<CELL; j++)//draws the colors
|
||||
for (i=0; i<CELL; i++)
|
||||
if(i == j)
|
||||
BlendPixel({ x*CELL+i, y*CELL+j }, 0xFFC800_rgb .WithAlpha(120));
|
||||
else
|
||||
BlendPixel({ x*CELL+i, y*CELL+j }, 0x202020_rgb .WithAlpha(120));
|
||||
BlendPixel(np + o, (o.X == o.Y ? 0xFFC800_rgb : 0x202020_rgb).WithAlpha(120));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -837,28 +833,26 @@ void Renderer::draw_grav_zones()
|
||||
|
||||
void Renderer::draw_grav()
|
||||
{
|
||||
int x, y, i, ca;
|
||||
float nx, ny, dist;
|
||||
|
||||
if(!gravityFieldEnabled)
|
||||
return;
|
||||
|
||||
for (y=0; y<YCELLS; y++)
|
||||
if (!gravityFieldEnabled)
|
||||
{
|
||||
for (x=0; x<XCELLS; x++)
|
||||
return;
|
||||
}
|
||||
for (auto p : CELLS.OriginRect())
|
||||
{
|
||||
auto gx = sim->gravOut.forceX[p];
|
||||
auto gy = sim->gravOut.forceY[p];
|
||||
auto agx = std::abs(gx);
|
||||
auto agy = std::abs(gy);
|
||||
if (agx <= 0.001f && agy <= 0.001f)
|
||||
{
|
||||
ca = y*XCELLS+x;
|
||||
if(fabsf(sim->gravx[ca]) <= 0.001f && fabsf(sim->gravy[ca]) <= 0.001f)
|
||||
continue;
|
||||
nx = float(x*CELL);
|
||||
ny = float(y*CELL);
|
||||
dist = fabsf(sim->gravy[ca])+fabsf(sim->gravx[ca]);
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
nx -= sim->gravx[ca]*0.5f;
|
||||
ny -= sim->gravy[ca]*0.5f;
|
||||
AddPixel({ int(nx+0.5f), int(ny+0.5f) }, 0xFFFFFF_rgb .WithAlpha(int(dist*20.0f)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto np = Vec2<float>(p.X * CELL, p.Y * CELL);
|
||||
auto dist = agx + agy;
|
||||
for (auto i = 0; i < 4; ++i)
|
||||
{
|
||||
np -= Vec2{ gx * 0.5f, gy * 0.5f };
|
||||
AddPixel({ int(np.X + 0.5f), int(np.Y + 0.5f) }, 0xFFFFFF_rgb .WithAlpha(int(dist * 20.0f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,30 @@ void Renderer::RenderZoom()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Renderer::render_gravlensing(const Video &source)
|
||||
{
|
||||
for (auto p : RES.OriginRect())
|
||||
{
|
||||
auto cp = p / CELL;
|
||||
auto rp = Vec2{ int(p.X - sim->gravOut.forceX[cp] * 0.75f + 0.5f), int(p.Y - sim->gravOut.forceY[cp] * 0.75f + 0.5f) };
|
||||
auto gp = Vec2{ int(p.X - sim->gravOut.forceX[cp] * 0.875f + 0.5f), int(p.Y - sim->gravOut.forceY[cp] * 0.875f + 0.5f) };
|
||||
auto bp = Vec2{ int(p.X - sim->gravOut.forceX[cp] + 0.5f), int(p.Y - sim->gravOut.forceY[cp] + 0.5f) };
|
||||
if (RES.OriginRect().Contains(rp) &&
|
||||
RES.OriginRect().Contains(gp) &&
|
||||
RES.OriginRect().Contains(bp))
|
||||
{
|
||||
auto v = RGB<uint8_t>::Unpack(video[p]);
|
||||
auto s = RGB<uint8_t>::Unpack(source[rp]);
|
||||
video[p] = RGB<uint8_t>(
|
||||
std::min(0xFF, s.Red + v.Red ),
|
||||
std::min(0xFF, s.Green + v.Green),
|
||||
std::min(0xFF, s.Blue + v.Blue )
|
||||
).Pack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::DrawBlob(Vec2<int> pos, RGB<uint8_t> colour)
|
||||
{
|
||||
BlendPixel(pos + Vec2{ +1, 0 }, colour.WithAlpha(112));
|
||||
@ -108,33 +132,6 @@ void Renderer::DrawBlob(Vec2<int> pos, RGB<uint8_t> colour)
|
||||
BlendPixel(pos + Vec2{ -1, +1 }, colour.WithAlpha(64));
|
||||
}
|
||||
|
||||
|
||||
void Renderer::render_gravlensing(const Video &source)
|
||||
{
|
||||
int nx, ny, rx, ry, gx, gy, bx, by, co;
|
||||
for(nx = 0; nx < XRES; nx++)
|
||||
{
|
||||
for(ny = 0; ny < YRES; ny++)
|
||||
{
|
||||
co = (ny/CELL)*XCELLS+(nx/CELL);
|
||||
rx = (int)(nx-sim->gravx[co]*0.75f+0.5f);
|
||||
ry = (int)(ny-sim->gravy[co]*0.75f+0.5f);
|
||||
gx = (int)(nx-sim->gravx[co]*0.875f+0.5f);
|
||||
gy = (int)(ny-sim->gravy[co]*0.875f+0.5f);
|
||||
bx = (int)(nx-sim->gravx[co]+0.5f);
|
||||
by = (int)(ny-sim->gravy[co]+0.5f);
|
||||
if(rx >= 0 && rx < XRES && ry >= 0 && ry < YRES && gx >= 0 && gx < XRES && gy >= 0 && gy < YRES && bx >= 0 && bx < XRES && by >= 0 && by < YRES)
|
||||
{
|
||||
auto t = RGB<uint8_t>::Unpack(video[{ nx, ny }]);
|
||||
t.Red = std::min(0xFF, (int)RGB<uint8_t>::Unpack(source[{ rx, ry }]).Red + t.Red);
|
||||
t.Green = std::min(0xFF, (int)RGB<uint8_t>::Unpack(source[{ gx, gy }]).Green + t.Green);
|
||||
t.Blue = std::min(0xFF, (int)RGB<uint8_t>::Unpack(source[{ bx, by }]).Blue + t.Blue);
|
||||
video[{ nx, ny }] = t.Pack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float temp[CELL*3][CELL*3];
|
||||
float fire_alphaf[CELL*3][CELL*3];
|
||||
float glow_alphaf[11][11];
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Config.h"
|
||||
#include "GameModel.h"
|
||||
#include "BitmapBrush.h"
|
||||
#include "EllipseBrush.h"
|
||||
@ -106,9 +107,10 @@ GameModel::GameModel():
|
||||
sim->air->ambientAirTemp = ambientAirTemp;
|
||||
decoSpace = prefs.Get("Simulation.DecoSpace", NUM_DECOSPACES, DECOSPACE_SRGB);
|
||||
sim->SetDecoSpace(decoSpace);
|
||||
int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", NUM_GRAVMODES, GRAV_VERTICAL);
|
||||
if (ngrav_enable)
|
||||
sim->grav->start_grav_async();
|
||||
if (prefs.Get("Simulation.NewtonianGravity", false))
|
||||
{
|
||||
sim->EnableNewtonianGravity(true);
|
||||
}
|
||||
sim->aheat_enable = prefs.Get("Simulation.AmbientHeat", 0); // TODO: AmbientHeat enum
|
||||
sim->pretty_powder = prefs.Get("Simulation.PrettyPowder", 0); // TODO: PrettyPowder enum
|
||||
|
||||
@ -166,7 +168,7 @@ GameModel::~GameModel()
|
||||
prefs.Set("Renderer.GravityField", (bool)ren->gravityFieldEnabled);
|
||||
prefs.Set("Renderer.Decorations", (bool)ren->decorations_enable);
|
||||
prefs.Set("Renderer.DebugMode", ren->debugLines); //These two should always be equivalent, even though they are different things
|
||||
prefs.Set("Simulation.NewtonianGravity", sim->grav->IsEnabled());
|
||||
prefs.Set("Simulation.NewtonianGravity", bool(sim->grav));
|
||||
prefs.Set("Simulation.AmbientHeat", sim->aheat_enable);
|
||||
prefs.Set("Simulation.PrettyPowder", sim->pretty_powder);
|
||||
prefs.Set("Decoration.Red", (int)colour.Red);
|
||||
@ -967,14 +969,7 @@ void GameModel::SaveToSimParameters(const GameSave &saveData)
|
||||
sim->legacy_enable = saveData.legacyEnable;
|
||||
sim->water_equal_test = saveData.waterEEnabled;
|
||||
sim->aheat_enable = saveData.aheatEnable;
|
||||
if (saveData.gravityEnable && !sim->grav->IsEnabled())
|
||||
{
|
||||
sim->grav->start_grav_async();
|
||||
}
|
||||
else if (!saveData.gravityEnable && sim->grav->IsEnabled())
|
||||
{
|
||||
sim->grav->stop_grav_async();
|
||||
}
|
||||
sim->EnableNewtonianGravity(saveData.gravityEnable);
|
||||
sim->frameCount = saveData.frameCount;
|
||||
if (saveData.hasRngState)
|
||||
{
|
||||
@ -1296,14 +1291,13 @@ void GameModel::ResetAHeat()
|
||||
|
||||
void GameModel::SetNewtonianGravity(bool newtonainGravity)
|
||||
{
|
||||
sim->EnableNewtonianGravity(newtonainGravity);
|
||||
if (newtonainGravity)
|
||||
{
|
||||
sim->grav->start_grav_async();
|
||||
SetInfoTip("Newtonian Gravity: On");
|
||||
}
|
||||
else
|
||||
{
|
||||
sim->grav->stop_grav_async();
|
||||
SetInfoTip("Newtonian Gravity: Off");
|
||||
}
|
||||
UpdateQuickOptions();
|
||||
@ -1311,7 +1305,7 @@ void GameModel::SetNewtonianGravity(bool newtonainGravity)
|
||||
|
||||
bool GameModel::GetNewtonianGrvity()
|
||||
{
|
||||
return sim->grav->IsEnabled();
|
||||
return bool(sim->grav);
|
||||
}
|
||||
|
||||
void GameModel::ShowGravityGrid(bool showGrid)
|
||||
|
@ -2457,7 +2457,9 @@ void GameView::OnDraw()
|
||||
|
||||
sampleInfo << "X:" << sample.PositionX << " Y:" << sample.PositionY;
|
||||
|
||||
if (sample.Gravity)
|
||||
auto gravtot = std::abs(sample.GravityVelocityX) +
|
||||
std::abs(sample.GravityVelocityY);
|
||||
if (gravtot)
|
||||
sampleInfo << ", GX: " << sample.GravityVelocityX << " GY: " << sample.GravityVelocityY;
|
||||
|
||||
if (c->GetAHeatEnable())
|
||||
|
@ -43,15 +43,12 @@ void OptionsModel::SetAmbientHeatSimulation(bool state)
|
||||
|
||||
bool OptionsModel::GetNewtonianGravity()
|
||||
{
|
||||
return sim->grav->IsEnabled();
|
||||
return bool(sim->grav);
|
||||
}
|
||||
|
||||
void OptionsModel::SetNewtonianGravity(bool state)
|
||||
{
|
||||
if(state)
|
||||
sim->grav->start_grav_async();
|
||||
else
|
||||
sim->grav->stop_grav_async();
|
||||
sim->EnableNewtonianGravity(state);
|
||||
notifySettingsChanged();
|
||||
}
|
||||
|
||||
|
@ -52,14 +52,10 @@ static int newtonianGravity(lua_State *L)
|
||||
int acount = lua_gettop(L);
|
||||
if (acount == 0)
|
||||
{
|
||||
lua_pushboolean(L, lsi->sim->grav->IsEnabled());
|
||||
lua_pushboolean(L, bool(lsi->sim->grav));
|
||||
return 1;
|
||||
}
|
||||
int gravstate = lua_toboolean(L, 1);
|
||||
if(gravstate)
|
||||
lsi->sim->grav->start_grav_async();
|
||||
else
|
||||
lsi->sim->grav->stop_grav_async();
|
||||
lsi->sim->EnableNewtonianGravity(lua_toboolean(L, 1));
|
||||
lsi->gameModel->UpdateQuickOptions();
|
||||
return 0;
|
||||
}
|
||||
@ -205,7 +201,7 @@ static int gravityMass(lua_State *L)
|
||||
{
|
||||
auto *lsi = GetLSI();
|
||||
return LuaBlockMap(L, [lsi](Vec2<int> p) -> float & {
|
||||
return lsi->sim->gravmap[p.Y * XCELLS + p.X];
|
||||
return lsi->sim->gravIn.mass[p];
|
||||
});
|
||||
}
|
||||
|
||||
@ -217,8 +213,8 @@ static int gravityField(lua_State *L)
|
||||
{
|
||||
return luaL_error(L, "Coordinates (%i, %i) out of range", pos.X, pos.Y);
|
||||
}
|
||||
lua_pushnumber(L, lsi->sim->gravx[pos.Y * XCELLS + pos.X]);
|
||||
lua_pushnumber(L, lsi->sim->gravy[pos.Y * XCELLS + pos.X]);
|
||||
lua_pushnumber(L, lsi->sim->gravOut.forceX[pos]);
|
||||
lua_pushnumber(L, lsi->sim->gravOut.forceY[pos]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@ -1798,34 +1794,6 @@ static int resetSpark(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resetGravityField(lua_State *L)
|
||||
{
|
||||
int nx, ny;
|
||||
int x1, y1, width, height;
|
||||
x1 = abs(luaL_optint(L, 1, 0));
|
||||
y1 = abs(luaL_optint(L, 2, 0));
|
||||
width = abs(luaL_optint(L, 3, XCELLS));
|
||||
height = abs(luaL_optint(L, 4, YCELLS));
|
||||
if(x1 > XCELLS-1)
|
||||
x1 = XCELLS-1;
|
||||
if(y1 > YCELLS-1)
|
||||
y1 = YCELLS-1;
|
||||
if(x1+width > XCELLS-1)
|
||||
width = XCELLS-x1;
|
||||
if(y1+height > YCELLS-1)
|
||||
height = YCELLS-y1;
|
||||
auto *lsi = GetLSI();
|
||||
auto *sim = lsi->sim;
|
||||
for (nx = x1; nx<x1+width; nx++)
|
||||
for (ny = y1; ny<y1+height; ny++)
|
||||
{
|
||||
sim->gravx[ny*XCELLS+nx] = 0;
|
||||
sim->gravy[ny*XCELLS+nx] = 0;
|
||||
sim->gravp[ny*XCELLS+nx] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int randomSeed(lua_State *L)
|
||||
{
|
||||
auto *lsi = GetLSI();
|
||||
@ -1946,7 +1914,6 @@ void LuaSimulation::Open(lua_State *L)
|
||||
LFUNC(paused),
|
||||
LFUNC(gravityMass),
|
||||
LFUNC(gravityField),
|
||||
LFUNC(resetGravityField),
|
||||
LFUNC(resetSpark),
|
||||
LFUNC(resetVelocity),
|
||||
LFUNC(wallMap),
|
||||
|
@ -47,7 +47,6 @@ tpt.get_name = tpt.getUserName
|
||||
tpt.menu_enabled = ui.menuEnabled
|
||||
tpt.num_menus = ui.numMenus
|
||||
tpt.perfectCircleBrush = ui.perfectCircleBrush
|
||||
tpt.reset_gravity_field = sim.resetGravityField
|
||||
tpt.reset_spark = sim.resetSpark
|
||||
tpt.reset_velocity = sim.resetVelocity
|
||||
tpt.set_clipboard = plat.clipboardPaste
|
||||
|
@ -26,16 +26,15 @@ std::unique_ptr<Snapshot> Simulation::CreateSnapshot() const
|
||||
snap->BlockAirH .insert (snap->BlockAirH .begin(), &air->bmap_blockairh[0][0], &air->bmap_blockairh[0][0] + NCELL);
|
||||
snap->FanVelocityX .insert (snap->FanVelocityX .begin(), &fvx [0][0] , &fvx [0][0] + NCELL);
|
||||
snap->FanVelocityY .insert (snap->FanVelocityY .begin(), &fvy [0][0] , &fvy [0][0] + NCELL);
|
||||
snap->GravVelocityX .insert (snap->GravVelocityX .begin(), &gravx [0] , &gravx [0] + NCELL);
|
||||
snap->GravVelocityY .insert (snap->GravVelocityY .begin(), &gravy [0] , &gravy [0] + NCELL);
|
||||
snap->GravValue .insert (snap->GravValue .begin(), &gravp [0] , &gravp [0] + NCELL);
|
||||
snap->GravMap .insert (snap->GravMap .begin(), &gravmap[0] , &gravmap[0] + NCELL);
|
||||
snap->Particles .insert (snap->Particles .begin(), &parts [0] , &parts [0] + parts_lastActiveIndex + 1);
|
||||
snap->PortalParticles.insert (snap->PortalParticles.begin(), &portalp[0][0][0], &portalp[0][0][0] + CHANNELS * 8 * 80);
|
||||
snap->WirelessData .insert (snap->WirelessData .begin(), &wireless[0][0] , &wireless[0][0] + CHANNELS * 2);
|
||||
snap->stickmen .insert (snap->stickmen .begin(), &fighters[0] , &fighters[0] + MAX_FIGHTERS);
|
||||
snap->stickmen .push_back(player2);
|
||||
snap->stickmen .push_back(player);
|
||||
snap->GravMass .insert(snap->GravMass .begin(), &gravIn.mass[{ 0, 0 }] , &gravIn.mass[{ 0, 0 }] + NCELL);
|
||||
snap->GravForceX.insert(snap->GravForceX.begin(), &gravOut.forceX[{ 0, 0 }], &gravOut.forceX[{ 0, 0 }] + NCELL);
|
||||
snap->GravForceY.insert(snap->GravForceY.begin(), &gravOut.forceY[{ 0, 0 }], &gravOut.forceY[{ 0, 0 }] + NCELL);
|
||||
snap->signs = signs;
|
||||
snap->FrameCount = frameCount;
|
||||
snap->RngState = rng.state();
|
||||
@ -61,26 +60,27 @@ void Simulation::Restore(const Snapshot &snap)
|
||||
std::copy(snap.BlockAirH .begin(), snap.BlockAirH .end(), &air->bmap_blockairh[0][0]);
|
||||
std::copy(snap.FanVelocityX .begin(), snap.FanVelocityX .end(), &fvx[0][0] );
|
||||
std::copy(snap.FanVelocityY .begin(), snap.FanVelocityY .end(), &fvy[0][0] );
|
||||
if (grav->IsEnabled())
|
||||
{
|
||||
grav->Clear();
|
||||
std::copy(snap.GravVelocityX.begin(), snap.GravVelocityX.end(), &gravx [0] );
|
||||
std::copy(snap.GravVelocityY.begin(), snap.GravVelocityY.end(), &gravy [0] );
|
||||
std::copy(snap.GravValue .begin(), snap.GravValue .end(), &gravp [0] );
|
||||
std::copy(snap.GravMap .begin(), snap.GravMap .end(), &gravmap[0] );
|
||||
}
|
||||
std::copy(snap.Particles .begin(), snap.Particles .end(), &parts[0] );
|
||||
std::copy(snap.PortalParticles.begin(), snap.PortalParticles.end(), &portalp[0][0][0]);
|
||||
std::copy(snap.WirelessData .begin(), snap.WirelessData .end(), &wireless[0][0] );
|
||||
std::copy(snap.stickmen .begin(), snap.stickmen.end() - 2 , &fighters[0] );
|
||||
player = snap.stickmen[snap.stickmen.size() - 1];
|
||||
player2 = snap.stickmen[snap.stickmen.size() - 2];
|
||||
{
|
||||
GravityInput newGravIn;
|
||||
GravityOutput newGravOut;
|
||||
std::copy(snap.GravMass .begin(), snap.GravMass .end(), &newGravIn.mass[{ 0, 0 }] );
|
||||
std::copy(snap.GravForceX.begin(), snap.GravForceX.end(), &newGravOut.forceX[{ 0, 0 }]);
|
||||
std::copy(snap.GravForceY.begin(), snap.GravForceY.end(), &newGravOut.forceY[{ 0, 0 }]);
|
||||
// we apply the old grav values but Newtonian gravity enable state is not part of the snapshot so this may be pointless
|
||||
// TODO: maybe track settings like Newtonian gravity enable state in the history
|
||||
ResetNewtonianGravity(newGravIn, newGravOut);
|
||||
}
|
||||
signs = snap.signs;
|
||||
frameCount = snap.FrameCount;
|
||||
rng.state(snap.RngState);
|
||||
parts_lastActiveIndex = NPART - 1;
|
||||
RecalcFreeParticles(false);
|
||||
gravWallChanged = true;
|
||||
}
|
||||
|
||||
void Simulation::clear_area(int area_x, int area_y, int area_w, int area_h)
|
||||
@ -120,6 +120,7 @@ void Simulation::clear_area(int area_x, int area_y, int area_w, int area_h)
|
||||
SimulationSample Simulation::GetSample(int x, int y)
|
||||
{
|
||||
SimulationSample sample;
|
||||
sample.particle.type = 0;
|
||||
sample.PositionX = x;
|
||||
sample.PositionY = y;
|
||||
if (x >= 0 && x < XRES && y >= 0 && y < YRES)
|
||||
@ -143,11 +144,10 @@ SimulationSample Simulation::GetSample(int x, int y)
|
||||
sample.AirVelocityX = vx[y/CELL][x/CELL];
|
||||
sample.AirVelocityY = vy[y/CELL][x/CELL];
|
||||
|
||||
if(grav->IsEnabled())
|
||||
if (grav)
|
||||
{
|
||||
sample.Gravity = gravp[(y/CELL)*XCELLS+(x/CELL)];
|
||||
sample.GravityVelocityX = gravx[(y/CELL)*XCELLS+(x/CELL)];
|
||||
sample.GravityVelocityY = gravy[(y/CELL)*XCELLS+(x/CELL)];
|
||||
sample.GravityVelocityX = gravOut.forceX[Vec2{ x, y } / CELL];
|
||||
sample.GravityVelocityY = gravOut.forceY[Vec2{ x, y } / CELL];
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1,24 +1,21 @@
|
||||
#pragma once
|
||||
#include "Particle.h"
|
||||
|
||||
class SimulationSample
|
||||
struct SimulationSample
|
||||
{
|
||||
public:
|
||||
Particle particle;
|
||||
int ParticleID;
|
||||
int PositionX, PositionY;
|
||||
float AirPressure;
|
||||
float AirTemperature;
|
||||
float AirVelocityX;
|
||||
float AirVelocityY;
|
||||
int ParticleID = 0;
|
||||
int PositionX = 0;
|
||||
int PositionY = 0;
|
||||
float AirPressure = 0;
|
||||
float AirTemperature = 0;
|
||||
float AirVelocityX = 0;
|
||||
float AirVelocityY = 0;
|
||||
|
||||
int WallType;
|
||||
float Gravity;
|
||||
float GravityVelocityX;
|
||||
float GravityVelocityY;
|
||||
int WallType = 0;
|
||||
float GravityVelocityX = 0;
|
||||
float GravityVelocityY = 0;
|
||||
|
||||
int NumParts;
|
||||
bool isMouseInSim;
|
||||
|
||||
SimulationSample() : particle(), ParticleID(0), PositionX(0), PositionY(0), AirPressure(0), AirTemperature(0), AirVelocityX(0), AirVelocityY(0), WallType(0), Gravity(0), GravityVelocityX(0), GravityVelocityY(0), NumParts(0), isMouseInSim(true) {}
|
||||
int NumParts = 0;
|
||||
bool isMouseInSim = true;
|
||||
};
|
||||
|
@ -261,6 +261,7 @@ void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> bloc
|
||||
signs.push_back(tempSign);
|
||||
}
|
||||
}
|
||||
auto useGravityMaps = save->hasGravityMaps && grav;
|
||||
for (auto bpos : RectSized(blockP, save->blockSize) & CELLS.OriginRect())
|
||||
{
|
||||
auto spos = bpos - blockP;
|
||||
@ -284,10 +285,21 @@ void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> bloc
|
||||
}
|
||||
if (save->hasBlockAirMaps)
|
||||
{
|
||||
air->bmap_blockair[bpos.Y][bpos.X] = save->blockAir[spos];
|
||||
air->bmap_blockair [bpos.Y][bpos.X] = save->blockAir [spos];
|
||||
air->bmap_blockairh[bpos.Y][bpos.X] = save->blockAirh[spos];
|
||||
}
|
||||
}
|
||||
if (useGravityMaps)
|
||||
{
|
||||
gravIn.mass [bpos] = save->gravMass [spos];
|
||||
gravIn.mask [bpos] = save->gravMask [spos];
|
||||
gravOut.forceX[bpos] = save->gravForceX[spos];
|
||||
gravOut.forceY[bpos] = save->gravForceY[spos];
|
||||
}
|
||||
}
|
||||
if (useGravityMaps)
|
||||
{
|
||||
ResetNewtonianGravity(gravIn, gravOut);
|
||||
}
|
||||
|
||||
gravWallChanged = true;
|
||||
@ -398,6 +410,21 @@ std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> partR
|
||||
newSave->blockAir[bpos] = air->bmap_blockair[bpos.Y + blockP.Y][bpos.X + blockP.X];
|
||||
newSave->blockAirh[bpos] = air->bmap_blockairh[bpos.Y + blockP.Y][bpos.X + blockP.X];
|
||||
}
|
||||
if (grav)
|
||||
{
|
||||
newSave->gravMass [bpos] = gravIn.mass [bpos + blockP];
|
||||
newSave->gravMask [bpos] = gravIn.mask [bpos + blockP];
|
||||
newSave->gravForceX[bpos] = gravOut.forceX[bpos + blockP];
|
||||
newSave->gravForceY[bpos] = gravOut.forceY[bpos + blockP];
|
||||
}
|
||||
}
|
||||
if (includePressure)
|
||||
{
|
||||
newSave->hasBlockAirMaps = true;
|
||||
}
|
||||
if (grav)
|
||||
{
|
||||
newSave->hasGravityMaps = true;
|
||||
}
|
||||
if (includePressure || ensureDeterminism)
|
||||
{
|
||||
@ -433,7 +460,7 @@ void Simulation::SaveSimOptions(GameSave &gameSave)
|
||||
gameSave.edgeMode = edgeMode;
|
||||
gameSave.legacyEnable = legacy_enable;
|
||||
gameSave.waterEEnabled = water_equal_test;
|
||||
gameSave.gravityEnable = grav->IsEnabled();
|
||||
gameSave.gravityEnable = bool(grav);
|
||||
gameSave.aheatEnable = aheat_enable;
|
||||
}
|
||||
|
||||
@ -1005,8 +1032,7 @@ void Simulation::clear_sim(void)
|
||||
//memset(fire_b, 0, sizeof(fire_b));
|
||||
//if(gravmask)
|
||||
//memset(gravmask, 0xFFFFFFFF, NCELL*sizeof(unsigned));
|
||||
if(grav)
|
||||
grav->Clear();
|
||||
ResetNewtonianGravity({}, {});
|
||||
if(air)
|
||||
{
|
||||
air->Clear();
|
||||
@ -1959,8 +1985,8 @@ void Simulation::GetGravityField(int x, int y, float particleGrav, float newtonG
|
||||
}
|
||||
if (newtonGrav)
|
||||
{
|
||||
pGravX += newtonGrav*gravx[(y/CELL)*XCELLS+(x/CELL)];
|
||||
pGravY += newtonGrav*gravy[(y/CELL)*XCELLS+(x/CELL)];
|
||||
pGravX += newtonGrav * gravOut.forceX[Vec2{ x, y } / CELL];
|
||||
pGravY += newtonGrav * gravOut.forceY[Vec2{ x, y } / CELL];
|
||||
}
|
||||
}
|
||||
|
||||
@ -2791,7 +2817,8 @@ void Simulation::UpdateParticles(int start, int end)
|
||||
|
||||
{
|
||||
auto s = 1;
|
||||
auto gravtot = fabs(gravy[(y/CELL)*XCELLS+(x/CELL)])+fabs(gravx[(y/CELL)*XCELLS+(x/CELL)]);
|
||||
auto gravtot = std::abs(gravOut.forceX[Vec2{ x, y } / CELL]) +
|
||||
std::abs(gravOut.forceY[Vec2{ x, y } / CELL]);
|
||||
if (elements[t].HighPressureTransition != NT && pv[y/CELL][x/CELL]>elements[t].HighPressure) {
|
||||
// particle type change due to high pressure
|
||||
if (elements[t].HighPressureTransition != ST)
|
||||
@ -3171,7 +3198,7 @@ killed:
|
||||
goto movedone;
|
||||
}
|
||||
}
|
||||
if (elements[t].Falldown>1 && !grav->IsEnabled() && gravityMode==GRAV_VERTICAL && parts[i].vy>fabsf(parts[i].vx))
|
||||
if (elements[t].Falldown>1 && !grav && gravityMode==GRAV_VERTICAL && parts[i].vy>fabsf(parts[i].vx))
|
||||
{
|
||||
auto s = 0;
|
||||
// stagnant is true if FLAG_STAGNANT was set for this particle in previous frame
|
||||
@ -3675,6 +3702,48 @@ void Simulation::CheckStacking()
|
||||
}
|
||||
}
|
||||
|
||||
void Simulation::UpdateGravityMask()
|
||||
{
|
||||
for (auto p : CELLS.OriginRect())
|
||||
{
|
||||
gravIn.mask[p] = 0;
|
||||
}
|
||||
std::stack<Vec2<int>> toCheck;
|
||||
auto check = [this, &toCheck](Vec2<int> p) {
|
||||
if (!(bmap[p.Y][p.X] == WL_GRAV || gravIn.mask[p]))
|
||||
{
|
||||
gravIn.mask[p] = UINT32_C(0xFFFFFFFF);
|
||||
for (auto o : RectSized<int>({ -1, -1 }, { 3, 3 }))
|
||||
{
|
||||
if ((o.X + o.Y) & 1) // i.e. immediate neighbours but not diagonal ones
|
||||
{
|
||||
auto q = p + o;
|
||||
if (CELLS.OriginRect().Contains(q))
|
||||
{
|
||||
toCheck.push(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (auto x = 0; x < CELLS.X; ++x)
|
||||
{
|
||||
check({ x, 0 });
|
||||
check({ x, CELLS.Y - 1 });
|
||||
}
|
||||
for (auto y = 1; y < CELLS.Y - 1; ++y) // corners already checked in the previous loop
|
||||
{
|
||||
check({ 0 , y });
|
||||
check({ CELLS.X - 1, y });
|
||||
}
|
||||
while (!toCheck.empty())
|
||||
{
|
||||
auto p = toCheck.top();
|
||||
toCheck.pop();
|
||||
check(p);
|
||||
}
|
||||
}
|
||||
|
||||
//updates pmap, gol, and some other simulation stuff (but not particles)
|
||||
void Simulation::BeforeSim()
|
||||
{
|
||||
@ -3685,16 +3754,9 @@ void Simulation::BeforeSim()
|
||||
if(aheat_enable)
|
||||
air->update_airh();
|
||||
|
||||
if(grav->IsEnabled())
|
||||
{
|
||||
grav->gravity_update_async();
|
||||
DispatchNewtonianGravity();
|
||||
gravIn = {};
|
||||
|
||||
//Get updated buffer pointers for gravity
|
||||
gravx = &grav->gravx[0];
|
||||
gravy = &grav->gravy[0];
|
||||
gravp = &grav->gravp[0];
|
||||
gravmap = &grav->gravmap[0];
|
||||
}
|
||||
if(emp_decor>0)
|
||||
emp_decor -= emp_decor/25+2;
|
||||
if(emp_decor < 0)
|
||||
@ -3714,7 +3776,7 @@ void Simulation::BeforeSim()
|
||||
|
||||
if (gravWallChanged)
|
||||
{
|
||||
grav->gravity_mask();
|
||||
UpdateGravityMask();
|
||||
gravWallChanged = false;
|
||||
}
|
||||
|
||||
@ -3923,16 +3985,6 @@ Simulation::Simulation():
|
||||
std::fill(elementCount, elementCount+PT_NUM, 0);
|
||||
elementRecount = true;
|
||||
|
||||
//Create and attach gravity simulation
|
||||
grav = Gravity::Create();
|
||||
//Give air sim references to our data
|
||||
grav->bmap = bmap;
|
||||
//Gravity sim gives us maps to use
|
||||
gravx = &grav->gravx[0];
|
||||
gravy = &grav->gravy[0];
|
||||
gravp = &grav->gravp[0];
|
||||
gravmap = &grav->gravmap[0];
|
||||
|
||||
//Create and attach air simulation
|
||||
air = std::make_unique<Air>(*this);
|
||||
//Give air sim references to our data
|
||||
@ -3951,7 +4003,40 @@ Simulation::Simulation():
|
||||
|
||||
clear_sim();
|
||||
|
||||
grav->gravity_mask();
|
||||
UpdateGravityMask();
|
||||
}
|
||||
|
||||
void Simulation::DispatchNewtonianGravity()
|
||||
{
|
||||
if (grav)
|
||||
{
|
||||
grav->Exchange(gravOut, gravIn);
|
||||
}
|
||||
}
|
||||
|
||||
void Simulation::ResetNewtonianGravity(GravityInput newGravIn, GravityOutput newGravOut)
|
||||
{
|
||||
gravIn = newGravIn;
|
||||
DispatchNewtonianGravity();
|
||||
if (grav)
|
||||
{
|
||||
gravOut = newGravOut;
|
||||
gravWallChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Simulation::EnableNewtonianGravity(bool enable)
|
||||
{
|
||||
if (grav && !enable)
|
||||
{
|
||||
grav.reset();
|
||||
gravOut = {}; // reset as per the invariant
|
||||
}
|
||||
if (!grav && enable)
|
||||
{
|
||||
grav = Gravity::Create();
|
||||
DispatchNewtonianGravity();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t ce_log2(size_t n)
|
||||
|
@ -7,8 +7,8 @@
|
||||
#include "BuiltinGOL.h"
|
||||
#include "MenuSection.h"
|
||||
#include "CoordStack.h"
|
||||
#include "gravity/GravityPtr.h"
|
||||
#include "common/tpt-rand.h"
|
||||
#include "gravity/Gravity.h"
|
||||
#include "Element.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <cstring>
|
||||
@ -22,13 +22,12 @@ constexpr int CHANNELS = int(MAX_TEMP - 73) / 100 + 2;
|
||||
|
||||
class Snapshot;
|
||||
class Brush;
|
||||
class SimulationSample;
|
||||
struct SimulationSample;
|
||||
struct matrix2d;
|
||||
struct vector2d;
|
||||
|
||||
class Simulation;
|
||||
class Renderer;
|
||||
class Gravity;
|
||||
class Air;
|
||||
class GameSave;
|
||||
|
||||
@ -36,6 +35,9 @@ class Simulation
|
||||
{
|
||||
public:
|
||||
GravityPtr grav;
|
||||
GravityInput gravIn;
|
||||
GravityOutput gravOut; // invariant: when grav is empty, this is in its default-constructed state
|
||||
|
||||
std::unique_ptr<Air> air;
|
||||
RNG rng;
|
||||
|
||||
@ -80,11 +82,6 @@ public:
|
||||
float (*vy)[XCELLS];
|
||||
float (*pv)[XCELLS];
|
||||
float (*hv)[XCELLS];
|
||||
//Gravity sim
|
||||
float *gravx;//gravx[YCELLS * XCELLS];
|
||||
float *gravy;//gravy[YCELLS * XCELLS];
|
||||
float *gravp;//gravp[YCELLS * XCELLS];
|
||||
float *gravmap;//gravmap[YCELLS * XCELLS];
|
||||
//Walls
|
||||
unsigned char bmap[YCELLS][XCELLS];
|
||||
unsigned char emap[YCELLS][XCELLS];
|
||||
@ -216,6 +213,11 @@ public:
|
||||
|
||||
bool useLuaCallbacks = false;
|
||||
|
||||
void EnableNewtonianGravity(bool enable);
|
||||
void ResetNewtonianGravity(GravityInput newGravIn, GravityOutput newGravOut);
|
||||
void DispatchNewtonianGravity();
|
||||
void UpdateGravityMask();
|
||||
|
||||
private:
|
||||
CoordStack& getCoordStackSingleton();
|
||||
};
|
||||
|
@ -22,10 +22,9 @@ uint32_t Snapshot::Hash() const
|
||||
takeVector(AirVelocityY);
|
||||
takeVector(AmbientHeat);
|
||||
takeVector(Particles);
|
||||
takeVector(GravVelocityX);
|
||||
takeVector(GravVelocityY);
|
||||
takeVector(GravValue);
|
||||
takeVector(GravMap);
|
||||
takeVector(GravMass);
|
||||
takeVector(GravForceX);
|
||||
takeVector(GravForceY);
|
||||
takeVector(BlockMap);
|
||||
takeVector(ElecMap);
|
||||
takeVector(BlockAir);
|
||||
|
@ -17,10 +17,9 @@ public:
|
||||
|
||||
std::vector<Particle> Particles;
|
||||
|
||||
std::vector<float> GravVelocityX;
|
||||
std::vector<float> GravVelocityY;
|
||||
std::vector<float> GravValue;
|
||||
std::vector<float> GravMap;
|
||||
std::vector<float> GravForceX;
|
||||
std::vector<float> GravForceY;
|
||||
std::vector<float> GravMass;
|
||||
|
||||
std::vector<unsigned char> BlockMap;
|
||||
std::vector<unsigned char> ElecMap;
|
||||
|
@ -207,10 +207,9 @@ std::unique_ptr<SnapshotDelta> SnapshotDelta::FromSnapshots(const Snapshot &oldS
|
||||
FillHunkVector(oldSnap.AirVelocityX , newSnap.AirVelocityX , delta.AirVelocityX );
|
||||
FillHunkVector(oldSnap.AirVelocityY , newSnap.AirVelocityY , delta.AirVelocityY );
|
||||
FillHunkVector(oldSnap.AmbientHeat , newSnap.AmbientHeat , delta.AmbientHeat );
|
||||
FillHunkVector(oldSnap.GravVelocityX , newSnap.GravVelocityX , delta.GravVelocityX );
|
||||
FillHunkVector(oldSnap.GravVelocityY , newSnap.GravVelocityY , delta.GravVelocityY );
|
||||
FillHunkVector(oldSnap.GravValue , newSnap.GravValue , delta.GravValue );
|
||||
FillHunkVector(oldSnap.GravMap , newSnap.GravMap , delta.GravMap );
|
||||
FillHunkVector(oldSnap.GravMass , newSnap.GravMass , delta.GravMass );
|
||||
FillHunkVector(oldSnap.GravForceX , newSnap.GravForceX , delta.GravForceX );
|
||||
FillHunkVector(oldSnap.GravForceY , newSnap.GravForceY , delta.GravForceY );
|
||||
FillHunkVector(oldSnap.BlockMap , newSnap.BlockMap , delta.BlockMap );
|
||||
FillHunkVector(oldSnap.ElecMap , newSnap.ElecMap , delta.ElecMap );
|
||||
FillHunkVector(oldSnap.BlockAir , newSnap.BlockAir , delta.BlockAir );
|
||||
@ -244,10 +243,9 @@ std::unique_ptr<Snapshot> SnapshotDelta::Forward(const Snapshot &oldSnap)
|
||||
ApplyHunkVector<false>(AirVelocityX , newSnap.AirVelocityX );
|
||||
ApplyHunkVector<false>(AirVelocityY , newSnap.AirVelocityY );
|
||||
ApplyHunkVector<false>(AmbientHeat , newSnap.AmbientHeat );
|
||||
ApplyHunkVector<false>(GravVelocityX , newSnap.GravVelocityX );
|
||||
ApplyHunkVector<false>(GravVelocityY , newSnap.GravVelocityY );
|
||||
ApplyHunkVector<false>(GravValue , newSnap.GravValue );
|
||||
ApplyHunkVector<false>(GravMap , newSnap.GravMap );
|
||||
ApplyHunkVector<false>(GravMass , newSnap.GravMass );
|
||||
ApplyHunkVector<false>(GravForceX , newSnap.GravForceX );
|
||||
ApplyHunkVector<false>(GravForceY , newSnap.GravForceY );
|
||||
ApplyHunkVector<false>(BlockMap , newSnap.BlockMap );
|
||||
ApplyHunkVector<false>(ElecMap , newSnap.ElecMap );
|
||||
ApplyHunkVector<false>(BlockAir , newSnap.BlockAir );
|
||||
@ -279,10 +277,9 @@ std::unique_ptr<Snapshot> SnapshotDelta::Restore(const Snapshot &newSnap)
|
||||
ApplyHunkVector<true>(AirVelocityX , oldSnap.AirVelocityX );
|
||||
ApplyHunkVector<true>(AirVelocityY , oldSnap.AirVelocityY );
|
||||
ApplyHunkVector<true>(AmbientHeat , oldSnap.AmbientHeat );
|
||||
ApplyHunkVector<true>(GravVelocityX , oldSnap.GravVelocityX );
|
||||
ApplyHunkVector<true>(GravVelocityY , oldSnap.GravVelocityY );
|
||||
ApplyHunkVector<true>(GravValue , oldSnap.GravValue );
|
||||
ApplyHunkVector<true>(GravMap , oldSnap.GravMap );
|
||||
ApplyHunkVector<true>(GravMass , oldSnap.GravMass );
|
||||
ApplyHunkVector<true>(GravForceX , oldSnap.GravForceX );
|
||||
ApplyHunkVector<true>(GravForceY , oldSnap.GravForceY );
|
||||
ApplyHunkVector<true>(BlockMap , oldSnap.BlockMap );
|
||||
ApplyHunkVector<true>(ElecMap , oldSnap.ElecMap );
|
||||
ApplyHunkVector<true>(BlockAir , oldSnap.BlockAir );
|
||||
|
@ -49,10 +49,9 @@ struct SnapshotDelta
|
||||
HunkVector<uint32_t> commonParticles;
|
||||
std::vector<Particle> extraPartsOld, extraPartsNew;
|
||||
|
||||
HunkVector<float> GravVelocityX;
|
||||
HunkVector<float> GravVelocityY;
|
||||
HunkVector<float> GravValue;
|
||||
HunkVector<float> GravMap;
|
||||
HunkVector<float> GravMass;
|
||||
HunkVector<float> GravForceX;
|
||||
HunkVector<float> GravForceY;
|
||||
|
||||
HunkVector<unsigned char> BlockMap;
|
||||
HunkVector<unsigned char> ElecMap;
|
||||
|
@ -53,7 +53,8 @@ void Element::Element_DEUT()
|
||||
|
||||
static int update(UPDATE_FUNC_ARGS)
|
||||
{
|
||||
float gravtot = fabs(sim->gravy[(y/CELL)*XCELLS+(x/CELL)])+fabs(sim->gravx[(y/CELL)*XCELLS+(x/CELL)]);
|
||||
auto gravtot = std::abs(sim->gravOut.forceX[Vec2{ x, y } / CELL]) +
|
||||
std::abs(sim->gravOut.forceY[Vec2{ x, y } / CELL]);
|
||||
// Prevent division by 0
|
||||
float temp = std::max(1.0f, (parts[i].temp + 1));
|
||||
auto maxlife = int(((10000/(temp + 1))-1));
|
||||
|
@ -69,10 +69,14 @@ static int update(UPDATE_FUNC_ARGS)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parts[i].life>20)
|
||||
sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] = 20;
|
||||
else if (parts[i].life>=1)
|
||||
sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] = -80;
|
||||
if (parts[i].life > 20)
|
||||
{
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] = 20;
|
||||
}
|
||||
else if (parts[i].life >= 1)
|
||||
{
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] = -80;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ static int update(UPDATE_FUNC_ARGS)
|
||||
if (parts[i].temp<= -256.0f+273.15f)
|
||||
parts[i].temp = -256.0f+273.15f;
|
||||
|
||||
sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] = 0.2f*(parts[i].temp-273.15);
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] = 0.2f * (parts[i].temp - 273.15);
|
||||
for (auto rx = -2; rx <= 2; rx++)
|
||||
{
|
||||
for (auto ry = -2; ry <= 2; ry++)
|
||||
|
@ -65,11 +65,11 @@ static int update(UPDATE_FUNC_ARGS)
|
||||
//Randomly kill GRVT inside RSSS
|
||||
if((utype == PT_RSSS) && sim->rng.chance(1, 5))
|
||||
{
|
||||
|
||||
sim->kill_part(i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] = 0.2f*parts[i].tmp;
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] = 0.2f * parts[i].tmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,12 @@ void Element::Element_NBHL()
|
||||
static int update(UPDATE_FUNC_ARGS)
|
||||
{
|
||||
if (parts[i].tmp)
|
||||
sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] += restrict_flt(0.001f*parts[i].tmp, 0.1f, 51.2f);
|
||||
{
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] += restrict_flt(0.001f * parts[i].tmp, 0.1f, 51.2f);
|
||||
}
|
||||
else
|
||||
sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] += 0.1f;
|
||||
{
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] += 0.1f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -48,8 +48,12 @@ void Element::Element_NWHL()
|
||||
static int update(UPDATE_FUNC_ARGS)
|
||||
{
|
||||
if (parts[i].tmp)
|
||||
sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] -= restrict_flt(0.001f*parts[i].tmp, 0.1f, 51.2f);
|
||||
{
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] -= restrict_flt(0.001f * parts[i].tmp, 0.1f, 51.2f);
|
||||
}
|
||||
else
|
||||
sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] -= 0.1f;
|
||||
{
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] -= 0.1f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -81,9 +81,8 @@ static int update(UPDATE_FUNC_ARGS)
|
||||
}
|
||||
if (parts[i].temp > 9973.15 && sim->pv[y/CELL][x/CELL] > 250.0f)
|
||||
{
|
||||
int gravPos = ((y/CELL)*XCELLS)+(x/CELL);
|
||||
float gravx = sim->gravx[gravPos];
|
||||
float gravy = sim->gravy[gravPos];
|
||||
auto gravx = sim->gravOut.forceX[Vec2{ x, y } / CELL];
|
||||
auto gravy = sim->gravOut.forceY[Vec2{ x, y } / CELL];
|
||||
if (gravx*gravx + gravy*gravy > 400)
|
||||
{
|
||||
if (sim->rng.chance(1, 5))
|
||||
|
@ -165,8 +165,8 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS)
|
||||
break;
|
||||
}
|
||||
|
||||
gvx += sim->gravx[((int)parts[i].y/CELL)*XCELLS+((int)parts[i].x/CELL)];
|
||||
gvy += sim->gravy[((int)parts[i].y/CELL)*XCELLS+((int)parts[i].x/CELL)];
|
||||
gvx += sim->gravOut.forceX[Vec2{ int(parts[i].x), int(parts[i].y) } / CELL];
|
||||
gvy += sim->gravOut.forceY[Vec2{ int(parts[i].x), int(parts[i].y) } / CELL];
|
||||
|
||||
float mvx = gvx;
|
||||
float mvy = gvy;
|
||||
|
@ -1,287 +0,0 @@
|
||||
#include "Gravity.h"
|
||||
#include "simulation/CoordStack.h"
|
||||
#include "simulation/Simulation.h"
|
||||
#include "simulation/SimulationData.h"
|
||||
#include "Misc.h"
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <sys/types.h>
|
||||
|
||||
Gravity::Gravity(CtorTag)
|
||||
{
|
||||
th_ogravmap.resize(NCELL);
|
||||
th_gravmap.resize(NCELL);
|
||||
th_gravy.resize(NCELL);
|
||||
th_gravx.resize(NCELL);
|
||||
th_gravp.resize(NCELL);
|
||||
gravmap.resize(NCELL);
|
||||
gravy.resize(NCELL);
|
||||
gravx.resize(NCELL);
|
||||
gravp.resize(NCELL);
|
||||
gravmask.resize(NCELL);
|
||||
}
|
||||
|
||||
Gravity::~Gravity()
|
||||
{
|
||||
stop_grav_async();
|
||||
}
|
||||
|
||||
void Gravity::Clear()
|
||||
{
|
||||
std::fill(&gravy[0], &gravy[0] + NCELL, 0.0f);
|
||||
std::fill(&gravx[0], &gravx[0] + NCELL, 0.0f);
|
||||
std::fill(&gravp[0], &gravp[0] + NCELL, 0.0f);
|
||||
std::fill(&gravmap[0], &gravmap[0] + NCELL, 0.0f);
|
||||
std::fill(&gravmask[0], &gravmask[0] + NCELL, UINT32_C(0xFFFFFFFF));
|
||||
|
||||
ignoreNextResult = true;
|
||||
}
|
||||
|
||||
void Gravity::gravity_update_async()
|
||||
{
|
||||
int result;
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
bool signal_grav = false;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> l(gravmutex, std::defer_lock);
|
||||
if (l.try_lock())
|
||||
{
|
||||
result = grav_ready;
|
||||
if (result) //Did the gravity thread finish?
|
||||
{
|
||||
if (th_gravchanged && !ignoreNextResult)
|
||||
{
|
||||
// Copy thread gravity maps into this one
|
||||
get_result();
|
||||
}
|
||||
ignoreNextResult = false;
|
||||
|
||||
std::swap(gravmap, th_gravmap);
|
||||
|
||||
grav_ready = 0; //Tell the other thread that we're ready for it to continue
|
||||
signal_grav = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (signal_grav)
|
||||
{
|
||||
gravcv.notify_one();
|
||||
}
|
||||
unsigned int size = NCELL;
|
||||
membwand(&gravy[0], &gravmask[0], size * sizeof(float), size * sizeof(uint32_t));
|
||||
membwand(&gravx[0], &gravmask[0], size * sizeof(float), size * sizeof(uint32_t));
|
||||
std::fill(&gravmap[0], &gravmap[0] + size, 0.0f);
|
||||
}
|
||||
|
||||
void Gravity::update_grav_async()
|
||||
{
|
||||
int done = 0;
|
||||
int thread_done = 0;
|
||||
std::fill(&th_ogravmap[0], &th_ogravmap[0] + NCELL, 0.0f);
|
||||
std::fill(&th_gravmap[0], &th_gravmap[0] + NCELL, 0.0f);
|
||||
std::fill(&th_gravy[0], &th_gravy[0] + NCELL, 0.0f);
|
||||
std::fill(&th_gravx[0], &th_gravx[0] + NCELL, 0.0f);
|
||||
std::fill(&th_gravp[0], &th_gravp[0] + NCELL, 0.0f);
|
||||
|
||||
std::unique_lock<std::mutex> l(gravmutex);
|
||||
while (!thread_done)
|
||||
{
|
||||
if (!done)
|
||||
{
|
||||
// run gravity update
|
||||
update_grav();
|
||||
done = 1;
|
||||
grav_ready = 1;
|
||||
thread_done = gravthread_done;
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait for main thread
|
||||
gravcv.wait(l);
|
||||
done = grav_ready;
|
||||
thread_done = gravthread_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gravity::start_grav_async()
|
||||
{
|
||||
if (enabled) //If it's already enabled, restart it
|
||||
stop_grav_async();
|
||||
|
||||
gravthread_done = 0;
|
||||
grav_ready = 0;
|
||||
gravthread = std::thread([this]() { update_grav_async(); }); //Start asynchronous gravity simulation
|
||||
enabled = true;
|
||||
|
||||
std::fill(&gravy[0], &gravy[0] + NCELL, 0.0f);
|
||||
std::fill(&gravx[0], &gravx[0] + NCELL, 0.0f);
|
||||
std::fill(&gravp[0], &gravp[0] + NCELL, 0.0f);
|
||||
std::fill(&gravmap[0], &gravmap[0] + NCELL, 0.0f);
|
||||
}
|
||||
|
||||
void Gravity::stop_grav_async()
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> g(gravmutex);
|
||||
gravthread_done = 1;
|
||||
}
|
||||
gravcv.notify_one();
|
||||
gravthread.join();
|
||||
enabled = false;
|
||||
}
|
||||
// Clear the grav velocities
|
||||
std::fill(&gravy[0], &gravy[0] + NCELL, 0.0f);
|
||||
std::fill(&gravx[0], &gravx[0] + NCELL, 0.0f);
|
||||
std::fill(&gravp[0], &gravp[0] + NCELL, 0.0f);
|
||||
std::fill(&gravmap[0], &gravmap[0] + NCELL, 0.0f);
|
||||
}
|
||||
|
||||
bool Gravity::grav_mask_r(int x, int y, char checkmap[YCELLS][XCELLS], char shape[YCELLS][XCELLS])
|
||||
{
|
||||
int x1, x2;
|
||||
bool ret = false;
|
||||
try
|
||||
{
|
||||
CoordStack cs;
|
||||
cs.push(x, y);
|
||||
do
|
||||
{
|
||||
cs.pop(x, y);
|
||||
x1 = x2 = x;
|
||||
while (x1 >= 0)
|
||||
{
|
||||
if (x1 == 0)
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
else if (checkmap[y][x1-1] || bmap[y][x1-1] == WL_GRAV)
|
||||
break;
|
||||
x1--;
|
||||
}
|
||||
while (x2 <= XCELLS-1)
|
||||
{
|
||||
if (x2 == XCELLS-1)
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
else if (checkmap[y][x2+1] || bmap[y][x2+1] == WL_GRAV)
|
||||
break;
|
||||
x2++;
|
||||
}
|
||||
for (x = x1; x <= x2; x++)
|
||||
{
|
||||
shape[y][x] = 1;
|
||||
checkmap[y][x] = 1;
|
||||
}
|
||||
if (y == 0)
|
||||
{
|
||||
for (x = x1; x <= x2; x++)
|
||||
if (bmap[y][x] != WL_GRAV)
|
||||
ret = true;
|
||||
}
|
||||
else if (y >= 1)
|
||||
{
|
||||
for (x = x1; x <= x2; x++)
|
||||
if (!checkmap[y-1][x] && bmap[y-1][x] != WL_GRAV)
|
||||
{
|
||||
if (y-1 == 0)
|
||||
ret = true;
|
||||
cs.push(x, y-1);
|
||||
}
|
||||
}
|
||||
if (y < YCELLS-1)
|
||||
for (x=x1; x<=x2; x++)
|
||||
if (!checkmap[y+1][x] && bmap[y+1][x] != WL_GRAV)
|
||||
{
|
||||
if (y+1 == YCELLS-1)
|
||||
ret = true;
|
||||
cs.push(x, y+1);
|
||||
}
|
||||
} while (cs.getSize()>0);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void Gravity::mask_free(mask_el *c_mask_el)
|
||||
{
|
||||
if (c_mask_el == nullptr)
|
||||
return;
|
||||
delete[] c_mask_el->next;
|
||||
delete[] c_mask_el->shape;
|
||||
delete[] c_mask_el;
|
||||
}
|
||||
|
||||
void Gravity::gravity_mask()
|
||||
{
|
||||
char checkmap[YCELLS][XCELLS];
|
||||
unsigned maskvalue;
|
||||
mask_el *t_mask_el = nullptr;
|
||||
mask_el *c_mask_el = nullptr;
|
||||
memset(checkmap, 0, sizeof(checkmap));
|
||||
for (int x = 0; x < XCELLS; x++)
|
||||
{
|
||||
for(int y = 0; y < YCELLS; y++)
|
||||
{
|
||||
if (bmap[y][x] != WL_GRAV && checkmap[y][x] == 0)
|
||||
{
|
||||
// Create a new shape
|
||||
if (t_mask_el == nullptr)
|
||||
{
|
||||
t_mask_el = new mask_el[sizeof(mask_el)];
|
||||
t_mask_el->shape = new char[NCELL];
|
||||
std::fill(&t_mask_el->shape[0], &t_mask_el->shape[0] + NCELL, 0);
|
||||
t_mask_el->shapeout = 0;
|
||||
t_mask_el->next = nullptr;
|
||||
c_mask_el = t_mask_el;
|
||||
}
|
||||
else
|
||||
{
|
||||
c_mask_el->next = new mask_el[sizeof(mask_el)];
|
||||
c_mask_el = c_mask_el->next;
|
||||
c_mask_el->shape = new char[NCELL];
|
||||
std::fill(&c_mask_el->shape[0], &c_mask_el->shape[0] + NCELL, 0);
|
||||
c_mask_el->shapeout = 0;
|
||||
c_mask_el->next = nullptr;
|
||||
}
|
||||
// Fill the shape
|
||||
if (grav_mask_r(x, y, checkmap, reinterpret_cast<char(*)[XCELLS]>(c_mask_el->shape)))
|
||||
c_mask_el->shapeout = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
c_mask_el = t_mask_el;
|
||||
std::fill(&gravmask[0], &gravmask[0] + NCELL, 0);
|
||||
while (c_mask_el != nullptr)
|
||||
{
|
||||
char *cshape = c_mask_el->shape;
|
||||
for (int x = 0; x < XCELLS; x++)
|
||||
{
|
||||
for (int y = 0; y < YCELLS; y++)
|
||||
{
|
||||
if (cshape[y * XCELLS + x])
|
||||
{
|
||||
if (c_mask_el->shapeout)
|
||||
maskvalue = 0xFFFFFFFF;
|
||||
else
|
||||
maskvalue = 0x00000000;
|
||||
gravmask[y * XCELLS + x] = maskvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
c_mask_el = c_mask_el->next;
|
||||
}
|
||||
mask_free(t_mask_el);
|
||||
}
|
@ -1,14 +1,23 @@
|
||||
#include "Gravity.h"
|
||||
#include "Misc.h"
|
||||
#include "Config.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <memory>
|
||||
#include <fftw3.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
constexpr auto xblock2 = XCELLS * 2;
|
||||
constexpr auto yblock2 = YCELLS * 2;
|
||||
constexpr auto fft_tsize = (xblock2 / 2 + 1) * yblock2;
|
||||
//NCELL*4 is size of data array, scaling needed because FFTW calculates an unnormalized DFT
|
||||
// DFT is cyclic in nature; gravity would wrap around sort of like in loop mode without the 2x here;
|
||||
// in fact it still does, it's just not as visible. the arrays are 2x as big along all dimensions as normal cell maps
|
||||
constexpr auto blocks = CELLS * 2;
|
||||
|
||||
// https://www.fftw.org/fftw3_doc/Multi_002dDimensional-DFTs-of-Real-Data.html#Multi_002dDimensional-DFTs-of-Real-Data
|
||||
constexpr auto transSize = (blocks.X / 2 + 1) * blocks.Y;
|
||||
|
||||
// NCELL * 4 is size of data array, scaling needed because FFTW calculates an unnormalized DFT
|
||||
constexpr auto scaleFactor = -float(M_GRAV) / (NCELL * 4);
|
||||
|
||||
static_assert(sizeof(std::complex<float>) == sizeof(fftwf_complex));
|
||||
@ -29,149 +38,203 @@ FftwComplexArrayPtr FftwComplexArray(size_t size)
|
||||
|
||||
struct GravityImpl : public Gravity
|
||||
{
|
||||
bool grav_fft_status = false;
|
||||
FftwArrayPtr th_gravmapbig , th_gravxbig , th_gravybig ;
|
||||
FftwComplexArrayPtr th_ptgravxt, th_ptgravyt, th_gravmapbigt, th_gravxbigt, th_gravybigt;
|
||||
FftwPlanPtr plan_gravmap, plan_gravx_inverse, plan_gravy_inverse;
|
||||
FftwArrayPtr massBig , forceXBig , forceYBig ;
|
||||
FftwComplexArrayPtr kernelXT, kernelYT, massBigT, forceXBigT, forceYBigT;
|
||||
FftwPlanPtr massForward, forceXInverse, forceYInverse;
|
||||
bool initDone = false;
|
||||
|
||||
void grav_fft_init();
|
||||
void grav_fft_cleanup();
|
||||
std::thread thr;
|
||||
bool working = false;
|
||||
bool shouldStop = false;
|
||||
std::mutex stateMx;
|
||||
std::condition_variable stateCv;
|
||||
|
||||
GravityImpl() : Gravity(CtorTag{})
|
||||
{
|
||||
}
|
||||
GravityInput gravIn;
|
||||
GravityOutput gravOut;
|
||||
bool copyGravOut = false;
|
||||
|
||||
~GravityImpl();
|
||||
|
||||
void Init();
|
||||
void Work();
|
||||
void Wait();
|
||||
void Stop();
|
||||
void Dispatch();
|
||||
};
|
||||
|
||||
GravityImpl::~GravityImpl()
|
||||
{
|
||||
stop_grav_async();
|
||||
grav_fft_cleanup();
|
||||
if (initDone)
|
||||
{
|
||||
Wait();
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void GravityImpl::grav_fft_init()
|
||||
void GravityImpl::Dispatch()
|
||||
{
|
||||
if (grav_fft_status) return;
|
||||
FftwPlanPtr plan_ptgravx, plan_ptgravy;
|
||||
{
|
||||
std::unique_lock lk(stateMx);
|
||||
working = true;
|
||||
}
|
||||
stateCv.notify_one();
|
||||
}
|
||||
|
||||
//use fftw malloc function to ensure arrays are aligned, to get better performance
|
||||
FftwArrayPtr th_ptgravx = FftwArray(xblock2 * yblock2);
|
||||
FftwArrayPtr th_ptgravy = FftwArray(xblock2 * yblock2);
|
||||
th_ptgravxt = FftwComplexArray(fft_tsize);
|
||||
th_ptgravyt = FftwComplexArray(fft_tsize);
|
||||
th_gravmapbig = FftwArray(xblock2 * yblock2);
|
||||
th_gravmapbigt = FftwComplexArray(fft_tsize);
|
||||
th_gravxbig = FftwArray(xblock2 * yblock2);
|
||||
th_gravybig = FftwArray(xblock2 * yblock2);
|
||||
th_gravxbigt = FftwComplexArray(fft_tsize);
|
||||
th_gravybigt = FftwComplexArray(fft_tsize);
|
||||
void GravityImpl::Stop()
|
||||
{
|
||||
{
|
||||
std::unique_lock lk(stateMx);
|
||||
shouldStop = true;
|
||||
}
|
||||
stateCv.notify_one();
|
||||
thr.join();
|
||||
}
|
||||
|
||||
void GravityImpl::Wait()
|
||||
{
|
||||
std::unique_lock lk(stateMx);
|
||||
stateCv.wait(lk, [this]() {
|
||||
return !working;
|
||||
});
|
||||
}
|
||||
|
||||
void GravityImpl::Work()
|
||||
{
|
||||
{
|
||||
PlaneAdapter<PlaneBase<float>, blocks.X, blocks.Y> massBigP(blocks, std::in_place, massBig.get());
|
||||
for (auto p : CELLS.OriginRect())
|
||||
{
|
||||
// used to be a membwand but we'd need a new buffer for this,
|
||||
// not worth it just to make this unalinged copy faster
|
||||
massBigP[p + CELLS] = gravIn.mask[p] ? gravIn.mass[p] : 0.f;
|
||||
}
|
||||
}
|
||||
fftwf_execute(massForward.get());
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/Convolution_theorem
|
||||
for (int i = 0; i < transSize; ++i)
|
||||
{
|
||||
forceXBigT[i] = massBigT[i] * kernelXT[i];
|
||||
forceYBigT[i] = massBigT[i] * kernelYT[i];
|
||||
}
|
||||
}
|
||||
fftwf_execute(forceXInverse.get());
|
||||
fftwf_execute(forceYInverse.get());
|
||||
{
|
||||
PlaneAdapter<PlaneBase<float>, blocks.X, blocks.Y> forceXBigP(blocks, std::in_place, forceXBig.get());
|
||||
PlaneAdapter<PlaneBase<float>, blocks.X, blocks.Y> forceYBigP(blocks, std::in_place, forceYBig.get());
|
||||
for (auto p : CELLS.OriginRect())
|
||||
{
|
||||
// similarly
|
||||
gravOut.forceX[p] = gravIn.mask[p] ? forceXBigP[p] : 0;
|
||||
gravOut.forceY[p] = gravIn.mask[p] ? forceYBigP[p] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GravityImpl::Init()
|
||||
{
|
||||
//select best algorithm, could use FFTW_PATIENT or FFTW_EXHAUSTIVE but that increases the time taken to plan, and I don't see much increase in execution speed
|
||||
auto fftwPlanFlags = FFTW_PLAN_MEASURE ? FFTW_MEASURE : FFTW_ESTIMATE;
|
||||
plan_ptgravx = FftwPlanPtr(fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravx.get(), reinterpret_cast<fftwf_complex *>(th_ptgravxt.get()), fftwPlanFlags));
|
||||
plan_ptgravy = FftwPlanPtr(fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravy.get(), reinterpret_cast<fftwf_complex *>(th_ptgravyt.get()), fftwPlanFlags));
|
||||
plan_gravmap = FftwPlanPtr(fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_gravmapbig.get(), reinterpret_cast<fftwf_complex *>(th_gravmapbigt.get()), fftwPlanFlags));
|
||||
plan_gravx_inverse = FftwPlanPtr(fftwf_plan_dft_c2r_2d(yblock2, xblock2, reinterpret_cast<fftwf_complex *>(th_gravxbigt.get()), th_gravxbig.get(), fftwPlanFlags));
|
||||
plan_gravy_inverse = FftwPlanPtr(fftwf_plan_dft_c2r_2d(yblock2, xblock2, reinterpret_cast<fftwf_complex *>(th_gravybigt.get()), th_gravybig.get(), fftwPlanFlags));
|
||||
|
||||
//use fftw malloc function to ensure arrays are aligned, to get better performance
|
||||
kernelXT = FftwComplexArray(transSize);
|
||||
kernelYT = FftwComplexArray(transSize);
|
||||
massBig = FftwArray(blocks.X * blocks.Y);
|
||||
massBigT = FftwComplexArray(transSize);
|
||||
forceXBig = FftwArray(blocks.X * blocks.Y);
|
||||
forceYBig = FftwArray(blocks.X * blocks.Y);
|
||||
forceXBigT = FftwComplexArray(transSize);
|
||||
forceYBigT = FftwComplexArray(transSize);
|
||||
|
||||
massForward = FftwPlanPtr(fftwf_plan_dft_r2c_2d(blocks.Y, blocks.X, massBig.get(), reinterpret_cast<fftwf_complex *>(massBigT.get()), fftwPlanFlags));
|
||||
forceXInverse = FftwPlanPtr(fftwf_plan_dft_c2r_2d(blocks.Y, blocks.X, reinterpret_cast<fftwf_complex *>(forceXBigT.get()), forceXBig.get(), fftwPlanFlags));
|
||||
forceYInverse = FftwPlanPtr(fftwf_plan_dft_c2r_2d(blocks.Y, blocks.X, reinterpret_cast<fftwf_complex *>(forceYBigT.get()), forceYBig.get(), fftwPlanFlags));
|
||||
|
||||
auto kernelXRaw = FftwArray(blocks.X * blocks.Y);
|
||||
auto kernelYRaw = FftwArray(blocks.X * blocks.Y);
|
||||
auto kernelXForward = FftwPlanPtr(fftwf_plan_dft_r2c_2d(blocks.Y, blocks.X, kernelXRaw.get(), reinterpret_cast<fftwf_complex *>(kernelXT.get()), fftwPlanFlags));
|
||||
auto kernelYForward = FftwPlanPtr(fftwf_plan_dft_r2c_2d(blocks.Y, blocks.X, kernelYRaw.get(), reinterpret_cast<fftwf_complex *>(kernelYT.get()), fftwPlanFlags));
|
||||
PlaneAdapter<PlaneBase<float>, blocks.X, blocks.Y> kernelX(blocks, std::in_place, kernelXRaw.get());
|
||||
PlaneAdapter<PlaneBase<float>, blocks.X, blocks.Y> kernelY(blocks, std::in_place, kernelYRaw.get());
|
||||
//calculate velocity map caused by a point mass
|
||||
for (int y = 0; y < yblock2; y++)
|
||||
for (auto p : blocks.OriginRect())
|
||||
{
|
||||
for (int x = 0; x < xblock2; x++)
|
||||
auto d = p - CELLS;
|
||||
if (d == Vec2{ 0, 0 })
|
||||
{
|
||||
if (x == XCELLS && y == YCELLS)
|
||||
continue;
|
||||
auto distance = hypotf(float(x-XCELLS), float(y-YCELLS));
|
||||
th_ptgravx[y * xblock2 + x] = scaleFactor * (x - XCELLS) / powf(distance, 3);
|
||||
th_ptgravy[y * xblock2 + x] = scaleFactor * (y - YCELLS) / powf(distance, 3);
|
||||
kernelX[p] = 0.f;
|
||||
kernelY[p] = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto distance = std::hypot(float(d.X), float(d.Y));
|
||||
kernelX[p] = scaleFactor * d.X / std::pow(distance, 3.f);
|
||||
kernelY[p] = scaleFactor * d.Y / std::pow(distance, 3.f);
|
||||
}
|
||||
}
|
||||
th_ptgravx[yblock2 * xblock2 / 2 + xblock2 / 2] = 0.0f;
|
||||
th_ptgravy[yblock2 * xblock2 / 2 + xblock2 / 2] = 0.0f;
|
||||
|
||||
//transform point mass velocity maps
|
||||
fftwf_execute(plan_ptgravx.get());
|
||||
fftwf_execute(plan_ptgravy.get());
|
||||
fftwf_execute(kernelXForward.get());
|
||||
fftwf_execute(kernelYForward.get());
|
||||
|
||||
//clear padded gravmap
|
||||
memset(th_gravmapbig.get(), 0, xblock2 * yblock2 * sizeof(float));
|
||||
std::memset(massBig.get(), 0.f, blocks.X * blocks.Y * sizeof(float));
|
||||
|
||||
grav_fft_status = true;
|
||||
thr = std::thread([this]() {
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
std::unique_lock lk(stateMx);
|
||||
stateCv.wait(lk, [this]() {
|
||||
return working || shouldStop;
|
||||
});
|
||||
if (shouldStop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
Work();
|
||||
{
|
||||
std::unique_lock lk(stateMx);
|
||||
working = false;
|
||||
}
|
||||
stateCv.notify_one();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GravityImpl::grav_fft_cleanup()
|
||||
{
|
||||
if (!grav_fft_status) return;
|
||||
grav_fft_status = false;
|
||||
}
|
||||
|
||||
void Gravity::get_result()
|
||||
{
|
||||
std::swap(gravy, th_gravy);
|
||||
std::swap(gravx, th_gravx);
|
||||
std::swap(gravp, th_gravp);
|
||||
}
|
||||
|
||||
void Gravity::update_grav()
|
||||
void Gravity::Exchange(GravityOutput &gravOut, const GravityInput &gravIn)
|
||||
{
|
||||
auto *fftGravity = static_cast<GravityImpl *>(this);
|
||||
if (!fftGravity->grav_fft_status)
|
||||
fftGravity->grav_fft_init();
|
||||
|
||||
auto *th_gravmapbig = fftGravity->th_gravmapbig.get();
|
||||
auto *th_gravxbig = fftGravity->th_gravxbig.get();
|
||||
auto *th_gravybig = fftGravity->th_gravybig.get();
|
||||
auto *th_ptgravxt = fftGravity->th_ptgravxt.get();
|
||||
auto *th_ptgravyt = fftGravity->th_ptgravyt.get();
|
||||
auto *th_gravmapbigt = fftGravity->th_gravmapbigt.get();
|
||||
auto *th_gravxbigt = fftGravity->th_gravxbigt.get();
|
||||
auto *th_gravybigt = fftGravity->th_gravybigt.get();
|
||||
auto &plan_gravmap = fftGravity->plan_gravmap;
|
||||
auto &plan_gravx_inverse = fftGravity->plan_gravx_inverse;
|
||||
auto &plan_gravy_inverse = fftGravity->plan_gravy_inverse;
|
||||
|
||||
if (memcmp(&th_ogravmap[0], &th_gravmap[0], sizeof(float) * NCELL) != 0)
|
||||
// lazy init
|
||||
if (!fftGravity->initDone)
|
||||
{
|
||||
th_gravchanged = 1;
|
||||
|
||||
membwand(&th_gravmap[0], &gravmask[0], NCELL * sizeof(float), NCELL * sizeof(uint32_t));
|
||||
//copy gravmap into padded gravmap array
|
||||
for (int y = 0; y < YCELLS; y++)
|
||||
{
|
||||
for (int x = 0; x < XCELLS; x++)
|
||||
{
|
||||
th_gravmapbig[(y+YCELLS)*xblock2+XCELLS+x] = th_gravmap[y*XCELLS+x];
|
||||
}
|
||||
}
|
||||
//transform gravmap
|
||||
fftwf_execute(plan_gravmap.get());
|
||||
//do convolution (multiply the complex numbers)
|
||||
for (int i = 0; i < fft_tsize; i++)
|
||||
{
|
||||
th_gravxbigt[i] = th_gravmapbigt[i] * th_ptgravxt[i];
|
||||
th_gravybigt[i] = th_gravmapbigt[i] * th_ptgravyt[i];
|
||||
}
|
||||
//inverse transform, and copy from padded arrays into normal velocity maps
|
||||
fftwf_execute(plan_gravx_inverse.get());
|
||||
fftwf_execute(plan_gravy_inverse.get());
|
||||
for (int y = 0; y < YCELLS; y++)
|
||||
{
|
||||
for (int x = 0; x < XCELLS; x++)
|
||||
{
|
||||
th_gravx[y*XCELLS+x] = th_gravxbig[y*xblock2+x];
|
||||
th_gravy[y*XCELLS+x] = th_gravybig[y*xblock2+x];
|
||||
th_gravp[y*XCELLS+x] = hypotf(th_gravxbig[y*xblock2+x], th_gravybig[y*xblock2+x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
th_gravchanged = 0;
|
||||
// this takes a noticeable amount of time
|
||||
// TODO: hide the wait somehow
|
||||
fftGravity->Init();
|
||||
fftGravity->initDone = true;
|
||||
}
|
||||
|
||||
// Copy th_ogravmap into th_gravmap (doesn't matter what th_ogravmap is afterwards)
|
||||
std::swap(th_gravmap, th_ogravmap);
|
||||
fftGravity->Wait();
|
||||
|
||||
// take output
|
||||
if (fftGravity->copyGravOut)
|
||||
{
|
||||
fftGravity->copyGravOut = false;
|
||||
std::swap(gravOut, fftGravity->gravOut);
|
||||
}
|
||||
|
||||
// pass input (but same input => same output)
|
||||
if (std::memcmp(&fftGravity->gravIn.mass[{ 0, 0 }], &gravIn.mass[{ 0, 0 }], NCELL * sizeof(float)) ||
|
||||
std::memcmp(&fftGravity->gravIn.mask[{ 0, 0 }], &gravIn.mask[{ 0, 0 }], NCELL * sizeof(float)))
|
||||
{
|
||||
fftGravity->copyGravOut = true;
|
||||
fftGravity->gravIn = gravIn;
|
||||
}
|
||||
|
||||
fftGravity->Dispatch();
|
||||
}
|
||||
|
||||
GravityPtr Gravity::Create()
|
||||
|
@ -1,78 +1,14 @@
|
||||
#pragma once
|
||||
#include "Config.h"
|
||||
#include "GravityData.h"
|
||||
#include "GravityPtr.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
class Simulation;
|
||||
|
||||
class Gravity
|
||||
{
|
||||
protected:
|
||||
bool enabled = false;
|
||||
|
||||
// Maps to be processed by the gravity thread
|
||||
std::vector<float> th_ogravmap;
|
||||
std::vector<float> th_gravmap;
|
||||
std::vector<float> th_gravx;
|
||||
std::vector<float> th_gravy;
|
||||
std::vector<float> th_gravp;
|
||||
|
||||
int th_gravchanged = 0;
|
||||
|
||||
std::thread gravthread;
|
||||
std::mutex gravmutex;
|
||||
std::condition_variable gravcv;
|
||||
int grav_ready = 0;
|
||||
int gravthread_done = 0;
|
||||
bool ignoreNextResult = false;
|
||||
|
||||
struct mask_el {
|
||||
char *shape;
|
||||
char shapeout;
|
||||
mask_el *next;
|
||||
};
|
||||
using mask_el = struct mask_el;
|
||||
|
||||
bool grav_mask_r(int x, int y, char checkmap[YCELLS][XCELLS], char shape[YCELLS][XCELLS]);
|
||||
void mask_free(mask_el *c_mask_el);
|
||||
|
||||
void update_grav();
|
||||
void get_result();
|
||||
void update_grav_async();
|
||||
|
||||
struct CtorTag // Please use Gravity::Create().
|
||||
{
|
||||
};
|
||||
Gravity() = default;
|
||||
|
||||
public:
|
||||
Gravity(CtorTag);
|
||||
~Gravity();
|
||||
|
||||
//Maps to be used by the main thread
|
||||
std::vector<float> gravmap;
|
||||
std::vector<float> gravp;
|
||||
std::vector<float> gravy;
|
||||
std::vector<float> gravx;
|
||||
std::vector<uint32_t> gravmask;
|
||||
static_assert(sizeof(float) == sizeof(uint32_t));
|
||||
|
||||
unsigned char (*bmap)[XCELLS];
|
||||
|
||||
bool IsEnabled() { return enabled; }
|
||||
|
||||
void Clear();
|
||||
|
||||
void gravity_update_async();
|
||||
|
||||
void start_grav_async();
|
||||
void stop_grav_async();
|
||||
void gravity_mask();
|
||||
void Exchange(GravityOutput &gravOut, const GravityInput &gravIn);
|
||||
|
||||
static GravityPtr Create();
|
||||
};
|
||||
|
29
src/simulation/gravity/GravityData.h
Normal file
29
src/simulation/gravity/GravityData.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "common/Plane.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
template<class Item>
|
||||
using GravityPlane = PlaneAdapter<std::vector<Item>, CELLS.X, CELLS.Y>;
|
||||
|
||||
struct GravityInput
|
||||
{
|
||||
GravityPlane<float> mass;
|
||||
GravityPlane<uint32_t> mask;
|
||||
static_assert(sizeof(float) == sizeof(uint32_t));
|
||||
|
||||
GravityInput() : mass(CELLS, 0.f), mask(CELLS, UINT32_C(0xFFFFFFFF))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct GravityOutput
|
||||
{
|
||||
GravityPlane<float> forceX;
|
||||
GravityPlane<float> forceY;
|
||||
|
||||
GravityOutput() : forceX(CELLS, 0.f), forceY(CELLS, 0.f)
|
||||
{
|
||||
}
|
||||
};
|
@ -1,23 +1,12 @@
|
||||
#include "Gravity.h"
|
||||
#include "Misc.h"
|
||||
#include <cstring>
|
||||
|
||||
// gravity without fast Fourier transforms
|
||||
|
||||
void Gravity::get_result()
|
||||
{
|
||||
memcpy(&gravy[0], &th_gravy[0], NCELL * sizeof(float));
|
||||
memcpy(&gravx[0], &th_gravx[0], NCELL * sizeof(float));
|
||||
memcpy(&gravp[0], &th_gravp[0], NCELL * sizeof(float));
|
||||
}
|
||||
|
||||
void Gravity::update_grav(void)
|
||||
void Gravity::Exchange(GravityOutput &gravOut, const GravityInput &gravIn)
|
||||
{
|
||||
}
|
||||
|
||||
GravityPtr Gravity::Create()
|
||||
{
|
||||
return GravityPtr(new Gravity(CtorTag{}));
|
||||
return GravityPtr(new Gravity());
|
||||
}
|
||||
|
||||
void GravityDeleter::operator ()(Gravity *ptr) const
|
||||
|
@ -1,7 +1,3 @@
|
||||
simulation_files += files(
|
||||
'Common.cpp',
|
||||
)
|
||||
|
||||
if host_platform == 'emscripten'
|
||||
# FFTW_MEASURE fails on emscripten for some reason, probably a timing issue
|
||||
# FFTW_ESTIMATE is 20% slower
|
||||
|
@ -13,6 +13,6 @@ void SimTool::Tool_NGRV()
|
||||
|
||||
static int perform(Simulation * sim, Particle * cpart, int x, int y, int brushX, int brushYy, float strength)
|
||||
{
|
||||
sim->gravmap[((y/CELL)*XCELLS)+(x/CELL)] = strength*-5.0f;
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] = strength * -5.0f;
|
||||
return 1;
|
||||
}
|
||||
|
@ -13,6 +13,6 @@ void SimTool::Tool_PGRV()
|
||||
|
||||
static int perform(Simulation * sim, Particle * cpart, int x, int y, int brushX, int brushY, float strength)
|
||||
{
|
||||
sim->gravmap[((y/CELL)*XCELLS)+(x/CELL)] = strength*5.0f;
|
||||
sim->gravIn.mass[Vec2{ x, y } / CELL] = strength * 5.0f;
|
||||
return 1;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user