Bewegung im Wasser bearbeitet
This commit is contained in:
BIN
blight-assets/src/main/resources/audio/River Loop.ogg
Normal file
BIN
blight-assets/src/main/resources/audio/River Loop.ogg
Normal file
Binary file not shown.
BIN
blight-assets/src/main/resources/audio/River Stream Loop.ogg
Normal file
BIN
blight-assets/src/main/resources/audio/River Stream Loop.ogg
Normal file
Binary file not shown.
BIN
blight-assets/src/main/resources/audio/Waterfall Loop.ogg
Normal file
BIN
blight-assets/src/main/resources/audio/Waterfall Loop.ogg
Normal file
Binary file not shown.
@@ -15,13 +15,18 @@ import com.jme3.scene.Geometry;
|
|||||||
import com.jme3.scene.Mesh;
|
import com.jme3.scene.Mesh;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.VertexBuffer;
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.texture.Image;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
|
import com.jme3.texture.Texture2D;
|
||||||
import com.jme3.util.BufferUtils;
|
import com.jme3.util.BufferUtils;
|
||||||
|
import de.blight.common.PlacedModel;
|
||||||
|
import de.blight.common.PlacedModelIO;
|
||||||
import de.blight.common.PlacedWater;
|
import de.blight.common.PlacedWater;
|
||||||
import de.blight.common.WaterBodyIO;
|
import de.blight.common.WaterBodyIO;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -47,6 +52,9 @@ public class WaterBodyState extends BaseAppState {
|
|||||||
private final List<WaterPolygonFilter> filters = new ArrayList<>();
|
private final List<WaterPolygonFilter> filters = new ArrayList<>();
|
||||||
private final List<Geometry> inclinedGeos = new ArrayList<>();
|
private final List<Geometry> inclinedGeos = new ArrayList<>();
|
||||||
private final List<Material> animatedMaterials = new ArrayList<>();
|
private final List<Material> animatedMaterials = new ArrayList<>();
|
||||||
|
private final List<Geometry> objectFoams = new ArrayList<>();
|
||||||
|
private final List<Material> objectFoamMats = new ArrayList<>();
|
||||||
|
private Texture2D foamRingTex = null;
|
||||||
private float time = 0f;
|
private float time = 0f;
|
||||||
|
|
||||||
private Node rootNode;
|
private Node rootNode;
|
||||||
@@ -85,15 +93,20 @@ public class WaterBodyState extends BaseAppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info("{} flache + {} abschüssige Wasserfläche(n) geladen.", flat, inclined);
|
log.info("{} flache + {} abschüssige Wasserfläche(n) geladen.", flat, inclined);
|
||||||
|
// buildObjectFoam(assets, bodies); // TODO: Objekt-Schaum deaktiviert
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(float tpf) {
|
public void update(float tpf) {
|
||||||
if (!animatedMaterials.isEmpty()) {
|
if (!animatedMaterials.isEmpty() || !objectFoamMats.isEmpty()) {
|
||||||
time += tpf;
|
time += tpf;
|
||||||
for (Material m : animatedMaterials) {
|
for (Material m : animatedMaterials) {
|
||||||
m.setFloat("Time", time);
|
m.setFloat("Time", time);
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < objectFoamMats.size(); i++) {
|
||||||
|
float a = 0.22f + 0.12f * (float) Math.sin(time * 2.2f + i * 1.3f);
|
||||||
|
objectFoamMats.get(i).setColor("Color", new ColorRGBA(1f, 1f, 1f, a));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +117,9 @@ public class WaterBodyState extends BaseAppState {
|
|||||||
for (Geometry g : inclinedGeos) rootNode.detachChild(g);
|
for (Geometry g : inclinedGeos) rootNode.detachChild(g);
|
||||||
inclinedGeos.clear();
|
inclinedGeos.clear();
|
||||||
animatedMaterials.clear();
|
animatedMaterials.clear();
|
||||||
|
for (Geometry g : objectFoams) rootNode.detachChild(g);
|
||||||
|
objectFoams.clear();
|
||||||
|
objectFoamMats.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onEnable() {}
|
@Override protected void onEnable() {}
|
||||||
@@ -144,6 +160,109 @@ public class WaterBodyState extends BaseAppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Objekt-Schaum ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Meereshöhe für den globalen DynamicWaterFilter (kein PlacedWater-Body). */
|
||||||
|
private static final float SEA_LEVEL = 0f;
|
||||||
|
|
||||||
|
private void buildObjectFoam(AssetManager assets, List<PlacedWater> bodies) {
|
||||||
|
List<PlacedWater> flatBodies = bodies.stream().filter(PlacedWater::isFlat).toList();
|
||||||
|
|
||||||
|
List<PlacedModel> models;
|
||||||
|
try {
|
||||||
|
models = PlacedModelIO.load();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[WaterBodyState] Objekte für Objekt-Schaum nicht ladbar: {}", e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (PlacedModel m : models) {
|
||||||
|
boolean added = false;
|
||||||
|
|
||||||
|
// 1. Definierte flache Wasserkörper (Seen, Teiche …)
|
||||||
|
for (PlacedWater body : flatBodies) {
|
||||||
|
if (!pointInPolygon(m.x(), m.z(), body.pointsX(), body.pointsZ())) continue;
|
||||||
|
if (m.y() > body.waterHeight() + 1.0f) continue;
|
||||||
|
if (m.y() < body.waterHeight() - 6.0f) continue;
|
||||||
|
|
||||||
|
attachObjectFoam(new Vector3f(m.x(), body.waterHeight() + 0.04f, m.z()),
|
||||||
|
m.scale(), assets);
|
||||||
|
count++;
|
||||||
|
added = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Meereshöhe (globaler DynamicWaterFilter, kein Polygon nötig)
|
||||||
|
if (!added && m.y() <= SEA_LEVEL + 1.0f && m.y() >= SEA_LEVEL - 6.0f) {
|
||||||
|
attachObjectFoam(new Vector3f(m.x(), SEA_LEVEL + 0.04f, m.z()),
|
||||||
|
m.scale(), assets);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("[WaterBodyState] {} Objekt-Schaum-Scheibe(n) platziert.", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attachObjectFoam(Vector3f center, float scale, AssetManager assets) {
|
||||||
|
float r = com.jme3.math.FastMath.clamp(scale * 0.5f, 0.4f, 2.0f);
|
||||||
|
// Horizontales Quad in der XZ-Ebene, texturiert mit Ring-Textur
|
||||||
|
FloatBuffer pos = BufferUtils.createFloatBuffer(new float[]{ -r,0,-r, r,0,-r, r,0,r, -r,0,r });
|
||||||
|
FloatBuffer uv = BufferUtils.createFloatBuffer(new float[]{ 0,0, 1,0, 1,1, 0,1 });
|
||||||
|
FloatBuffer norm = BufferUtils.createFloatBuffer(new float[]{ 0,1,0, 0,1,0, 0,1,0, 0,1,0 });
|
||||||
|
IntBuffer idx = BufferUtils.createIntBuffer(new int[]{ 0,1,2, 0,2,3 });
|
||||||
|
Mesh mesh = new Mesh();
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.Position, 3, pos);
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, uv);
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.Normal, 3, norm);
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.Index, 3, idx);
|
||||||
|
mesh.updateBound(); mesh.updateCounts();
|
||||||
|
|
||||||
|
Material mat = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
mat.setTexture("ColorMap", getFoamRingTex());
|
||||||
|
mat.setColor("Color", new ColorRGBA(1f, 1f, 1f, 0.28f));
|
||||||
|
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
|
||||||
|
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
|
||||||
|
|
||||||
|
Geometry geo = new Geometry("obj_foam", mesh);
|
||||||
|
geo.setMaterial(mat);
|
||||||
|
geo.setLocalTranslation(center);
|
||||||
|
geo.setQueueBucket(RenderQueue.Bucket.Transparent);
|
||||||
|
rootNode.attachChild(geo);
|
||||||
|
objectFoams.add(geo);
|
||||||
|
objectFoamMats.add(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ring-Textur: transparent innen + außen, opak in der Mitte — sin(π*d)-Profil. */
|
||||||
|
private Texture2D getFoamRingTex() {
|
||||||
|
if (foamRingTex != null) return foamRingTex;
|
||||||
|
int size = 64;
|
||||||
|
ByteBuffer buf = ByteBuffer.allocateDirect(size * size * 4);
|
||||||
|
float c = (size - 1) / 2f;
|
||||||
|
for (int y = 0; y < size; y++) {
|
||||||
|
for (int x = 0; x < size; x++) {
|
||||||
|
float dx = (x - c) / c, dy = (y - c) / c;
|
||||||
|
float d = (float) Math.sqrt(dx * dx + dy * dy); // 0=Mitte, 1=Rand
|
||||||
|
float a = (d < 1f) ? (float) Math.sin(Math.PI * d) : 0f;
|
||||||
|
buf.put((byte) 255).put((byte) 255).put((byte) 255).put((byte)(a * 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.flip();
|
||||||
|
foamRingTex = new Texture2D(new Image(Image.Format.RGBA8, size, size, buf));
|
||||||
|
return foamRingTex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean pointInPolygon(float px, float pz, float[] xs, float[] zs) {
|
||||||
|
boolean inside = false;
|
||||||
|
int n = xs.length;
|
||||||
|
for (int i = 0, j = n - 1; i < n; j = i++) {
|
||||||
|
if (((zs[i] > pz) != (zs[j] > pz)) &&
|
||||||
|
(px < (xs[j] - xs[i]) * (pz - zs[i]) / (zs[j] - zs[i]) + xs[i])) {
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inside;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Baut das Wasserfall-Mesh aus den gespeicherten Eckpunkt-Koordinaten.
|
* Baut das Wasserfall-Mesh aus den gespeicherten Eckpunkt-Koordinaten.
|
||||||
* Y wird zwischen Nachbar-Ecken linear interpoliert – kein Terrain-Raycast,
|
* Y wird zwischen Nachbar-Ecken linear interpoliert – kein Terrain-Raycast,
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
package de.blight.game.state;
|
package de.blight.game.state;
|
||||||
|
|
||||||
import com.jme3.app.Application;
|
import com.jme3.app.Application;
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
import com.jme3.app.state.BaseAppState;
|
import com.jme3.app.state.BaseAppState;
|
||||||
import com.jme3.bullet.control.CharacterControl;
|
import com.jme3.bullet.control.CharacterControl;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.material.RenderState;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Vector2f;
|
import com.jme3.math.Vector2f;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.renderer.queue.RenderQueue;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.util.BufferUtils;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.Texture2D;
|
import com.jme3.texture.Texture2D;
|
||||||
@@ -15,6 +26,8 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,9 +50,9 @@ public class WaterInteractionState extends BaseAppState {
|
|||||||
private static final int N = 256;
|
private static final int N = 256;
|
||||||
private static final float HALF_EXTENT = 64f; // half of 128m world coverage
|
private static final float HALF_EXTENT = 64f; // half of 128m world coverage
|
||||||
private static final float INV_EXTENT = 1.0f / (HALF_EXTENT * 2f);
|
private static final float INV_EXTENT = 1.0f / (HALF_EXTENT * 2f);
|
||||||
private static final float WAVE_K = 0.25f; // wave eq. coefficient (stability: ≤ 0.5)
|
private static final float WAVE_K = 0.004f; // wave eq. coefficient — speed = sqrt(K)*0.5m*fps ≈ sqrt(0.004)*30 ≈ 1.9 m/s
|
||||||
private static final float DAMPING = 0.995f;
|
private static final float DAMPING = 0.985f; // stärkere Dämpfung da langsame Wellen länger verweilen
|
||||||
private static final float WAVE_STRENGTH = 6.0f; // normal perturbation scale in shader
|
private static final float WAVE_STRENGTH = 1.5f; // normal perturbation scale in shader
|
||||||
private static final float BLOB_RADIUS_TEX = 3.0f; // blob radius in texels
|
private static final float BLOB_RADIUS_TEX = 3.0f; // blob radius in texels
|
||||||
private static final float BLOB_COOLDOWN = 0.07f; // seconds between blob injections
|
private static final float BLOB_COOLDOWN = 0.07f; // seconds between blob injections
|
||||||
private static final float RECENTER_THRESH = HALF_EXTENT * 0.45f; // ~29m
|
private static final float RECENTER_THRESH = HALF_EXTENT * 0.45f; // ~29m
|
||||||
@@ -66,6 +79,12 @@ public class WaterInteractionState extends BaseAppState {
|
|||||||
private float waterHeight = 0f;
|
private float waterHeight = 0f;
|
||||||
private final Vector3f prevPos = new Vector3f(Float.NaN, 0f, Float.NaN);
|
private final Vector3f prevPos = new Vector3f(Float.NaN, 0f, Float.NaN);
|
||||||
|
|
||||||
|
// ── Player foam ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private Geometry foamGeo;
|
||||||
|
private Material foamMat;
|
||||||
|
private float foamTime = 0f;
|
||||||
|
|
||||||
public WaterInteractionState(DynamicWaterFilter filter) {
|
public WaterInteractionState(DynamicWaterFilter filter) {
|
||||||
this.dynFilter = filter;
|
this.dynFilter = filter;
|
||||||
}
|
}
|
||||||
@@ -100,6 +119,8 @@ public class WaterInteractionState extends BaseAppState {
|
|||||||
waveTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
waveTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
||||||
waveTex.setWrap(Texture.WrapMode.EdgeClamp);
|
waveTex.setWrap(Texture.WrapMode.EdgeClamp);
|
||||||
|
|
||||||
|
// buildPlayerFoam(app); // TODO: Spieler-Schaum deaktiviert
|
||||||
|
|
||||||
log.debug("[WaterInteraction] Simulation initialized ({}×{}, {}m coverage)", N, N, HALF_EXTENT * 2f);
|
log.debug("[WaterInteraction] Simulation initialized ({}×{}, {}m coverage)", N, N, HALF_EXTENT * 2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +130,7 @@ public class WaterInteractionState extends BaseAppState {
|
|||||||
waveBuf = null;
|
waveBuf = null;
|
||||||
waveImg = null;
|
waveImg = null;
|
||||||
waveTex = null;
|
waveTex = null;
|
||||||
|
if (foamGeo != null) foamGeo.removeFromParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onEnable() {}
|
@Override protected void onEnable() {}
|
||||||
@@ -125,6 +147,7 @@ public class WaterInteractionState extends BaseAppState {
|
|||||||
paramsApplied = true;
|
paramsApplied = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean playerInWater = false;
|
||||||
if (playerControl != null) {
|
if (playerControl != null) {
|
||||||
Vector3f pos = playerControl.getPhysicsLocation();
|
Vector3f pos = playerControl.getPhysicsLocation();
|
||||||
|
|
||||||
@@ -132,11 +155,12 @@ public class WaterInteractionState extends BaseAppState {
|
|||||||
|
|
||||||
float feetY = pos.y - 0.9f; // CharacterControl position = capsule center, ~0.9m above feet
|
float feetY = pos.y - 0.9f; // CharacterControl position = capsule center, ~0.9m above feet
|
||||||
if (feetY < waterHeight + 0.4f) { // player in or just above water
|
if (feetY < waterHeight + 0.4f) { // player in or just above water
|
||||||
|
playerInWater = true;
|
||||||
float speed = Float.isNaN(prevPos.x) ? 0f : pos.distance(prevPos) / tpf;
|
float speed = Float.isNaN(prevPos.x) ? 0f : pos.distance(prevPos) / tpf;
|
||||||
if (speed > 0.3f) { // ignore sub-threshold shuffling
|
if (speed > 0.3f) { // ignore sub-threshold shuffling
|
||||||
blobTimer -= tpf;
|
blobTimer -= tpf;
|
||||||
if (blobTimer <= 0f) {
|
if (blobTimer <= 0f) {
|
||||||
float strength = FastMath.clamp(speed / 8.0f, 0.1f, 1.0f);
|
float strength = FastMath.clamp(speed / 8.0f, 0.05f, 0.3f);
|
||||||
addBlob(pos.x, pos.z, strength);
|
addBlob(pos.x, pos.z, strength);
|
||||||
blobTimer = BLOB_COOLDOWN;
|
blobTimer = BLOB_COOLDOWN;
|
||||||
}
|
}
|
||||||
@@ -145,6 +169,8 @@ public class WaterInteractionState extends BaseAppState {
|
|||||||
prevPos.set(pos);
|
prevPos.set(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updatePlayerFoam(tpf, playerInWater); // TODO: Spieler-Schaum deaktiviert
|
||||||
|
|
||||||
runSimulation();
|
runSimulation();
|
||||||
uploadTexture();
|
uploadTexture();
|
||||||
}
|
}
|
||||||
@@ -210,4 +236,63 @@ public class WaterInteractionState extends BaseAppState {
|
|||||||
waveBuf.rewind();
|
waveBuf.rewind();
|
||||||
waveImg.setUpdateNeeded();
|
waveImg.setUpdateNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Player foam ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void buildPlayerFoam(Application app) {
|
||||||
|
Node root = ((SimpleApplication) app).getRootNode();
|
||||||
|
float r = 0.38f;
|
||||||
|
FloatBuffer pos = BufferUtils.createFloatBuffer(new float[]{ -r,0,-r, r,0,-r, r,0,r, -r,0,r });
|
||||||
|
FloatBuffer uv = BufferUtils.createFloatBuffer(new float[]{ 0,0, 1,0, 1,1, 0,1 });
|
||||||
|
FloatBuffer norm = BufferUtils.createFloatBuffer(new float[]{ 0,1,0, 0,1,0, 0,1,0, 0,1,0 });
|
||||||
|
IntBuffer idx = BufferUtils.createIntBuffer(new int[]{ 0,1,2, 0,2,3 });
|
||||||
|
Mesh mesh = new Mesh();
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.Position, 3, pos);
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, uv);
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.Normal, 3, norm);
|
||||||
|
mesh.setBuffer(VertexBuffer.Type.Index, 3, idx);
|
||||||
|
mesh.updateBound(); mesh.updateCounts();
|
||||||
|
|
||||||
|
foamMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
foamMat.setTexture("ColorMap", buildFoamRingTex());
|
||||||
|
foamMat.setColor("Color", new ColorRGBA(1f, 1f, 1f, 0.30f));
|
||||||
|
foamMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
|
||||||
|
foamMat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
|
||||||
|
|
||||||
|
foamGeo = new Geometry("player_foam", mesh);
|
||||||
|
foamGeo.setMaterial(foamMat);
|
||||||
|
foamGeo.setQueueBucket(RenderQueue.Bucket.Transparent);
|
||||||
|
foamGeo.setCullHint(Spatial.CullHint.Always);
|
||||||
|
root.attachChild(foamGeo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePlayerFoam(float tpf, boolean inWater) {
|
||||||
|
if (foamGeo == null || playerControl == null) return;
|
||||||
|
if (inWater) {
|
||||||
|
Vector3f pos = playerControl.getPhysicsLocation();
|
||||||
|
foamGeo.setLocalTranslation(pos.x, waterHeight + 0.04f, pos.z);
|
||||||
|
foamGeo.setCullHint(Spatial.CullHint.Inherit);
|
||||||
|
foamTime += tpf;
|
||||||
|
float a = 0.20f + 0.12f * FastMath.sin(foamTime * 2.5f);
|
||||||
|
foamMat.setColor("Color", new ColorRGBA(1f, 1f, 1f, a));
|
||||||
|
} else {
|
||||||
|
foamGeo.setCullHint(Spatial.CullHint.Always);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Texture2D buildFoamRingTex() {
|
||||||
|
int size = 64;
|
||||||
|
ByteBuffer buf = ByteBuffer.allocateDirect(size * size * 4);
|
||||||
|
float c = (size - 1) / 2f;
|
||||||
|
for (int y = 0; y < size; y++) {
|
||||||
|
for (int x = 0; x < size; x++) {
|
||||||
|
float dx = (x - c) / c, dy = (y - c) / c;
|
||||||
|
float d = (float) Math.sqrt(dx * dx + dy * dy);
|
||||||
|
float a = (d < 1f) ? (float) Math.sin(Math.PI * d) : 0f;
|
||||||
|
buf.put((byte) 255).put((byte) 255).put((byte) 255).put((byte)(a * 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.flip();
|
||||||
|
return new Texture2D(new Image(Image.Format.RGBA8, size, size, buf));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user