Skip to content

Commit e108eb9

Browse files
authored
Feature/backgdrop occluder (#941)
1 parent 5ca1598 commit e108eb9

File tree

68 files changed

+1219
-257
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1219
-257
lines changed

UNRELEASED-CHANGES.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
### 🚀 Features & improvements
22

3-
- ...
3+
- Added backdrop occluder shadows (#937)
4+
- `RopeOccluderComponent` adds a backdrop shadow to ropes
5+
- `SoftBodyOccluderComponent` adds a backdrop shadow to soft bodies
6+
- Allow double value strokeWidth for drawing polygons
7+
- Added `Polygon.stroked(double)`, `.moveTo(Vector)` and `.fromBounds(Bounds)`
8+
- Added functions to `Line`: `.expand(double)`, `.isLeft(Vector)`, `.isRight(Vector)` and `.contains(Vector)`
9+
- Rendering non smoothed ropes
10+
- Added `Vector.normalize()`
411

512
### 🪛 Bug Fixes
613

714
- Fixed fast repeating sequence generated by `FastRandom` (#939)
15+
- Fixed missing segment when drawing polygon outline
16+
- Fixed rendering polygon and line stroke width not scaling with zoom
17+
- Fixed light going to occluder edges
818

919
### 🧽 Cleanup & refactoring
1020

11-
- ...
21+
- Moved path creation to `AwtMapper`
1222

1323
### 📦 Dependency updates
1424

15-
- ...
25+
- Bump JUnit to 6.0.3
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: Illumination reworked
3+
authors: [ srcimon ]
4+
tags: [ new-features, milestones ]
5+
---
6+
7+
Hello everyone,
8+
9+
the last three versions of ScrewBox put huge effort to improve the game illumination:
10+
11+
The overall performance was improved significantly by improving the speed of the internally used image filters and
12+
algorithms.
13+
The blurring filter is now 20-30% faster and supports higher blur values for even smoother visuals.
14+
The time consumed for image opacity inversion was reduced by a staggering 98%.
15+
16+
This increased throughput allows for higher-quality shadow maps and heavier shadow blurring without sacrificing frame
17+
rates.
18+
19+
![occluders.png](../docs/guides/soft-physics/occluders.png)
20+
21+
I have introduced new directional light source and backdrop occluders:
22+
23+
- Directional light easily simulate natural illumination like sunlight.
24+
- Backdrop occluders create shadows directly on the background behind objects.
25+
Backdrop occluders work in perfect harmony with soft physics entities.
26+
The shadows add a new level of immersion to these entities.
27+
28+
<!-- truncate -->

docs/docs/core-modules/graphics/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ The following types of lights and occluders are supported:
267267
| point light | a radial light source that will be affected by occluders |
268268
| spot light | a radial light source that won't be affected by occluders |
269269
| directional light | light source that emitts light from from a line |
270+
| backdrop occluder | area that cast shadows on the background |
270271
| occluder | area that cast shadows and also can block lights when rendered on top |
271272
| orthographic wall | an orthographic wall that can be illuminated but will cast shadows (used in common rpg perspective) |
272273
| area light | a area light effect |

docs/docs/guides/soft-physics/index.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,14 @@ cloth.root().add(new ClothRenderComponent(), x -> {
157157

158158
// attach the top border of the flag to the game world
159159
cloth.topBorder().forEach(entity -> entity.remove(PhysicsComponent.class));
160-
```
160+
```
161+
162+
## Illumination
163+
164+
Soft physics objects also support creating backdrop shadows using the `RopeOccluderComponent` and `SoftBodyOccluderComponent`.
165+
Because cloth soft bodies also contain a `SoftBodyComponent` they can also make use of the `SoftBodyOccluderComponent`.
166+
167+
Backdrop shadows are quite expensive and should be used with performance in mind, but the effect might be worth it.
168+
To reduce the performance impact disable the rounding effect in the `ShadowOptions`.
169+
170+
![occluders3.png](occluders.png)
66 KB
Loading

docs/docs/reference/components-overview.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ Get a more inside from the [ropes guide](../guides/soft-physics).
115115
| `SoftStructureComponent` | Links one entity to multiple others with flexible links. Used to create flexible structures. |
116116
| `RopeComponent` | Used to mark the start of a rope. |
117117
| `RopeRenderComponent` | Renders ropes when added to entity also containing a `RopeComponent`. |
118+
| `RopeOccluderComponent` | Adds a backdrop shadow to a rope. |
119+
| `SoftBodyOccluderComponent` | Adds a backdrop shadow to a soft body. |
118120
| `ClothComponent` | Used to mark the root node of a soft body cloth. |
119121
| `ClothRenderComponent` | Adds rendering using mesh shading of soft bodies with a `ClothComponent`. |
120122
| `SoftBodyComponent` | Used to mark a chain or loop of entities as a soft body. |
@@ -173,6 +175,7 @@ To use these components call `environment.enableLight()` first.
173175
| `ConeGlowComponent` | Adds a cone glow at the entity position. |
174176
| `OrthographicWallComponent` | Marks entity as orthographic wall that will only be illuminated from below and cast shadows. |
175177
| `OccluderComponent` | Adds shadow casting from the entity bounds. |
178+
| `BackdropOccluderComponent` | Adds backdrop shadow casting from the entity bounds. |
176179
| `StaticOccluderComponent` | Optimizes performance by combining occluder components that won't move. |
177180

178181
## Particle components

examples/platformer/src/main/java/dev/screwbox/platformer/props/Platfom.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import dev.screwbox.core.Bounds;
44
import dev.screwbox.core.Percent;
55
import dev.screwbox.core.assets.Asset;
6-
import dev.screwbox.core.environment.importing.Blueprint;
76
import dev.screwbox.core.environment.Entity;
87
import dev.screwbox.core.environment.core.TransformComponent;
8+
import dev.screwbox.core.environment.importing.Blueprint;
99
import dev.screwbox.core.environment.light.OccluderComponent;
1010
import dev.screwbox.core.environment.physics.ColliderComponent;
1111
import dev.screwbox.core.environment.physics.CollisionSensorComponent;
@@ -27,7 +27,7 @@ public Entity assembleFrom(GameObject object) {
2727
.add(new RenderComponent(SPRITE, object.layer().order()))
2828
.add(new TransformComponent(Bounds.atPosition(object.position(), 48, 12)))
2929
.add(new CollisionSensorComponent(), sensor -> sensor.range = 1)
30-
.add(new OccluderComponent(false), x -> x.expand = -2)
30+
.add(new OccluderComponent(false))
3131
.add(new MovingPlatformComponent(object.properties().getInt("waypoint"), speed));
3232
}
3333

examples/playground/src/main/java/dev/screwbox/playground/PlaygroundApp.java

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.screwbox.playground;
22

33
import dev.screwbox.core.Angle;
4+
import dev.screwbox.core.Duration;
45
import dev.screwbox.core.Engine;
56
import dev.screwbox.core.Percent;
67
import dev.screwbox.core.ScrewBox;
@@ -12,10 +13,12 @@
1213
import dev.screwbox.core.environment.core.LogFpsSystem;
1314
import dev.screwbox.core.environment.core.TransformComponent;
1415
import dev.screwbox.core.environment.importing.ImportOptions;
16+
import dev.screwbox.core.environment.light.BackdropOccluderComponent;
1517
import dev.screwbox.core.environment.light.DirectionalLightComponent;
1618
import dev.screwbox.core.environment.light.OccluderComponent;
1719
import dev.screwbox.core.environment.light.PointLightComponent;
1820
import dev.screwbox.core.environment.light.StaticOccluderComponent;
21+
import dev.screwbox.core.environment.physics.ChaoticMovementComponent;
1922
import dev.screwbox.core.environment.physics.ColliderComponent;
2023
import dev.screwbox.core.environment.physics.CollisionDetailsComponent;
2124
import dev.screwbox.core.environment.physics.CollisionSensorComponent;
@@ -24,9 +27,16 @@
2427
import dev.screwbox.core.environment.physics.PhysicsComponent;
2528
import dev.screwbox.core.environment.physics.StaticColliderComponent;
2629
import dev.screwbox.core.environment.rendering.RenderComponent;
30+
import dev.screwbox.core.environment.softphysics.RopeOccluderComponent;
31+
import dev.screwbox.core.environment.softphysics.RopeRenderComponent;
32+
import dev.screwbox.core.environment.softphysics.SoftBodyOccluderComponent;
33+
import dev.screwbox.core.environment.softphysics.SoftBodyRenderComponent;
34+
import dev.screwbox.core.environment.softphysics.SoftPhysicsSupport;
2735
import dev.screwbox.core.graphics.Color;
2836
import dev.screwbox.core.graphics.Sprite;
37+
import dev.screwbox.core.graphics.options.ShadowOptions;
2938
import dev.screwbox.core.utils.TileMap;
39+
import dev.screwbox.playground.misc.InteractionSystem;
3040

3141
import static dev.screwbox.core.Vector.$;
3242

@@ -39,13 +49,18 @@ public static void main(String[] args) {
3949
.move($(40, 40))
4050
.setZoom(4);
4151
engine.loop().unlockFps();
52+
4253
engine.graphics().configuration().setLightQuality(Percent.half());
4354
var map = TileMap.fromString("""
4455
O O
4556
P # ### ##
46-
# ## O
47-
O ##
48-
### ######
57+
# RRR## O
58+
T O ##
59+
60+
61+
62+
63+
############ ######
4964
""");
5065
engine.environment()
5166
.enableAllFeatures()
@@ -66,22 +81,41 @@ public static void main(String[] args) {
6681
.add(new StaticColliderComponent())
6782
.add(new RenderComponent(Sprite.placeholder(Color.GREY, 16)))
6883
)
84+
.assignComplex('T', (tile, idPool) -> {
85+
var body = SoftPhysicsSupport.createSoftBody(tile.bounds().expand(-2), idPool);
86+
body.root().add(new SoftBodyRenderComponent(Color.ORANGE.opacity(0.5)), r -> r.outlineColor = Color.ORANGE);
87+
body.root().add(new SoftBodyOccluderComponent(ShadowOptions.rounded().backdropDistance(0.4).distortion(Percent.of(0.04))));
88+
body.forEach(node -> node.get(PhysicsComponent.class).friction = 2);
89+
body.forEach(node -> node.add(new LeftRightControlComponent()));
90+
body.forEach(node -> node.add(new JumpControlComponent()));
91+
return body;
92+
})
93+
.assignComplex('R', (tile, idPool) -> {
94+
var rope = SoftPhysicsSupport.createRope(tile.position().addY(tile.size().height() / -2.0), tile.position().addY(20), 9, idPool);
95+
rope.forEach(node -> node.get(PhysicsComponent.class).friction = 2);
96+
rope.forEach(node -> node.add(new ChaoticMovementComponent(400, Duration.ofMillis(800))));
97+
rope.root()
98+
.add(new RopeOccluderComponent(ShadowOptions.angular().backdropDistance(0.45)))
99+
.add(new RopeRenderComponent(Color.WHITE, 1.25))
100+
.remove(PhysicsComponent.class);
101+
return rope;
102+
})
69103
.assign('P', tile -> new Entity().bounds(tile.bounds().expand(-8))
70-
.add(new StaticOccluderComponent())
71-
.add(new OccluderComponent(false))
72104
.add(new PhysicsComponent(), p -> p.friction = 3)
73105
.add(new LeftRightControlComponent())
106+
.add(new BackdropOccluderComponent(ShadowOptions.angular().backdropDistance(0.75)))
74107
.add(new JumpControlComponent(), j -> j.acceleration = 300)
75108
.add(new CollisionSensorComponent())
76109
.add(new CollisionDetailsComponent())
77110
.add(new SuspendJumpControlComponent(), s -> s.maxJumps = 2)
78111
.add(new RenderComponent(Sprite.placeholder(Color.YELLOW, 8)))))
79112
.addEntity(new Entity().add(new GravityComponent(Vector.y(500))))
80113
.addSystem(new LogFpsSystem())
81-
.addEntity(new Entity().add(new TransformComponent()).add(new CursorAttachmentComponent()).add(new PointLightComponent(80, Color.BLACK)))
82-
.addEntity(new Entity().bounds(map.bounds().expand(1000)).add(new DirectionalLightComponent(), d -> d.angle = Angle.degrees(-10)))
114+
.addSystem(new InteractionSystem())
115+
.addEntity(new Entity().add(new TransformComponent(0, 0, 120, 40)).add(new CursorAttachmentComponent()).add(new PointLightComponent(60, Color.BLACK)))
83116
.addSystem(e -> e.environment().tryFetchSingletonComponent(DirectionalLightComponent.class).ifPresent(d -> d.angle = Angle.degrees(e.mouse().position().x() / 4)))
84-
.addSystem(e -> e.graphics().canvas().fillWith(Color.BLUE));
117+
.addSystem(e -> e.graphics().canvas().fillWith(Color.BLUE))
118+
.addSystem(e -> e.graphics().camera().setZoom(e.graphics().camera().zoom() + e.mouse().unitsScrolled() / 10.0));
85119

86120
engine.start();
87121
}

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
<dependency>
7272
<groupId>org.junit</groupId>
7373
<artifactId>junit-bom</artifactId>
74-
<version>6.0.2</version>
74+
<version>6.0.3</version>
7575
<type>pom</type>
7676
<scope>import</scope>
7777
</dependency>

screwbox-core/src/main/java/dev/screwbox/core/Line.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,55 @@ public Line move(final Vector movement) {
251251
}
252252

253253
/**
254-
* Sets the length to the specified value.
254+
* Sets the length to the specified value. Moves only the {@link #end()}.
255255
*
256256
* @since 3.22.0
257257
*/
258258
public Line length(final double distance) {
259259
return Line.between(start, start.add(end.substract(start).length(distance)));
260260
}
261+
262+
/**
263+
* Expands the lenght of the line in both directions.
264+
*
265+
* @since 3.23.0
266+
*/
267+
public Line expand(final double length) {
268+
final var delta = end.substract(start);
269+
final var x = delta.x() / delta.length() * length * 0.5;
270+
final var y = delta.y() / delta.length() * length * 0.5;
271+
return Line.between(start.add(-x, -y), end.add(x, y));
272+
}
273+
274+
/**
275+
* Return {@code true} if position is left of line.
276+
*
277+
* @since 3.23.0
278+
*/
279+
public boolean isLeft(final Vector position) {
280+
return calculateShoelaceOf(position) < 0;
281+
}
282+
283+
/**
284+
* Return {@code true} if position is right of line.
285+
*
286+
* @since 3.23.0
287+
*/
288+
public boolean isRight(final Vector position) {
289+
return calculateShoelaceOf(position) > 0;
290+
}
291+
292+
/**
293+
* Return {@code true} if position is on the line
294+
*
295+
* @since 3.23.0
296+
*/
297+
public boolean contains(final Vector position) {
298+
return calculateShoelaceOf(position) == 0;
299+
}
300+
301+
private double calculateShoelaceOf(final Vector position) {
302+
return (end.x() - start.x()) * (position.y() - start.y()) -
303+
(end.y() - start.y()) * (position.x() - start.x());
304+
}
261305
}

0 commit comments

Comments
 (0)