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.TransformsPipeline;
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;
26 * In order to get perspective correct textures, large textured polygons are
27 * sliced into smaller ones.
30 public class AbstractCompositeShape extends AbstractShape {
31 private final List<SubShape> originalSubShapes = new ArrayList<>();
32 private final UserRelativityTracker relativityTracker;
33 double currentSliceFactor = 5;
34 private List<AbstractShape> renderedSubShapes = new ArrayList<>();
35 private boolean slicingOutdated = true;
36 private Transform transform;
38 public AbstractCompositeShape() {
39 this(new Transform());
42 public AbstractCompositeShape(final Point3D location) {
43 this(new Transform(location));
46 public AbstractCompositeShape(final Transform transform) {
47 this.transform = transform;
48 relativityTracker = new UserRelativityTracker();
51 public void addShape(final AbstractShape shape) {
52 addShape(shape, null);
55 public void addShape(final AbstractShape shape, final String groupId) {
56 final SubShape subShape = new SubShape(shape);
57 subShape.setGroup(groupId);
58 subShape.setVisible(true);
59 originalSubShapes.add(subShape);
60 slicingOutdated = true;
64 * This method should be overridden by anyone wanting to customize shape
65 * before it is rendered.
67 public void beforeTransformHook(final TransformsPipeline transformPipe,
68 final RenderingContext context) {
71 public Point3D getLocation() {
72 return transform.getTranslation();
75 public List<SubShape> getOriginalSubShapes() {
76 return originalSubShapes;
79 public UserRelativityTracker getRelativityTracker() {
80 return relativityTracker;
83 public void hideGroup(final String groupIdentifier) {
84 originalSubShapes.stream().filter(subShape -> subShape.matchesGroup(groupIdentifier)).forEach(subShape -> {
85 subShape.setVisible(false);
86 slicingOutdated = true;
90 private boolean isReslicingNeeded(double sliceFactor1, double sliceFactor2) {
95 if (sliceFactor1 > sliceFactor2) {
96 final double tmp = sliceFactor1;
97 sliceFactor1 = sliceFactor2;
101 return (sliceFactor2 / sliceFactor1) > 1.5d;
105 public void removeGroup(final String groupIdentifier) {
106 final java.util.Iterator<SubShape> iterator = originalSubShapes
109 while (iterator.hasNext()) {
110 final SubShape subShape = iterator.next();
111 if (subShape.matchesGroup(groupIdentifier)) {
113 slicingOutdated = true;
118 public List<SubShape> getGroup(final String groupIdentifier) {
119 return originalSubShapes.stream().filter(
120 subShape -> subShape.matchesGroup(groupIdentifier))
121 .collect(Collectors.toList());
124 private void resliceIfNeeded() {
126 final double proposedSliceFactor = relativityTracker
127 .proposeSliceFactor();
129 if (isReslicingNeeded(proposedSliceFactor, currentSliceFactor)) {
130 currentSliceFactor = proposedSliceFactor;
136 * Paint solid elements of this composite shape into given color.
138 public void setColor(final Color color) {
139 for (final SubShape subShape : getOriginalSubShapes()) {
140 final AbstractShape shape = subShape.getShape();
142 if (shape instanceof SolidPolygon)
143 ((SolidPolygon) shape).setColor(color);
145 if (shape instanceof Line)
146 ((Line) shape).color = color;
150 public void setGroupForUngrouped(final String groupIdentifier) {
151 originalSubShapes.stream().filter(SubShape::isUngrouped).forEach(subShape -> subShape.setGroup(groupIdentifier));
155 public void setMouseInteractionController(
156 final MouseInteractionController mouseInteractionController) {
157 super.setMouseInteractionController(mouseInteractionController);
159 for (final SubShape subShape : originalSubShapes)
160 subShape.getShape().setMouseInteractionController(
161 mouseInteractionController);
163 slicingOutdated = true;
167 public void setTransform(final Transform transform) {
168 this.transform = transform;
171 public void showGroup(final String groupIdentifier) {
172 originalSubShapes.stream().filter(subShape -> subShape.matchesGroup(groupIdentifier)).forEach(subShape -> {
173 subShape.setVisible(true);
174 slicingOutdated = true;
178 private void slice() {
179 slicingOutdated = false;
181 final List<AbstractShape> result = new ArrayList<>();
183 final Slicer slicer = new Slicer(currentSliceFactor);
184 originalSubShapes.stream().filter(subShape -> subShape.isVisible()).forEach(subShape -> {
185 if (subShape.getShape() instanceof TexturedPolygon)
186 slicer.slice((TexturedPolygon) subShape.getShape());
188 result.add(subShape.getShape());
191 result.addAll(slicer.getResult());
193 renderedSubShapes = result;
197 public void transform(final TransformsPipeline transformPipe,
198 final RenderAggregator aggregator, final RenderingContext context) {
200 // add current composite shape transform to the end of the transform
202 transformPipe.addTransform(transform);
204 relativityTracker.analyze(transformPipe, context);
206 beforeTransformHook(transformPipe, context);
208 // hack, to get somewhat perspective correct textures
211 // transform rendered subshapes
212 for (final AbstractShape shape : renderedSubShapes)
213 shape.transform(transformPipe, aggregator, context);
215 transformPipe.dropTransform();