2 * Sixth 3D engine. Author: Svjatoslav Agejenko.
3 * This project is released under Creative Commons Zero (CC0) license.
5 package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base;
7 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
8 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
9 import eu.svjatoslav.sixth.e3d.gui.UserRelativityTracker;
10 import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
11 import eu.svjatoslav.sixth.e3d.math.Transform;
12 import eu.svjatoslav.sixth.e3d.math.TransformsStack;
13 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
14 import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator;
15 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
16 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line;
17 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
18 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon;
19 import eu.svjatoslav.sixth.e3d.renderer.raster.slicer.Slicer;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.stream.Collectors;
25 public class AbstractCompositeShape extends AbstractShape {
26 private final List<SubShape> originalSubShapes = new ArrayList<>();
27 private final UserRelativityTracker relativityTracker;
28 double currentSliceFactor = 5;
29 private List<AbstractShape> renderedSubShapes = new ArrayList<>();
30 private boolean slicingOutdated = true;
31 private Transform transform;
33 public AbstractCompositeShape() {
34 this(new Transform());
37 public AbstractCompositeShape(final Point3D location) {
38 this(new Transform(location));
41 public AbstractCompositeShape(final Transform transform) {
42 this.transform = transform;
43 relativityTracker = new UserRelativityTracker();
46 public void addShape(final AbstractShape shape) {
47 addShape(shape, null);
50 public void addShape(final AbstractShape shape, final String groupId) {
51 final SubShape subShape = new SubShape(shape);
52 subShape.setGroup(groupId);
53 subShape.setVisible(true);
54 originalSubShapes.add(subShape);
55 slicingOutdated = true;
59 * This method should be overridden by anyone wanting to customize shape
60 * before it is rendered.
62 public void beforeTransformHook(final TransformsStack transformPipe,
63 final RenderingContext context) {
66 public Point3D getLocation() {
67 return transform.getTranslation();
70 public List<SubShape> getOriginalSubShapes() {
71 return originalSubShapes;
74 public UserRelativityTracker getRelativityTracker() {
75 return relativityTracker;
78 public void hideGroup(final String groupIdentifier) {
79 originalSubShapes.stream().filter(subShape -> subShape.matchesGroup(groupIdentifier)).forEach(subShape -> {
80 subShape.setVisible(false);
81 slicingOutdated = true;
85 private boolean isReslicingNeeded(double proposedNewSliceFactor, double currentSliceFactor) {
90 // reslice if there is significant difference between proposed and current slice factor
91 if (proposedNewSliceFactor > currentSliceFactor) {
92 final double tmp = proposedNewSliceFactor;
93 proposedNewSliceFactor = currentSliceFactor;
94 currentSliceFactor = tmp;
97 return (currentSliceFactor / proposedNewSliceFactor) > 1.5d;
100 public void removeGroup(final String groupIdentifier) {
101 final java.util.Iterator<SubShape> iterator = originalSubShapes
104 while (iterator.hasNext()) {
105 final SubShape subShape = iterator.next();
106 if (subShape.matchesGroup(groupIdentifier)) {
108 slicingOutdated = true;
113 public List<SubShape> getGroup(final String groupIdentifier) {
114 return originalSubShapes.stream().filter(
115 subShape -> subShape.matchesGroup(groupIdentifier))
116 .collect(Collectors.toList());
119 private void resliceIfNeeded() {
121 final double proposedSliceFactor = relativityTracker.proposeSliceFactor();
123 if (isReslicingNeeded(proposedSliceFactor, currentSliceFactor)) {
124 currentSliceFactor = proposedSliceFactor;
130 * Paint solid elements of this composite shape into given color.
132 public void setColor(final Color color) {
133 for (final SubShape subShape : getOriginalSubShapes()) {
134 final AbstractShape shape = subShape.getShape();
136 if (shape instanceof SolidPolygon)
137 ((SolidPolygon) shape).setColor(color);
139 if (shape instanceof Line)
140 ((Line) shape).color = color;
144 public void setGroupForUngrouped(final String groupIdentifier) {
145 originalSubShapes.stream().filter(SubShape::isUngrouped).forEach(subShape -> subShape.setGroup(groupIdentifier));
149 public void setMouseInteractionController(
150 final MouseInteractionController mouseInteractionController) {
151 super.setMouseInteractionController(mouseInteractionController);
153 for (final SubShape subShape : originalSubShapes)
154 subShape.getShape().setMouseInteractionController(
155 mouseInteractionController);
157 slicingOutdated = true;
161 public void setTransform(final Transform transform) {
162 this.transform = transform;
165 public void showGroup(final String groupIdentifier) {
166 originalSubShapes.stream().filter(subShape -> subShape.matchesGroup(groupIdentifier)).forEach(subShape -> {
167 subShape.setVisible(true);
168 slicingOutdated = true;
172 private void reslice() {
173 slicingOutdated = false;
175 final List<AbstractShape> result = new ArrayList<>();
177 final Slicer slicer = new Slicer(currentSliceFactor);
178 originalSubShapes.stream().filter(SubShape::isVisible).forEach(subShape -> {
179 if (subShape.getShape() instanceof TexturedPolygon)
180 slicer.slice((TexturedPolygon) subShape.getShape());
182 result.add(subShape.getShape());
185 result.addAll(slicer.getResult());
187 renderedSubShapes = result;
191 public void transform(final TransformsStack transformPipe,
192 final RenderAggregator aggregator, final RenderingContext context) {
194 // add current composite shape transform to the end of the transform
196 transformPipe.addTransform(transform);
198 relativityTracker.analyze(transformPipe, context);
200 beforeTransformHook(transformPipe, context);
202 // hack, to get somewhat perspective correct textures
205 // transform rendered subshapes
206 for (final AbstractShape shape : renderedSubShapes)
207 shape.transform(transformPipe, aggregator, context);
209 transformPipe.dropTransform();