Made Gravity Models configurable with packets

This commit is contained in:
OLEGSHA 2021-03-15 21:02:33 +03:00
parent f4311fb27c
commit f28c765e3f
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
13 changed files with 368 additions and 36 deletions

View File

@ -17,6 +17,9 @@
*/ */
package ru.windcorp.progressia.common.world; package ru.windcorp.progressia.common.world;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Objects; import java.util.Objects;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
@ -165,5 +168,63 @@ public abstract class GravityModel extends Namespaced {
* specified chunk. Never {@code null}. * specified chunk. Never {@code null}.
*/ */
protected abstract AbsFace doGetDiscreteUp(Vec3i chunkPos); protected abstract AbsFace doGetDiscreteUp(Vec3i chunkPos);
/**
* Parses the settings from the provided {@link DataInput} and configures this object appropriately. This method will not necessarily exhaust the input.
* @param input a stream to read the settings from
* @throws IOException if an I/O error occurs
* @throws DecodingException if the settings could not be parsed from input
*/
public void readSettings(DataInput input) throws IOException, DecodingException {
Objects.requireNonNull(input, "input");
try {
doReadSettings(input);
} catch (IOException | DecodingException e) {
throw e;
} catch (Exception e) {
throw CrashReports.report(
e,
"%s failed to read its settings",
this
);
}
}
/**
* Encodes the settings of this model into the provided {@link DataOutput}.
* @param output a stream to write the settings into
* @throws IOException if an I/O error occurs
*/
public void writeSettings(DataOutput output) throws IOException {
Objects.requireNonNull(output, "output");
try {
doWriteSettings(output);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw CrashReports.report(
e,
"%s failed to write its settings",
this
);
}
}
/**
* Parses the settings from the provided {@link DataInput} and configures this object appropriately. This method will not necessarily exhaust the input.
* @param input a stream to read the settings from
* @throws IOException if an I/O error occurs
* @throws DecodingException if the settings could not be parsed from input
*/
protected abstract void doReadSettings(DataInput input) throws IOException, DecodingException;
/**
* Encodes the settings of this model into the provided {@link DataOutput}.
* @param output a stream to write the settings into
* @throws IOException if an I/O error occurs
*/
protected abstract void doWriteSettings(DataOutput output) throws IOException;
} }

View File

@ -17,9 +17,9 @@
*/ */
package ru.windcorp.progressia.common.world; package ru.windcorp.progressia.common.world;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; import ru.windcorp.progressia.common.util.namespaces.NamespacedFactoryRegistry;
public class GravityModelRegistry extends NamespacedInstanceRegistry<GravityModel> { public class GravityModelRegistry extends NamespacedFactoryRegistry<GravityModel> {
public static final GravityModelRegistry INSTANCE = new GravityModelRegistry(); public static final GravityModelRegistry INSTANCE = new GravityModelRegistry();

View File

@ -21,9 +21,13 @@ import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import ru.windcorp.progressia.common.util.DataBuffer;
import ru.windcorp.progressia.common.util.crash.CrashReports;
public class PacketSetGravityModel extends PacketAffectWorld { public class PacketSetGravityModel extends PacketAffectWorld {
private String gravityModelId; private String gravityModelId;
private final DataBuffer settings = new DataBuffer();
public PacketSetGravityModel() { public PacketSetGravityModel() {
this("Core:SetGravityModel"); this("Core:SetGravityModel");
@ -35,22 +39,38 @@ public class PacketSetGravityModel extends PacketAffectWorld {
public void set(GravityModel model) { public void set(GravityModel model) {
this.gravityModelId = model.getId(); this.gravityModelId = model.getId();
try {
model.writeSettings(settings.getWriter());
} catch (IOException e) {
throw CrashReports.report(e, "%s has errored when writing its settings", model);
}
} }
@Override @Override
public void read(DataInput input) throws IOException, DecodingException { public void read(DataInput input) throws IOException, DecodingException {
gravityModelId = input.readUTF(); gravityModelId = input.readUTF();
settings.fill(input, input.readInt());
} }
@Override @Override
public void write(DataOutput output) throws IOException { public void write(DataOutput output) throws IOException {
output.writeUTF(gravityModelId); output.writeUTF(gravityModelId);
output.writeInt(settings.getSize());
settings.flush(output);
} }
@Override @Override
public void apply(WorldData world) { public void apply(WorldData world) {
GravityModel model = GravityModelRegistry.getInstance().get(gravityModelId); GravityModel model = GravityModelRegistry.getInstance().create(gravityModelId);
world.setGravityModel(model); world.setGravityModel(model);
try {
model.readSettings(settings.getReader());
} catch (IOException e) {
throw CrashReports.report(e, "%s has errored when reading its settings", model);
} catch (DecodingException e) {
throw CrashReports.report(e, "%s has failed to parse its settings", model);
}
} }
} }

View File

@ -61,7 +61,7 @@ public class Server {
private final TickingSettings tickingSettings = new TickingSettings(); private final TickingSettings tickingSettings = new TickingSettings();
public Server(WorldData world) { public Server(WorldData world) {
this.world = new WorldLogic(world, this, w -> new TestPlanetGenerator("Test:PlanetGenerator", new Planet(4, 16f, 9.8f, 16f), w)); this.world = new WorldLogic(world, this, w -> new TestPlanetGenerator("Test:PlanetGenerator", new Planet(4, 9.8f, 16f, 16f), w));
this.serverThread = new ServerThread(this); this.serverThread = new ServerThread(this);
this.clientManager = new ClientManager(this); this.clientManager = new ClientManager(this);

View File

@ -37,7 +37,7 @@ public abstract class AbstractWorldGenerator<H> extends WorldGenerator {
public AbstractWorldGenerator(String id, Class<H> hintClass, String gravityModelId) { public AbstractWorldGenerator(String id, Class<H> hintClass, String gravityModelId) {
super(id); super(id);
this.hintClass = Objects.requireNonNull(hintClass, "hintClass"); this.hintClass = Objects.requireNonNull(hintClass, "hintClass");
this.gravityModel = GravityModelRegistry.getInstance().get(Objects.requireNonNull(gravityModelId, "gravityModelId")); this.gravityModel = GravityModelRegistry.getInstance().create(Objects.requireNonNull(gravityModelId, "gravityModelId"));
if (this.gravityModel == null) { if (this.gravityModel == null) {
throw new IllegalArgumentException("Gravity model with ID \"" + gravityModelId + "\" not found"); throw new IllegalArgumentException("Gravity model with ID \"" + gravityModelId + "\" not found");

View File

@ -422,8 +422,8 @@ public class TestContent {
private static void registerMisc() { private static void registerMisc() {
ChunkIO.registerCodec(new TestChunkCodec()); ChunkIO.registerCodec(new TestChunkCodec());
ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new); ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new);
GravityModelRegistry.getInstance().register(new TestGravityModel()); GravityModelRegistry.getInstance().register("Test:TheGravityModel", TestGravityModel::new);
GravityModelRegistry.getInstance().register(new TestPlanetGravityModel()); GravityModelRegistry.getInstance().register("Test:PlanetGravityModel", TestPlanetGravityModel::new);
} }
} }

View File

@ -17,15 +17,20 @@
*/ */
package ru.windcorp.progressia.test.gen; package ru.windcorp.progressia.test.gen;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.GravityModel; import ru.windcorp.progressia.common.world.GravityModel;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.rels.AbsFace;
public class TestGravityModel extends GravityModel { public class TestGravityModel extends GravityModel {
public TestGravityModel() { public TestGravityModel(String id) {
super("Test:TheGravityModel"); super(id);
} }
@Override @Override
@ -39,4 +44,14 @@ public class TestGravityModel extends GravityModel {
return rounded == null ? AbsFace.POS_Z : rounded; return rounded == null ? AbsFace.POS_Z : rounded;
} }
@Override
protected void doReadSettings(DataInput input) throws IOException, DecodingException {
// Do nothing
}
@Override
protected void doWriteSettings(DataOutput output) throws IOException {
// Do nothing
}
} }

View File

@ -23,20 +23,20 @@ public class Planet {
private final int radiusInChunks; private final int radiusInChunks;
private final float curvature; private final TestPlanetGravityModel.Settings gravityModelSettings;
private final float surfaceGravitationalAcceleration;
private final float innerGravityRadius;
public Planet( public Planet(
int radiusInChunks, int radiusInChunks,
float curvature,
float surfaceGravitationalAcceleration, float surfaceGravitationalAcceleration,
float curvature,
float innerGravityRadius float innerGravityRadius
) { ) {
this.radiusInChunks = radiusInChunks; this.radiusInChunks = radiusInChunks;
this.curvature = curvature; this.gravityModelSettings = new TestPlanetGravityModel.Settings(
this.surfaceGravitationalAcceleration = surfaceGravitationalAcceleration; surfaceGravitationalAcceleration,
this.innerGravityRadius = innerGravityRadius; curvature,
innerGravityRadius
);
} }
/** /**
@ -62,21 +62,28 @@ public class Planet {
* @return the curvature * @return the curvature
*/ */
public float getCurvature() { public float getCurvature() {
return curvature; return gravityModelSettings.curvature;
} }
/** /**
* @return the innerGravityRadius * @return the innerGravityRadius
*/ */
public float getInnerGravityRadius() { public float getInnerGravityRadius() {
return innerGravityRadius; return gravityModelSettings.innerRadius;
} }
/** /**
* @return the surfaceGravitationalAcceleration * @return the surfaceGravitationalAcceleration
*/ */
public float getSurfaceGravitationalAcceleration() { public float getSurfaceGravitationalAcceleration() {
return surfaceGravitationalAcceleration; return gravityModelSettings.surfaceGravitationalAcceleration;
}
/**
* @return the gravityModelSettings
*/
public TestPlanetGravityModel.Settings getGravityModelSettings() {
return gravityModelSettings;
} }
} }

View File

@ -37,8 +37,12 @@ public class TestPlanetGenerator extends AbstractWorldGenerator<Boolean> {
public TestPlanetGenerator(String id, Planet planet, WorldLogic world) { public TestPlanetGenerator(String id, Planet planet, WorldLogic world) {
super(id, Boolean.class, "Test:PlanetGravityModel"); super(id, Boolean.class, "Test:PlanetGravityModel");
this.planet = planet; this.planet = planet;
TestPlanetGravityModel model = (TestPlanetGravityModel) this.getGravityModel();
model.configure(planet.getGravityModelSettings());
this.terrainGenerator = new PlanetTerrainGenerator(this); this.terrainGenerator = new PlanetTerrainGenerator(this);
this.scatterGenerator = new PlanetScatterGenerator(this); this.scatterGenerator = new PlanetScatterGenerator(this);
} }

View File

@ -19,36 +19,86 @@ package ru.windcorp.progressia.test.gen.planet;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.GravityModel; import ru.windcorp.progressia.common.world.GravityModel;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.rels.AbsFace;
import static java.lang.Math.*; import static java.lang.Math.*;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class TestPlanetGravityModel extends GravityModel { public class TestPlanetGravityModel extends GravityModel {
private static final float GRAVITATIONAL_ACCELERATION = Units.get("9.8 m/s^2"); public static class Settings {
private static final float ROUNDNESS = Units.get("16 m"); public float surfaceGravitationalAcceleration;
private static final float INNER_RADIUS = Units.get("16 m"); public float curvature;
public float innerRadius;
public Settings() {}
public Settings(float surfaceGravitationalAcceleration, float curvature, float innerRadius) {
this.surfaceGravitationalAcceleration = surfaceGravitationalAcceleration;
this.curvature = curvature;
this.innerRadius = innerRadius;
}
public void copyFrom(Settings copyFrom) {
this.surfaceGravitationalAcceleration = copyFrom.surfaceGravitationalAcceleration;
this.curvature = copyFrom.curvature;
this.innerRadius = copyFrom.innerRadius;
}
public void read(DataInput input) throws IOException, DecodingException {
surfaceGravitationalAcceleration = input.readFloat();
curvature = input.readFloat();
innerRadius = input.readFloat();
}
public void write(DataOutput output) throws IOException {
output.writeFloat(surfaceGravitationalAcceleration);
output.writeFloat(curvature);
output.writeFloat(innerRadius);
}
}
public TestPlanetGravityModel() { private Settings settings = new Settings();
this("Test:PlanetGravityModel");
public TestPlanetGravityModel(String id) {
super(id);
} }
protected TestPlanetGravityModel(String id) { public float getSurfaceGravitationalAcceleration() {
super(id); return settings.surfaceGravitationalAcceleration;
}
public float getCurvature() {
return settings.curvature;
}
public float getInnerRadius() {
return settings.innerRadius;
}
public void configure(Settings settings) {
this.settings = settings;
} }
@Override @Override
protected void doGetGravity(Vec3 pos, Vec3 output) { protected void doGetGravity(Vec3 pos, Vec3 output) {
float r = getInnerRadius();
float c = getCurvature();
float g = getSurfaceGravitationalAcceleration();
// Change to a CS where (0;0;0) is the center of the center chunk // Change to a CS where (0;0;0) is the center of the center chunk
float px = pos.x - ChunkData.CHUNK_RADIUS + 0.5f; float px = pos.x - ChunkData.CHUNK_RADIUS + 0.5f;
float py = pos.y - ChunkData.CHUNK_RADIUS + 0.5f; float py = pos.y - ChunkData.CHUNK_RADIUS + 0.5f;
float pz = pos.z - ChunkData.CHUNK_RADIUS + 0.5f; float pz = pos.z - ChunkData.CHUNK_RADIUS + 0.5f;
// Assume weightlessness when too close to center // Assume weightlessness when too close to center
if ((px*px + py*py + pz*pz) < INNER_RADIUS*INNER_RADIUS) { if ((px*px + py*py + pz*pz) < r*r) {
output.set(0, 0, 0); output.set(0, 0, 0);
return; return;
} }
@ -81,24 +131,26 @@ public class TestPlanetGravityModel extends GravityModel {
} }
} }
output.x = maxAbs - ax < ROUNDNESS ? (px > 0 ? +1 : -1) : 0; output.x = maxAbs - ax < c ? (px > 0 ? +1 : -1) : 0;
output.y = maxAbs - ay < ROUNDNESS ? (py > 0 ? +1 : -1) : 0; output.y = maxAbs - ay < c ? (py > 0 ? +1 : -1) : 0;
output.z = maxAbs - az < ROUNDNESS ? (pz > 0 ? +1 : -1) : 0; output.z = maxAbs - az < c ? (pz > 0 ? +1 : -1) : 0;
if (maxAbs - midAbs < ROUNDNESS) { if (maxAbs - midAbs < c) {
output.normalize(); output.normalize();
computeEdgeGravity(output.x, output.y, output.z, px, py, pz, output); computeEdgeGravity(output.x, output.y, output.z, px, py, pz, output);
} else { } else {
assert output.dot(output) == 1 : "maxAbs - midAbs = " + maxAbs + " - " + midAbs + " > " + ROUNDNESS + " yet l*l != 1"; assert output.dot(output) == 1 : "maxAbs - midAbs = " + maxAbs + " - " + midAbs + " > " + c + " yet l*l != 1";
} }
output.mul(-GRAVITATIONAL_ACCELERATION); output.mul(-g);
} }
private void computeEdgeGravity(float lx, float ly, float lz, float rx, float ry, float rz, Vec3 output) { private void computeEdgeGravity(float lx, float ly, float lz, float rx, float ry, float rz, Vec3 output) {
// da math is gud, no worry // da math is gud, no worry
// - Javapony // - Javapony
float r = getInnerRadius();
if (lx == 0) rx = 0; if (lx == 0) rx = 0;
if (ly == 0) ry = 0; if (ly == 0) ry = 0;
if (lz == 0) rz = 0; if (lz == 0) rz = 0;
@ -107,10 +159,10 @@ public class TestPlanetGravityModel extends GravityModel {
float rSquared = rx*rx + ry*ry + rz*rz; float rSquared = rx*rx + ry*ry + rz*rz;
float distanceAlongEdge = scalarProduct - (float) sqrt( float distanceAlongEdge = scalarProduct - (float) sqrt(
scalarProduct*scalarProduct - rSquared + ROUNDNESS*ROUNDNESS scalarProduct*scalarProduct - rSquared + r*r
); );
output.set(lx, ly, lz).mul(-distanceAlongEdge).add(rx, ry, rz).div(ROUNDNESS); output.set(lx, ly, lz).mul(-distanceAlongEdge).add(rx, ry, rz).div(r);
final float f = (float) sqrt(3.0/2); final float f = (float) sqrt(3.0/2);
@ -128,5 +180,15 @@ public class TestPlanetGravityModel extends GravityModel {
AbsFace rounded = AbsFace.roundToFace(chunkPos.x, chunkPos.y, chunkPos.z); AbsFace rounded = AbsFace.roundToFace(chunkPos.x, chunkPos.y, chunkPos.z);
return rounded == null ? AbsFace.POS_Z : rounded; return rounded == null ? AbsFace.POS_Z : rounded;
} }
@Override
protected void doReadSettings(DataInput input) throws IOException, DecodingException {
this.settings.read(input);
}
@Override
protected void doWriteSettings(DataOutput output) throws IOException {
this.settings.write(output);
}
} }

View File

@ -0,0 +1,77 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.test.gen.surface;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.rels.AbsFace;
/**
* A scalar field defined on a plane. For each pair of {@code float}s (north; west) a single {@code float} is defined by this object.
*/
public abstract class SurfaceCachingFloatField extends Namespaced implements SurfaceFloatField {
private final int levels;
private SurfaceFieldRegistry registry = null;
private int index;
public SurfaceCachingFloatField(String id, int levels) {
super(id);
this.levels = levels;
}
int getIndex() {
if (getRegistry() == null) {
throw new IllegalStateException("No registry assigned to field " + this);
}
return index;
}
void setIndex(int index) {
if (getRegistry() == null) {
throw new IllegalStateException("No registry assigned to field " + this);
}
this.index = index;
}
SurfaceFieldRegistry getRegistry() {
return registry;
}
void setRegistry(SurfaceFieldRegistry registry) {
this.registry = registry;
}
public int getLevels() {
return levels;
}
protected abstract float computeDetailAt(AbsFace surface, int level, float north, float west);
@Override
public float get(AbsFace surface, float north, float west) {
float result = 0;
for (int level = 0; level < getLevels(); ++level) {
result += computeDetailAt(surface, level, north, west);
}
return result;
}
}

View File

@ -0,0 +1,29 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.test.gen.surface;
public class SurfaceFieldRegistry {
private int nextIndex = 0;
public void register(SurfaceCachingFloatField field) {
field.setIndex(nextIndex);
nextIndex++;
}
}

View File

@ -0,0 +1,57 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.test.gen.surface;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import ru.windcorp.progressia.common.util.CoordinatePacker;
import ru.windcorp.progressia.common.world.DecodingException;
public class SurfaceNodeStorage {
public static class Node {
// private float[] floats;
}
private final TLongObjectMap<Node> map = new TLongObjectHashMap<>();
public Node getNode(int north, int west) {
return map.get(CoordinatePacker.pack2IntsIntoLong(north, west));
}
public boolean hasNode(int north, int west) {
return map.containsKey(CoordinatePacker.pack2IntsIntoLong(north, west));
}
public void put(int north, int west, Node node) {
map.put(CoordinatePacker.pack2IntsIntoLong(north, west), node);
}
public void read(DataInput input) throws IOException, DecodingException {
System.err.println("PlaneNodeMap.read did nothing because nobody implemented it yet");
}
public void write(DataOutput output) throws IOException {
System.err.println("PlaneNodeMap.write did nothing because nobody implemented it yet");
}
}