2 * Sixth 3D engine. Author: Svjatoslav Agejenko.
3 * This project is released under Creative Commons Zero (CC0) license.
8 package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base;
10 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
11 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
12 import eu.svjatoslav.sixth.e3d.gui.UserRelativityTracker;
13 import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
14 import eu.svjatoslav.sixth.e3d.math.Transform;
15 import eu.svjatoslav.sixth.e3d.math.TransformPipe;
16 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
17 import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator;
18 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
19 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line;
20 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
21 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon;
22 import eu.svjatoslav.sixth.e3d.renderer.raster.slicer.Slicer;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.stream.Collectors;
29 * In order to get perspective correct textures, large textured polygons are
30 * sliced into smaller ones.
33 public class AbstractCompositeShape extends AbstractShape {
34 private final List<SubShape> originalSubShapes = new ArrayList<>();
35 private final UserRelativityTracker relativityTracker;
36 double currentSliceFactor = 5;
37 private List<AbstractShape> renderedSubShapes = new ArrayList<>();
38 private boolean slicingOutdated = true;
39 private Transform transform;
41 public AbstractCompositeShape() {
42 this(new Transform());
45 public AbstractCompositeShape(final Point3D location) {
46 this(new Transform(location));
49 public AbstractCompositeShape(final Transform transform) {
50 this.transform = transform;
51 relativityTracker = new UserRelativityTracker();
54 public void addShape(final AbstractShape shape) {
55 addShape(shape, null);
58 public void addShape(final AbstractShape shape, final String groupId) {
59 final SubShape subShape = new SubShape(shape);
60 subShape.setGroup(groupId);
61 subShape.setVisible(true);
62 originalSubShapes.add(subShape);
63 slicingOutdated = true;
67 * This method should be overridden by anyone wanting to customize shape
68 * before it is rendered.
70 public void beforeTransformHook(final TransformPipe transformPipe,
71 final RenderingContext context) {
74 public Point3D getLocation() {
75 return transform.getTranslation();
78 public List<SubShape> getOriginalSubShapes() {
79 return originalSubShapes;
82 public UserRelativityTracker getRelativityTracker() {
83 return relativityTracker;
86 public void hideGroup(final String groupIdentifier) {
87 originalSubShapes.stream().filter(subShape -> subShape.matchesGroup(groupIdentifier)).forEach(subShape -> {
88 subShape.setVisible(false);
89 slicingOutdated = true;
93 private boolean isReslicingNeeded(double sliceFactor1, double sliceFactor2) {
98 if (sliceFactor1 > sliceFactor2) {
99 final double tmp = sliceFactor1;
100 sliceFactor1 = sliceFactor2;
104 return (sliceFactor2 / sliceFactor1) > 1.5d;
108 public void removeGroup(final String groupIdentifier) {
109 final java.util.Iterator<SubShape> iterator = originalSubShapes
112 while (iterator.hasNext()) {
113 final SubShape subShape = iterator.next();
114 if (subShape.matchesGroup(groupIdentifier)) {
116 slicingOutdated = true;
121 public List<SubShape> getGroup(final String groupIdentifier) {
122 return originalSubShapes.stream().filter(
123 subShape -> subShape.matchesGroup(groupIdentifier))
124 .collect(Collectors.toList());
127 private void resliceIfNeeded() {
129 final double proposedSliceFactor = relativityTracker
130 .proposeSliceFactor();
132 if (isReslicingNeeded(proposedSliceFactor, currentSliceFactor)) {
133 currentSliceFactor = proposedSliceFactor;
139 * Paint solid elements of this composite shape into given color.
141 public void setColor(final Color color) {
142 for (final SubShape subShape : getOriginalSubShapes()) {
143 final AbstractShape shape = subShape.getShape();
145 if (shape instanceof SolidPolygon)
146 ((SolidPolygon) shape).setColor(color);
148 if (shape instanceof Line)
149 ((Line) shape).color = color;
153 public void setGroupForUngrouped(final String groupIdentifier) {
154 originalSubShapes.stream().filter(SubShape::isUngrouped).forEach(subShape -> subShape.setGroup(groupIdentifier));
158 public void setMouseInteractionController(
159 final MouseInteractionController mouseInteractionController) {
160 super.setMouseInteractionController(mouseInteractionController);
162 for (final SubShape subShape : originalSubShapes)
163 subShape.getShape().setMouseInteractionController(
164 mouseInteractionController);
166 slicingOutdated = true;
170 public void setTransform(final Transform transform) {
171 this.transform = transform;
174 public void showGroup(final String groupIdentifier) {
175 originalSubShapes.stream().filter(subShape -> subShape.matchesGroup(groupIdentifier)).forEach(subShape -> {
176 subShape.setVisible(true);
177 slicingOutdated = true;
181 private void slice() {
182 slicingOutdated = false;
184 final List<AbstractShape> result = new ArrayList<>();
186 final Slicer slicer = new Slicer(currentSliceFactor);
187 originalSubShapes.stream().filter(subShape -> subShape.isVisible()).forEach(subShape -> {
188 if (subShape.getShape() instanceof TexturedPolygon)
189 slicer.slice((TexturedPolygon) subShape.getShape());
191 result.add(subShape.getShape());
194 result.addAll(slicer.getResult());
196 renderedSubShapes = result;
200 public void transform(final TransformPipe transformPipe,
201 final RenderAggregator aggregator, final RenderingContext context) {
203 // add current composite shape transform to the end of the transform
205 transformPipe.addTransform(transform);
207 relativityTracker.analyze(transformPipe, context);
209 beforeTransformHook(transformPipe, context);
211 // hack, to get somewhat perspective correct textures
214 // transform rendered subshapes
215 for (final AbstractShape shape : renderedSubShapes)
216 shape.transform(transformPipe, aggregator, context);
218 transformPipe.dropTransform();