Commit 297334ab authored by Matthias Betz's avatar Matthias Betz
Browse files

Add design spec for OpenGL 3D view



Replaces the JavaFX SubScene/MeshView rendering with an OpenGL viewport
(openglfx-jogl, GL 3.3 core), single global VBO, GPU color-picking, and
baked fake shading. Windows/Linux targets; JavaFX 3D path removed.

Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
parents 5c2847ef bd40b396
/*-
* Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.LandObject;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class LandUseNode extends Renderable {
private final LandObject landUse;
public LandUseNode(LandObject landUse) {
this.landUse = landUse;
}
@Override
public void refreshTextColor() {
if (!landUse.isValidated()) {
setStatus(CheckStatus.NOT_CHECKED);
} else if (landUse.containsAnyError()) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
}
}
@Override
public String getText() {
return "[LandUse] " + landUse.getGmlId().getGmlString();
}
@Override
public void visit(Renderer renderer) {
renderer.render(landUse);
}
}
/*-
* Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class ReliefNode extends Renderable {
private final ReliefObject relief;
public ReliefNode(ReliefObject relief) {
this.relief = relief;
}
@Override
public void refreshTextColor() {
if (!relief.isValidated()) {
setStatus(CheckStatus.NOT_CHECKED);
} else if (relief.containsAnyError()) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
}
}
@Override
public String getText() {
return "[ReliefFeature] " + relief.getGmlId().getGmlString();
}
@Override
public void visit(Renderer renderer) {
renderer.render(relief);
}
}
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus; import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.MainWindow;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.input.Clipboard;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import static javafx.scene.input.Clipboard.getSystemClipboard;
public abstract class Renderable { public abstract class Renderable {
private final ObjectProperty<CheckStatus> statusProperty = new SimpleObjectProperty<>(); private final ObjectProperty<CheckStatus> statusProperty = new SimpleObjectProperty<>();
private static Logger logger = LogManager.getLogger(Renderable.class);
/** /**
* @return the status * @return the status
*/ */
...@@ -31,4 +40,18 @@ public abstract class Renderable { ...@@ -31,4 +40,18 @@ public abstract class Renderable {
public abstract String getText(); public abstract String getText();
public abstract void visit(Renderer renderer); public abstract void visit(Renderer renderer);
public void getClipboardAction(MainWindow mainWindow) {
Clipboard clipboard = getSystemClipboard();
clipboard.clear();
if (logger.isInfoEnabled()) {
logger.info(Localization.getText("MainWindow.infoTopLevelCopyID"));
}
}
public void getFeatureExportAction(MainWindow mainWindow) {
if (logger.isInfoEnabled()) {
logger.info(Localization.getText("MainWindow.infoTopLevelExport"));
}
}
} }
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractRoom;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class RoomNode extends Renderable{
private final AbstractRoom room;
public RoomNode(AbstractRoom room) {
this.room = room;
}
@Override
public String getText() {
return room.getGmlId().getGmlString();
}
@Override
public void visit(Renderer renderer) {
renderer.render(room);
}
@Override
public void refreshTextColor() {
if (!room.isValidated()) {
setStatus(CheckStatus.NOT_CHECKED);
} else if (room.containsAnyError()) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
}
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.Storey;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class StoreyNode extends Renderable{
private final Storey storey;
public StoreyNode(Storey storey) {
this.storey = storey;
}
@Override
public String getText() {
return storey.getGmlId().getGmlString();
}
@Override
public void visit(Renderer renderer) {
renderer.render(storey);
}
@Override
public void refreshTextColor() {
if (!storey.isValidated()) {
setStatus(CheckStatus.NOT_CHECKED);
} else if (storey.containsAnyError()) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
}
}
}
/*-
* Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.TinObject;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class TinNode extends Renderable {
private final TinObject tin;
public TinNode(TinObject tin) {
this.tin = tin;
}
@Override
public void refreshTextColor() {
if (!tin.isValidated()) {
setStatus(CheckStatus.NOT_CHECKED);
} else if (tin.containsAnyError()) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
}
}
@Override
public String getText() {
return "[TINRelief] " + tin.getGmlId().getGmlString();
}
@Override
public void visit(Renderer renderer) {
renderer.render(tin);
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.Tunnel;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class TunnelNode extends Renderable{
private final Tunnel tunnel;
public TunnelNode(Tunnel tunnel) {
this.tunnel = tunnel;
}
@Override
public String getText() {
return tunnel.getGmlId().getGmlString();
}
public Tunnel getTunnel() {
return tunnel;
}
@Override
public void visit(Renderer renderer) {
renderer.render(tunnel);
}
@Override
public void refreshTextColor() {
if (!tunnel.isValidated()) {
setStatus(CheckStatus.NOT_CHECKED);
} else if (tunnel.containsAnyError()) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
}
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.TunnelPart;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class TunnelPartNode extends Renderable{
private final TunnelPart tp;
public TunnelPartNode(TunnelPart tp) {
this.tp = tp;
}
@Override
public String getText() {
return tp.getGmlId().getGmlString();
}
@Override
public void visit(Renderer renderer) {
renderer.render(tp);
}
@Override
public void refreshTextColor() {
if (!tp.isValidated()) {
setStatus(CheckStatus.NOT_CHECKED);
} else if (tp.containsAnyError()) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
}
}
}
/*-
* Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.Vegetation;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class VegetationNode extends Renderable {
private final Vegetation veg;
private final String text;
public VegetationNode(Vegetation veg) {
this.veg = veg;
text = "[" + veg.getVegetationType() + "] " + veg.getGmlId().getGmlString();
}
@Override
public void refreshTextColor() {
if (!veg.isValidated()) {
setStatus(CheckStatus.NOT_CHECKED);
} else if (veg.containsAnyError()) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
}
}
@Override
public String getText() {
return text;
}
@Override
public void visit(Renderer renderer) {
renderer.render(veg);
}
}
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.datastructure.GenericCityObject; import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus; import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import java.util.List; import java.util.List;
public class AllOtherObjectsNode extends Renderable{ public class CityObjectGroupNode extends Renderable {
private final List<GenericCityObject> genericObjects; private final List<? extends CityObject> cityObjects;
private final Color renderColor;
private final String nodeText;
public AllOtherObjectsNode(List<GenericCityObject> genericObjects) { public CityObjectGroupNode(List<? extends CityObject> cityObjects, String nodeText, Color renderColor) {
this.genericObjects = genericObjects; this.cityObjects = cityObjects;
this.nodeText = nodeText;
this.renderColor = renderColor;
} }
@Override @Override
public void refreshTextColor() { public void refreshTextColor() {
boolean wasChecked = false; if (cityObjects.stream().anyMatch(cityObject -> !cityObject.isValidated())) {
for (GenericCityObject gco : genericObjects) {
if (gco.isValidated()) {
wasChecked = true;
if (gco.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED); setStatus(CheckStatus.NOT_CHECKED);
} else if (cityObjects.stream().anyMatch(Checkable::containsAnyError)) {
setStatus(CheckStatus.ERROR);
} else {
setStatus(CheckStatus.OK);
} }
} }
@Override @Override
public String getText() { public String getText() {
return "GenericCityObjects"; return nodeText;
} }
@Override @Override
public void visit(Renderer renderer) { public void visit(Renderer renderer) {
renderer.clearCurrentRender(); renderer.renderCityObjects(cityObjects, renderColor);
renderer.renderCityObjects(genericObjects, Color.GRAY);
} }
} }
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus; import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class CityObjectNode extends Renderable { public class CityObjectNode extends Renderable {
private static final Logger logger = LogManager.getLogger(CityObjectNode.class);
private final CityObject co; private final CityObject co;
private String text;
public CityObjectNode(CityObject co) { public CityObjectNode(CityObject co) {
this.co = co; this.co = co;
this.text = co.getDisplayText();
} }
@Override @Override
...@@ -25,7 +32,7 @@ public class CityObjectNode extends Renderable { ...@@ -25,7 +32,7 @@ public class CityObjectNode extends Renderable {
@Override @Override
public String getText() { public String getText() {
return co.getGmlId().getGmlString(); return text;
} }
@Override @Override
...@@ -33,4 +40,8 @@ public class CityObjectNode extends Renderable { ...@@ -33,4 +40,8 @@ public class CityObjectNode extends Renderable {
renderer.render(co); renderer.render(co);
} }
public CityObject getFeature() {
return co;
}
} }
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
...@@ -6,6 +6,7 @@ import java.text.DecimalFormatSymbols; ...@@ -6,6 +6,7 @@ import java.text.DecimalFormatSymbols;
import de.hft.stuttgart.citydoctor2.datastructure.Edge; import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class EdgeNode extends Renderable { public class EdgeNode extends Renderable {
......
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class ErrorNode extends Renderable { public class ErrorNode extends Renderable {
......
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.datastructure.GenericAttribute; import de.hft.stuttgart.citydoctor2.datastructure.GenericAttribute;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class GenericAttributeNode extends Renderable { public class GenericAttributeNode extends Renderable {
......
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus; import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class GeometryNode extends Renderable { public class GeometryNode extends Renderable {
......
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus; import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class LinearRingNode extends Renderable { public class LinearRingNode extends Renderable {
......
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.datastructure.Opening; import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus; import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class OpeningNode extends Renderable { public class OpeningNode extends Renderable {
......
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus; import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class PolygonNode extends Renderable { public class PolygonNode extends Renderable {
......
package de.hft.stuttgart.citydoctor2.gui.tree; package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.gui.Renderer; import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class TextNode extends Renderable { public class TextNode extends Renderable {
......
package de.hft.stuttgart.citydoctor2.gui.tree.node;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.gui.MainWindow;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import static javafx.scene.input.Clipboard.getSystemClipboard;
public class TopLevelCityObjectNode extends CityObjectNode {
private static final Logger logger = LogManager.getLogger(TopLevelCityObjectNode.class);
public TopLevelCityObjectNode(CityObject co) {
super(co);
}
@Override
public void getClipboardAction(MainWindow mainWindow) {
Clipboard clipboard = getSystemClipboard();
ClipboardContent content = new ClipboardContent();
content.putString(this.getFeature().getGmlId().toString());
if (!clipboard.setContent(content)) {
if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("MainWindow.warnCopyIdFailed"));
}
}
}
@Override
public void getFeatureExportAction(MainWindow mainWindow) {
try {
mainWindow.getController().export(this.getFeature());
if (logger.isWarnEnabled()) {
logger.info(Localization.getText("CityDoctorController.exportSuccess"));
}
} catch (Exception e) {
logger.error("Export failed: Unexpected error occurred");
logger.error(e);
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment