Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CityDoctor
CityDoctor2
Commits
854aab9b
Commit
854aab9b
authored
Sep 10, 2026
by
Matthias Betz
Browse files
Merge remote-tracking branch 'remotes/origin/dev' into 120-portierung-sweep-algorithmen
parents
760147a4
cc4fe260
Pipeline
#12442
passed with stage
in 2 minutes and 26 seconds
Changes
56
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CHANGELOG.md
View file @
854aab9b
...
...
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on
[
Keep a Changelog
](
https://keepachangelog.com/en/1.1.0/
)
,
and this project adheres to
[
Semantic Versioning
](
https://semver.org/spec/v2.0.0.html
)
.
## [Unreleased]
### Changed
-
Replaced the JavaFX-based 3D view with an OpenGL renderer (openglfx/JOGL) for smooth display of
large city models. Picking is now GPU-based; the 3D view requires OpenGL 3.3 (Windows/Linux).
## [3.18.3] (2026-04-16)
### Fixes
-
Fixed FragmentedSurface errors reporting roof BoundarySurfaces as ground BoundarySurfaces
-
Fixed degenerated triangles causing false-positive detection of AllPolygonsWrongOrientation errors
-
Fixed -db_location and -db_settings arguments not being parsed when starting the GUI from the CLI
## [3.18.2] (2026-02-24)
### Added
...
...
CityDoctorParent/CityDoctorCheckResult/pom.xml
View file @
854aab9b
...
...
@@ -6,7 +6,7 @@
<parent>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
<version>
3.18.
2
</version>
<version>
3.18.
3
</version>
</parent>
<artifactId>
CityDoctorCheckResult
</artifactId>
<dependencies>
...
...
CityDoctorParent/CityDoctorEdge/pom.xml
View file @
854aab9b
...
...
@@ -6,7 +6,7 @@
<parent>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
<version>
3.18.
2
</version>
<version>
3.18.
3
</version>
</parent>
<artifactId>
CityDoctorEdge
</artifactId>
<dependencies>
...
...
CityDoctorParent/CityDoctorModel/pom.xml
View file @
854aab9b
...
...
@@ -6,7 +6,7 @@
<parent>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
<version>
3.18.
2
</version>
<version>
3.18.
3
</version>
</parent>
<properties>
<versionString>
${project.version}-${git.commit.id.abbrev}
</versionString>
...
...
CityDoctorParent/CityDoctorValidation/pom.xml
View file @
854aab9b
...
...
@@ -4,7 +4,7 @@
<parent>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
<version>
3.18.
2
</version>
<version>
3.18.
3
</version>
</parent>
<artifactId>
CityDoctorValidation
</artifactId>
<name>
CityDoctorValidation
</name>
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java
View file @
854aab9b
...
...
@@ -96,7 +96,7 @@ public class CityDoctorValidation {
return
outputFile
;
}
p
rivate
static
void
lookForDbConfigFileParameter
(
ArgumentParser
argParser
)
{
p
ublic
static
void
lookForDbConfigFileParameter
(
ArgumentParser
argParser
)
{
if
(!
argParser
.
containsOption
(
"db_config"
))
{
return
;
}
...
...
@@ -108,7 +108,7 @@ public class CityDoctorValidation {
}
}
p
rivate
static
void
lookForDbLocationParameter
(
ArgumentParser
argParser
)
{
p
ublic
static
void
lookForDbLocationParameter
(
ArgumentParser
argParser
)
{
if
(!
argParser
.
containsOption
(
"db_location"
))
{
return
;
}
...
...
CityDoctorParent/Extensions/CityDoctorGUI/pom.xml
View file @
854aab9b
...
...
@@ -6,7 +6,7 @@
<parent>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
<version>
3.18.
2
</version>
<version>
3.18.
3
</version>
<relativePath>
../../pom.xml
</relativePath>
</parent>
<artifactId>
CityDoctorGUI
</artifactId>
...
...
@@ -59,6 +59,24 @@
<groupId>
org.openjfx
</groupId>
<artifactId>
javafx-swing
</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.huskerdev/openglfx-jogl -->
<dependency>
<groupId>
com.huskerdev
</groupId>
<artifactId>
openglfx-jogl
</artifactId>
<version>
4.1.19
</version>
</dependency>
<!-- JOGL runtime + platform natives. openglfx-jogl does not declare JOGL itself;
the GUI now programs directly against com.jogamp.opengl, so depend on it explicitly.
Versions are managed in the parent pom. The -main artifacts bundle the natives. -->
<dependency>
<groupId>
org.jogamp.jogl
</groupId>
<artifactId>
jogl-all-main
</artifactId>
</dependency>
<dependency>
<groupId>
org.jogamp.gluegen
</groupId>
<artifactId>
gluegen-rt-main
</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
...
...
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java
View file @
854aab9b
...
...
@@ -130,7 +130,7 @@ public class CityDoctorController {
mainWindow
.
getOpenBtn
().
setDisable
(
true
);
mainWindow
.
getWriteReportButton
().
setDisable
(
true
);
mainWindow
.
getMeshGroup
().
getChildren
().
clear
();
mainWindow
.
clearMeshView
();
clearTrees
();
mainWindow
.
resetSearchBar
();
...
...
@@ -1660,7 +1660,7 @@ public class CityDoctorController {
}
public
void
errorFilterIndexChanged
(
Number
newV
)
{
mainWindow
.
getMeshGroup
().
getChildren
().
clear
();
mainWindow
.
clearMeshView
();
mainWindow
.
unselectEverything
();
mainWindow
.
resetSearchBar
();
renderer
.
clearCurrentRender
();
...
...
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/HighlightController.java
View file @
854aab9b
package
de.hft.stuttgart.citydoctor2.gui
;
import
java.util.List
;
import
java.util.function.Supplier
;
import
de.hft.stuttgart.citydoctor2.datastructure.Edge
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType
;
import
de.hft.stuttgart.citydoctor2.gui.gl.FloatList
;
import
de.hft.stuttgart.citydoctor2.gui.gl.GLViewport
;
import
de.hft.stuttgart.citydoctor2.math.Triangle3d
;
import
de.hft.stuttgart.citydoctor2.math.Vector3d
;
import
javafx.application.Platform
;
import
javafx.geometry.Point3D
;
import
javafx.scene.Group
;
import
javafx.scene.Node
;
import
javafx.scene.paint.Color
;
import
javafx.scene.paint.PhongMaterial
;
import
javafx.scene.shape.Cylinder
;
import
javafx.scene.shape.Sphere
;
import
javafx.scene.transform.Rotate
;
import
javafx.scene.transform.Translate
;
/**
* Accumulates highlight marker geometry (points and lines) and pushes it to the {@link GLViewport}
* overlay. Coordinates are recentred by the current scene center supplied by the {@link Renderer}.
*/
public
class
HighlightController
{
private
double
scale
;
private
Group
points
;
private
Group
edges
;
private
Group
highlights
;
public
HighlightController
(
Group
world
)
{
highlights
=
new
Group
();
points
=
new
Group
();
edges
=
new
Group
();
highlights
.
getChildren
().
add
(
edges
);
highlights
.
getChildren
().
add
(
points
);
Platform
.
runLater
(()
->
world
.
getChildren
().
add
(
highlights
));
}
public
void
clearHighlights
()
{
points
.
getChildren
().
clear
();
edges
.
getChildren
().
clear
();
}
public
void
changeScaling
(
double
translateZ
)
{
scale
=
Math
.
abs
(
translateZ
);
scale
=
Math
.
min
(
150
,
scale
);
scale
=
Math
.
max
(
10
,
scale
);
scale
=
scale
*
0.01
;
for
(
Node
n
:
points
.
getChildren
())
{
n
.
setScaleX
(
scale
);
n
.
setScaleY
(
scale
);
n
.
setScaleZ
(
scale
);
}
double
edgeSize
=
scale
/
10
;
for
(
Node
n
:
edges
.
getChildren
())
{
if
(
n
instanceof
Cylinder
cy
)
{
cy
.
setRadius
(
edgeSize
);
}
}
}
public
void
highlight
(
Polygon
p
,
TriangulatedGeometry
currentTriGeom
)
{
clearHighlights
();
addHighlight
(
p
,
currentTriGeom
,
Color
.
RED
,
Color
.
BLUE
);
}
private
void
addHighlight
(
Polygon
p
,
TriangulatedGeometry
currentTriGeom
,
Color
extColor
,
Color
intColor
)
{
if
(
currentTriGeom
==
null
||
p
==
null
)
{
return
;
}
addHighlight
(
p
.
getExteriorRing
(),
currentTriGeom
,
extColor
);
for
(
LinearRing
intRing
:
p
.
getInnerRings
())
{
addHighlight
(
intRing
,
currentTriGeom
,
intColor
);
}
}
public
void
highlight
(
LinearRing
ring
,
TriangulatedGeometry
currentTriGeom
)
{
clearHighlights
();
if
(
ring
.
getType
()
==
LinearRingType
.
EXTERIOR
)
{
addHighlight
(
ring
,
currentTriGeom
,
Color
.
RED
);
}
else
{
addHighlight
(
ring
,
currentTriGeom
,
Color
.
BLUE
);
}
}
public
void
highlight
(
List
<
LinearRing
>
rings
,
TriangulatedGeometry
currentTriGeom
)
{
clearHighlights
();
for
(
LinearRing
lr
:
rings
)
{
if
(
lr
.
getType
()
==
LinearRingType
.
EXTERIOR
)
{
addHighlight
(
lr
,
currentTriGeom
,
Color
.
RED
);
}
else
{
addHighlight
(
lr
,
currentTriGeom
,
Color
.
BLUE
);
}
}
}
private
void
addHighlight
(
LinearRing
ring
,
TriangulatedGeometry
currentTriGeom
,
Color
pointColor
)
{
if
(
currentTriGeom
==
null
||
ring
==
null
)
{
return
;
}
Vector3d
movedBy
=
currentTriGeom
.
getMovedBy
();
for
(
Vertex
v
:
ring
.
getVertices
())
{
highlightPoint
(
movedBy
,
v
,
pointColor
);
}
for
(
int
i
=
0
;
i
<
ring
.
getVertices
().
size
()
-
1
;
i
++)
{
Vertex
v1
=
ring
.
getVertices
().
get
(
i
);
Vertex
v2
=
ring
.
getVertices
().
get
(
i
+
1
);
highlightEdge
(
v1
,
v2
,
movedBy
);
}
}
public
void
highlight
(
Edge
e
,
TriangulatedGeometry
currentTriGeom
)
{
clearHighlights
();
addHighlight
(
e
,
currentTriGeom
);
}
private
void
addHighlight
(
Edge
e
,
TriangulatedGeometry
currentTriGeom
)
{
if
(
currentTriGeom
==
null
||
e
==
null
)
{
return
;
}
Vector3d
movedBy
=
currentTriGeom
.
getMovedBy
();
highlightEdge
(
e
.
getFrom
(),
e
.
getTo
(),
movedBy
);
highlightPoint
(
movedBy
,
e
.
getFrom
(),
Color
.
RED
);
highlightPoint
(
movedBy
,
e
.
getTo
(),
Color
.
RED
);
}
public
void
highlight
(
Vertex
v
,
TriangulatedGeometry
currentTriGeom
)
{
clearHighlights
();
addHighlight
(
v
,
currentTriGeom
,
Color
.
RED
);
}
public
void
addHighlight
(
Vertex
v
,
TriangulatedGeometry
currentTriGeom
,
Color
c
)
{
if
(
currentTriGeom
==
null
||
v
==
null
)
{
return
;
}
Vector3d
movedBy
=
currentTriGeom
.
getMovedBy
();
highlightPoint
(
movedBy
,
v
,
c
);
}
private
void
highlightPoint
(
Vector3d
movedBy
,
Vertex
v
,
Color
color
)
{
Sphere
sp
=
new
Sphere
(
0.5
);
sp
.
setMaterial
(
new
PhongMaterial
(
color
));
sp
.
setTranslateX
(
v
.
getX
()
-
movedBy
.
getX
());
sp
.
setTranslateY
(
v
.
getY
()
-
movedBy
.
getY
());
sp
.
setTranslateZ
(
v
.
getZ
()
-
movedBy
.
getZ
());
sp
.
setScaleX
(
scale
);
sp
.
setScaleY
(
scale
);
sp
.
setScaleZ
(
scale
);
sp
.
setUserData
(
new
VertexClickDispatcher
(
v
));
points
.
getChildren
().
add
(
sp
);
}
private
void
highlightEdge
(
Vector3d
v1
,
Vector3d
v2
,
Vector3d
movedBy
)
{
Point3D
origin
=
new
Point3D
(
v1
.
getX
()
-
movedBy
.
getX
(),
v1
.
getY
()
-
movedBy
.
getY
(),
v1
.
getZ
()
-
movedBy
.
getZ
());
Point3D
target
=
new
Point3D
(
v2
.
getX
()
-
movedBy
.
getX
(),
v2
.
getY
()
-
movedBy
.
getY
(),
v2
.
getZ
()
-
movedBy
.
getZ
());
Point3D
yAxis
=
new
Point3D
(
0
,
1
,
0
);
Point3D
diff
=
target
.
subtract
(
origin
);
double
height
=
diff
.
magnitude
();
Point3D
mid
=
target
.
midpoint
(
origin
);
Translate
moveToMidpoint
=
new
Translate
(
mid
.
getX
(),
mid
.
getY
(),
mid
.
getZ
());
Point3D
axisOfRotation
=
diff
.
crossProduct
(
yAxis
);
double
angle
=
Math
.
acos
(
diff
.
normalize
().
dotProduct
(
yAxis
));
Rotate
rotateAroundCenter
=
new
Rotate
(-
Math
.
toDegrees
(
angle
),
axisOfRotation
);
Cylinder
cy
=
new
Cylinder
(
scale
/
10
,
height
);
cy
.
setMaterial
(
new
PhongMaterial
(
Color
.
ORANGE
));
cy
.
getTransforms
().
addAll
(
moveToMidpoint
,
rotateAroundCenter
);
edges
.
getChildren
().
add
(
cy
);
}
public
void
highlightEdges
(
List
<
Edge
>
errorEdges
,
TriangulatedGeometry
currentTriGeom
)
{
clearHighlights
();
for
(
Edge
e
:
errorEdges
)
{
addHighlight
(
e
,
currentTriGeom
);
}
}
public
void
highlightPolygons
(
List
<
List
<
Polygon
>>
components
,
TriangulatedGeometry
currentTriGeom
)
{
clearHighlights
();
for
(
int
i
=
0
;
i
<
components
.
size
();
i
++)
{
Color
extColor
;
Color
intColor
=
switch
(
i
%
3
)
{
private
final
GLViewport
viewport
;
private
Supplier
<
double
[]>
centerSupplier
;
private
final
FloatList
points
=
new
FloatList
();
private
final
FloatList
pointColors
=
new
FloatList
();
private
final
FloatList
lines
=
new
FloatList
();
private
final
FloatList
lineColors
=
new
FloatList
();
public
HighlightController
(
GLViewport
viewport
)
{
this
.
viewport
=
viewport
;
}
public
void
setCenterSupplier
(
Supplier
<
double
[]>
centerSupplier
)
{
this
.
centerSupplier
=
centerSupplier
;
}
private
double
[]
center
()
{
if
(
centerSupplier
==
null
)
{
return
new
double
[
3
];
}
double
[]
c
=
centerSupplier
.
get
();
return
c
==
null
?
new
double
[
3
]
:
c
;
}
private
void
resetBuffers
()
{
points
.
clear
();
pointColors
.
clear
();
lines
.
clear
();
lineColors
.
clear
();
}
public
void
clearHighlights
()
{
resetBuffers
();
push
();
}
private
void
push
()
{
viewport
.
setHighlights
(
points
.
toArray
(),
pointColors
.
toArray
(),
lines
.
toArray
(),
lineColors
.
toArray
());
}
public
void
changeScaling
(
double
translateZ
)
{
// GL points/lines are sized in screen pixels, so highlight markers keep a constant on-screen
// size regardless of zoom distance; nothing to recompute here.
}
// ---- point/line accumulation ----
private
void
addPoint
(
Vertex
v
,
Color
color
)
{
double
[]
c
=
center
();
points
.
add
((
float
)
(
v
.
getX
()
-
c
[
0
]),
(
float
)
(
v
.
getY
()
-
c
[
1
]),
(
float
)
(
v
.
getZ
()
-
c
[
2
]));
pointColors
.
add
((
float
)
color
.
getRed
(),
(
float
)
color
.
getGreen
(),
(
float
)
color
.
getBlue
());
}
private
void
addLine
(
Vector3d
v1
,
Vector3d
v2
,
Color
color
)
{
double
[]
c
=
center
();
lines
.
add
((
float
)
(
v1
.
getX
()
-
c
[
0
]),
(
float
)
(
v1
.
getY
()
-
c
[
1
]),
(
float
)
(
v1
.
getZ
()
-
c
[
2
]));
lines
.
add
((
float
)
(
v2
.
getX
()
-
c
[
0
]),
(
float
)
(
v2
.
getY
()
-
c
[
1
]),
(
float
)
(
v2
.
getZ
()
-
c
[
2
]));
float
r
=
(
float
)
color
.
getRed
();
float
g
=
(
float
)
color
.
getGreen
();
float
b
=
(
float
)
color
.
getBlue
();
lineColors
.
add
(
r
,
g
,
b
);
lineColors
.
add
(
r
,
g
,
b
);
}
// ---- public highlight API ----
public
void
highlight
(
Polygon
p
)
{
resetBuffers
();
addHighlight
(
p
,
Color
.
RED
,
Color
.
BLUE
);
push
();
}
private
void
addHighlight
(
Polygon
p
,
Color
extColor
,
Color
intColor
)
{
if
(
p
==
null
)
{
return
;
}
addHighlight
(
p
.
getExteriorRing
(),
extColor
);
for
(
LinearRing
intRing
:
p
.
getInnerRings
())
{
addHighlight
(
intRing
,
intColor
);
}
}
public
void
highlight
(
LinearRing
ring
)
{
resetBuffers
();
if
(
ring
.
getType
()
==
LinearRingType
.
EXTERIOR
)
{
addHighlight
(
ring
,
Color
.
RED
);
}
else
{
addHighlight
(
ring
,
Color
.
BLUE
);
}
push
();
}
public
void
highlight
(
List
<
LinearRing
>
rings
)
{
resetBuffers
();
for
(
LinearRing
lr
:
rings
)
{
if
(
lr
.
getType
()
==
LinearRingType
.
EXTERIOR
)
{
addHighlight
(
lr
,
Color
.
RED
);
}
else
{
addHighlight
(
lr
,
Color
.
BLUE
);
}
}
push
();
}
private
void
addHighlight
(
LinearRing
ring
,
Color
pointColor
)
{
if
(
ring
==
null
)
{
return
;
}
for
(
Vertex
v
:
ring
.
getVertices
())
{
addPoint
(
v
,
pointColor
);
}
for
(
int
i
=
0
;
i
<
ring
.
getVertices
().
size
()
-
1
;
i
++)
{
Vertex
v1
=
ring
.
getVertices
().
get
(
i
);
Vertex
v2
=
ring
.
getVertices
().
get
(
i
+
1
);
addLine
(
v1
,
v2
,
Color
.
ORANGE
);
}
}
public
void
highlight
(
Edge
e
)
{
resetBuffers
();
addHighlight
(
e
);
push
();
}
private
void
addHighlight
(
Edge
e
)
{
if
(
e
==
null
)
{
return
;
}
addLine
(
e
.
getFrom
(),
e
.
getTo
(),
Color
.
ORANGE
);
addPoint
(
e
.
getFrom
(),
Color
.
RED
);
addPoint
(
e
.
getTo
(),
Color
.
RED
);
}
public
void
highlight
(
Vertex
v
)
{
resetBuffers
();
addPoint
(
v
,
Color
.
RED
);
push
();
}
public
void
addHighlight
(
Vertex
v
,
Color
c
)
{
if
(
v
==
null
)
{
return
;
}
addPoint
(
v
,
c
);
push
();
}
public
void
highlightEdges
(
List
<
Edge
>
errorEdges
)
{
resetBuffers
();
for
(
Edge
e
:
errorEdges
)
{
addHighlight
(
e
);
}
push
();
}
public
void
highlightPolygons
(
List
<
List
<
Polygon
>>
components
)
{
resetBuffers
();
for
(
int
i
=
0
;
i
<
components
.
size
();
i
++)
{
Color
extColor
;
Color
intColor
=
switch
(
i
%
3
)
{
case
1
->
{
extColor
=
Color
.
GREEN
;
yield
Color
.
YELLOW
;
...
...
@@ -202,21 +193,24 @@ public class HighlightController {
yield
Color
.
BLUE
;
}
};
// select some color pairs for exterior and inner rings
// select some color pairs for exterior and inner rings
List
<
Polygon
>
component
=
components
.
get
(
i
);
for
(
Polygon
p
:
component
)
{
addHighlight
(
p
,
currentTriGeom
,
extColor
,
intColor
);
}
}
}
public
void
addHighlight
(
Polygon
p
,
TriangulatedGeometry
currentTriGeom
)
{
addHighlight
(
p
,
currentTriGeom
,
Color
.
RED
,
Color
.
BLUE
);
}
public
void
highlight
(
Triangle3d
t
,
TriangulatedGeometry
currentTriGeom
)
{
highlightEdge
(
t
.
getP1
(),
t
.
getP2
(),
currentTriGeom
.
getMovedBy
());
highlightEdge
(
t
.
getP2
(),
t
.
getP3
(),
currentTriGeom
.
getMovedBy
());
highlightEdge
(
t
.
getP3
(),
t
.
getP1
(),
currentTriGeom
.
getMovedBy
());
}
for
(
Polygon
p
:
component
)
{
addHighlight
(
p
,
extColor
,
intColor
);
}
}
push
();
}
public
void
addHighlight
(
Polygon
p
)
{
addHighlight
(
p
,
Color
.
RED
,
Color
.
BLUE
);
push
();
}
public
void
highlight
(
Triangle3d
t
)
{
addLine
(
t
.
getP1
(),
t
.
getP2
(),
Color
.
ORANGE
);
addLine
(
t
.
getP2
(),
t
.
getP3
(),
Color
.
ORANGE
);
addLine
(
t
.
getP3
(),
t
.
getP1
(),
Color
.
ORANGE
);
push
();
}
}
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ListErrorVisitor.java
View file @
854aab9b
...
...
@@ -55,32 +55,27 @@ import javafx.scene.paint.Color;
public
class
ListErrorVisitor
implements
ErrorVisitor
{
private
HighlightController
controller
;
private
TriangulatedGeometry
geom
;
public
ListErrorVisitor
(
HighlightController
controller
)
{
this
.
controller
=
controller
;
}
public
void
setGeometry
(
TriangulatedGeometry
geom
)
{
this
.
geom
=
geom
;
}
@Override
public
void
visit
(
PolygonHoleOutsideError
err
)
{
List
<
LinearRing
>
highlightedRings
=
new
ArrayList
<>();
highlightedRings
.
add
(
err
.
getPolygon
().
getExteriorRing
());
highlightedRings
.
addAll
(
err
.
getHolesOutside
());
controller
.
highlight
(
highlightedRings
,
geom
);
controller
.
highlight
(
highlightedRings
);
}
@Override
public
void
visit
(
NonManifoldEdgeError
err
)
{
controller
.
highlightEdges
(
err
.
getEdges
()
,
geom
);
controller
.
highlightEdges
(
err
.
getEdges
());
}
@Override
public
void
visit
(
MultipleConnectedComponentsError
err
)
{
controller
.
highlightPolygons
(
err
.
getComponents
()
,
geom
);
controller
.
highlightPolygons
(
err
.
getComponents
());
}
@Override
...
...
@@ -88,18 +83,18 @@ public class ListErrorVisitor implements ErrorVisitor {
List
<
LinearRing
>
highlightedRings
=
new
ArrayList
<>();
highlightedRings
.
add
(
err
.
getPolygon
().
getExteriorRing
());
highlightedRings
.
add
(
err
.
getInnerRing
());
controller
.
highlight
(
highlightedRings
,
geom
);
controller
.
highlight
(
highlightedRings
);
}
@Override
public
void
visit
(
NonManifoldVertexError
err
)
{
controller
.
highlightPolygons
(
err
.
getComponents
()
,
geom
);
controller
.
addHighlight
(
err
.
getVertex
(),
geom
,
Color
.
BLUEVIOLET
);
controller
.
highlightPolygons
(
err
.
getComponents
());
controller
.
addHighlight
(
err
.
getVertex
(),
Color
.
BLUEVIOLET
);
}
@Override
public
void
visit
(
PolygonWrongOrientationError
err
)
{
controller
.
highlightEdges
(
err
.
getEdges
()
,
geom
);
controller
.
highlightEdges
(
err
.
getEdges
());
}
@Override
...
...
@@ -107,12 +102,12 @@ public class ListErrorVisitor implements ErrorVisitor {
List
<
LinearRing
>
highlightedRings
=
new
ArrayList
<>();
highlightedRings
.
add
(
err
.
getPolygon
().
getExteriorRing
());
highlightedRings
.
add
(
err
.
getInnerRing
());
controller
.
highlight
(
highlightedRings
,
geom
);
controller
.
highlight
(
highlightedRings
);
}
@Override
public
void
visit
(
SolidNotClosedError
err
)
{
controller
.
highlightEdges
(
err
.
getErrorEdges
()
,
geom
);
controller
.
highlightEdges
(
err
.
getErrorEdges
());
}
@Override
...
...
@@ -127,14 +122,14 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
RingNotClosedError
err
)
{
controller
.
highlight
(
err
.
getRing
()
,
geom
);
controller
.
highlight
(
err
.
getRing
());
}
@Override
public
void
visit
(
ConsecutivePointSameError
err
)
{
controller
.
highlight
(
err
.
getRing
()
,
geom
);
controller
.
addHighlight
(
err
.
getVertex1
(),
geom
,
Color
.
BLACK
);
controller
.
addHighlight
(
err
.
getVertex2
(),
geom
,
Color
.
BLACK
);
controller
.
highlight
(
err
.
getRing
());
controller
.
addHighlight
(
err
.
getVertex1
(),
Color
.
BLACK
);
controller
.
addHighlight
(
err
.
getVertex2
(),
Color
.
BLACK
);
}
@Override
...
...
@@ -144,7 +139,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
PolygonInteriorDisconnectedError
err
)
{
controller
.
highlight
(
err
.
getConnectedRings
()
,
geom
);
controller
.
highlight
(
err
.
getConnectedRings
());
}
@Override
...
...
@@ -154,18 +149,18 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
RingTooFewPointsError
err
)
{
controller
.
highlight
(
err
.
getRing
()
,
geom
);
controller
.
highlight
(
err
.
getRing
());
}
@Override
public
void
visit
(
NonPlanarPolygonNormalsDeviation
err
)
{
controller
.
highlight
(
err
.
getPolygon
()
,
geom
);
controller
.
highlight
(
err
.
getPolygon
());
}
@Override
public
void
visit
(
NonPlanarPolygonDistancePlaneError
err
)
{
controller
.
highlight
(
err
.
getPolygon
()
,
geom
);
controller
.
addHighlight
(
err
.
getVertex
(),
geom
,
Color
.
BLACK
);
controller
.
highlight
(
err
.
getPolygon
());
controller
.
addHighlight
(
err
.
getVertex
(),
Color
.
BLACK
);
}
@Override
...
...
@@ -173,15 +168,15 @@ public class ListErrorVisitor implements ErrorVisitor {
List
<
LinearRing
>
rings
=
new
ArrayList
<>();
rings
.
add
(
err
.
getIntersectingRings
().
getValue0
());
rings
.
add
(
err
.
getIntersectingRings
().
getValue1
());
controller
.
highlight
(
rings
,
geom
);
controller
.
highlight
(
rings
);
}
@Override
public
void
visit
(
SolidSelfIntError
err
)
{
controller
.
clearHighlights
();
for
(
PolygonIntersection
intersection
:
err
.
getIntersections
())
{
controller
.
addHighlight
(
intersection
.
getP1
()
,
geom
);
controller
.
addHighlight
(
intersection
.
getP2
()
,
geom
);
controller
.
addHighlight
(
intersection
.
getP1
());
controller
.
addHighlight
(
intersection
.
getP2
());
}
}
...
...
@@ -192,9 +187,9 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
RingDuplicatePointError
err
)
{
controller
.
highlight
(
err
.
getRing
()
,
geom
);
controller
.
addHighlight
(
err
.
getVertex1
(),
geom
,
Color
.
BLACK
);
controller
.
addHighlight
(
err
.
getVertex2
(),
geom
,
Color
.
BLACK
);
controller
.
highlight
(
err
.
getRing
());
controller
.
addHighlight
(
err
.
getVertex1
(),
Color
.
BLACK
);
controller
.
addHighlight
(
err
.
getVertex2
(),
Color
.
BLACK
);
}
@Override
...
...
@@ -202,14 +197,14 @@ public class ListErrorVisitor implements ErrorVisitor {
List
<
Edge
>
list
=
new
ArrayList
<>();
list
.
add
(
err
.
getEdge1
());
list
.
add
(
err
.
getEdge2
());
controller
.
highlightEdges
(
list
,
geom
);
controller
.
addHighlight
(
new
Vertex
(
err
.
getIntersection
()),
geom
,
Color
.
BLACK
);
controller
.
highlightEdges
(
list
);
controller
.
addHighlight
(
new
Vertex
(
err
.
getIntersection
()),
Color
.
BLACK
);
}
@Override
public
void
visit
(
PointTouchesEdgeError
err
)
{
controller
.
highlight
(
err
.
getEdge
()
,
geom
);
controller
.
addHighlight
(
err
.
getVertex
(),
geom
,
Color
.
BLACK
);
controller
.
highlight
(
err
.
getEdge
());
controller
.
addHighlight
(
err
.
getVertex
(),
Color
.
BLACK
);
}
@Override
...
...
@@ -219,22 +214,22 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
NotCeilingError
err
)
{
controller
.
highlight
(
err
.
getPolygon
()
,
geom
);
controller
.
highlight
(
err
.
getPolygon
());
}
@Override
public
void
visit
(
NotFloorError
err
)
{
controller
.
highlight
(
err
.
getPolygon
()
,
geom
);
controller
.
highlight
(
err
.
getPolygon
());
}
@Override
public
void
visit
(
NotWallError
err
)
{
controller
.
highlight
(
err
.
getPolygon
()
,
geom
);
controller
.
highlight
(
err
.
getPolygon
());
}
@Override
public
void
visit
(
NotGroundError
err
)
{
controller
.
highlight
(
err
.
getPolygon
()
,
geom
);
controller
.
highlight
(
err
.
getPolygon
());
}
@Override
...
...
@@ -249,7 +244,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
DegeneratedRingError
err
)
{
controller
.
highlight
(
err
.
getRing
()
,
geom
);
controller
.
highlight
(
err
.
getRing
());
}
@Override
...
...
@@ -269,7 +264,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
PolygonWithoutSurfaceError
err
)
{
controller
.
highlight
(
err
.
getPolygon
()
,
geom
);
controller
.
highlight
(
err
.
getPolygon
());
}
@Override
...
...
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java
View file @
854aab9b
...
...
@@ -31,9 +31,11 @@ import javafx.scene.layout.BorderPane;
import
javafx.scene.layout.HBox
;
import
javafx.scene.layout.Pane
;
import
javafx.scene.layout.Priority
;
import
javafx.scene.layout.Region
;
import
javafx.scene.paint.Color
;
import
javafx.scene.transform.Rotate
;
import
javafx.stage.Stage
;
import
de.hft.stuttgart.citydoctor2.gui.gl.GLViewport
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
...
...
@@ -49,10 +51,6 @@ public class MainWindow extends Application {
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
MainWindow
.
class
);
private
static
final
double
CAMERA_TRANSLATE_Z
=
-
100.0
;
private
static
final
double
CAMERA_INITIAL_X_ANGLE
=
20.0
;
private
static
final
double
CAMERA_INITIAL_Y_ANGLE
=
120.0
;
@FXML
private
TreeView
<
Renderable
>
buildingsView
;
...
...
@@ -214,19 +212,8 @@ public class MainWindow extends Application {
private
Image
north
;
private
Image
northFlip
;
private
Group
meshGroup
;
private
Group
world
;
private
PerspectiveCamera
camera
;
private
Rotate
cameraXRotation
=
new
Rotate
();
private
Rotate
cameraZRotation
=
new
Rotate
();
private
GLViewport
glViewport
;
private
double
dragX
;
private
double
dragY
;
private
double
cameraXRot
=
CAMERA_INITIAL_X_ANGLE
;
private
double
cameraYRot
=
CAMERA_INITIAL_Y_ANGLE
;
private
double
translateZ
=
CAMERA_TRANSLATE_Z
;
private
double
[]
clickStart
=
new
double
[
2
];
@FXML
...
...
@@ -245,7 +232,6 @@ public class MainWindow extends Application {
private
FeatureType
selectedTab
=
FeatureType
.
BUILDING
;
private
MainToolBar
mainToolBar
;
private
SubScene
geomScene
;
private
ImageView
northArrow
;
private
static
boolean
loadFileAtStartup
=
false
;
...
...
@@ -259,6 +245,11 @@ public class MainWindow extends Application {
private
ChangeListener
<
Number
>
filterChangeListener
;
public
static
void
main
(
String
[]
args
)
{
// The embedded OpenGL canvas (openglfx) breaks JavaFX's partial-repaint accounting, leaving
// sibling controls (e.g. the log panel) with a stale background. Disabling Prism's dirty-region
// optimization forces a full scene repaint each pulse, which fixes the artifacts. Must be set
// before the JavaFX toolkit initializes.
System
.
setProperty
(
"prism.dirtyopts"
,
"false"
);
setLocaleFromSettings
();
ArgumentParser
argParser
=
new
ArgumentParser
(
args
);
inputFile
=
CityDoctorValidation
.
getInputFile
(
argParser
,
true
);
...
...
@@ -278,6 +269,8 @@ public class MainWindow extends Application {
System
.
exit
(
4
);
}
}
CityDoctorValidation
.
lookForDbConfigFileParameter
(
argParser
);
CityDoctorValidation
.
lookForDbLocationParameter
(
argParser
);
Application
.
launch
(
args
);
}
...
...
@@ -300,7 +293,7 @@ public class MainWindow extends Application {
FXMLLoader
loader
=
new
FXMLLoader
(
MainWindow
.
class
.
getResource
(
"MainWindow.fxml"
));
loader
.
setController
(
this
);
BorderPane
bp
=
loader
.
load
();
highlightController
=
new
HighlightController
(
world
);
highlightController
=
new
HighlightController
(
glViewport
);
renderer
=
new
Renderer
(
this
,
highlightController
);
clickHandler
=
new
VertexClickHandler
(
errorView
,
renderer
,
stage
,
this
);
controller
=
new
CityDoctorController
(
this
,
highlightController
,
renderer
);
...
...
@@ -818,8 +811,11 @@ public class MainWindow extends Application {
MenuItem deleteOption = new MenuItem(Localization.getText("MainWindow.delete"));
deleteOption.setOnAction(ae -> controller.delete(featureView.getSelectionModel().getSelectedItem()));
menu.getItems().add(deleteOption);
*/
featureView
.
setContextMenu
(
menu
);
}
public
void
fetchCopyAction
(
Renderable
node
)
{
...
...
@@ -840,36 +836,31 @@ public class MainWindow extends Application {
}
private
void
setup3dView
()
{
Group
root
=
new
Group
();
geomScene
=
new
SubScene
(
root
,
500
,
300
,
true
,
SceneAntialiasing
.
BALANCED
);
geomScene
.
heightProperty
().
bind
(
meshView
.
heightProperty
());
geomScene
.
widthProperty
().
bind
(
meshView
.
widthProperty
());
geomScene
.
setFill
(
Color
.
AZURE
);
meshView
.
getChildren
().
add
(
geomScene
);
geomScene
.
addEventFilter
(
MouseEvent
.
MOUSE_PRESSED
,
me
->
{
glViewport
=
new
GLViewport
();
Region
canvas
=
glViewport
.
getNode
();
canvas
.
prefWidthProperty
().
bind
(
meshView
.
widthProperty
());
canvas
.
prefHeightProperty
().
bind
(
meshView
.
heightProperty
());
meshView
.
getChildren
().
add
(
canvas
);
canvas
.
addEventFilter
(
MouseEvent
.
MOUSE_PRESSED
,
me
->
{
clickStart
[
0
]
=
me
.
getScreenX
();
clickStart
[
1
]
=
me
.
getScreenY
();
});
geomScene
.
addEventFilter
(
MouseEvent
.
MOUSE_RELEASED
,
me
->
{
canvas
.
addEventFilter
(
MouseEvent
.
MOUSE_RELEASED
,
me
->
{
if
(
Math
.
abs
(
clickStart
[
0
]
-
me
.
getScreenX
())
>
3
||
Math
.
abs
(
clickStart
[
1
]
-
me
.
getScreenY
())
>
3
)
{
// skip when mouse moved too much
return
;
}
Node
node
=
me
.
getPickResult
().
getIntersectedNode
();
if
(
node
!=
null
)
{
Object
o
=
node
.
getUserData
();
if
(
o
instanceof
ClickDispatcher
cd
)
{
cd
.
click
(
me
,
clickHandler
);
glViewport
.
requestPick
(
me
.
getX
(),
me
.
getY
(),
target
->
{
if
(
target
instanceof
Polygon
p
)
{
new
PolygonClickDispatcher
(
p
).
click
(
me
,
clickHandler
);
}
}
}
);
});
world
=
new
Group
();
root
.
getChildren
().
add
(
world
);
meshGroup
=
new
Group
();
world
.
getChildren
().
add
(
meshGroup
);
// keep the north arrow aligned with the camera after orbit drags
canvas
.
addEventFilter
(
MouseEvent
.
MOUSE_DRAGGED
,
me
->
alignNorthArrow
());
north
=
new
Image
(
getClass
().
getResourceAsStream
(
"icons/north.png"
),
50
,
50
,
true
,
true
);
northFlip
=
new
Image
(
getClass
().
getResourceAsStream
(
"icons/north_flip.png"
),
50
,
50
,
true
,
true
);
...
...
@@ -880,81 +871,13 @@ public class MainWindow extends Application {
northArrow
.
setY
(
10
);
meshView
.
getChildren
().
add
(
northArrow
);
northArrow
.
setVisible
(
false
);
AmbientLight
al
=
new
AmbientLight
(
Color
.
WHITE
);
root
.
getChildren
().
add
(
al
);
buildCamera
();
cameraXRotation
.
setAxis
(
Rotate
.
X_AXIS
);
cameraZRotation
.
setAxis
(
Rotate
.
Z_AXIS
);
world
.
getTransforms
().
add
(
cameraXRotation
);
world
.
getTransforms
().
add
(
cameraZRotation
);
root
.
getChildren
().
add
(
camera
);
geomScene
.
setCamera
(
camera
);
setupMeshViewControls
();
}
private
void
setupMeshViewControls
()
{
meshView
.
setOnMousePressed
(
me
->
{
if
(
me
.
getButton
()
==
MouseButton
.
PRIMARY
)
{
dragX
=
me
.
getScreenX
();
dragY
=
me
.
getScreenY
();
}
if
(
me
.
getButton
()
==
MouseButton
.
SECONDARY
)
{
dragX
=
me
.
getScreenX
();
dragY
=
me
.
getScreenY
();
}
});
meshView
.
setOnScroll
(
se
->
{
if
(
se
.
getDeltaY
()
<
0
)
{
translateZ
+=
translateZ
*
0.05
;
}
else
{
translateZ
-=
translateZ
*
0.05
;
}
camera
.
setTranslateZ
(
translateZ
);
highlightController
.
changeScaling
(
translateZ
);
});
meshView
.
setOnMouseDragged
(
me
->
{
if
(
me
.
getButton
()
==
MouseButton
.
PRIMARY
)
{
double
deltaX
=
me
.
getScreenX
()
-
dragX
;
double
deltaY
=
me
.
getScreenY
()
-
dragY
;
dragX
=
me
.
getScreenX
();
dragY
=
me
.
getScreenY
();
cameraXRot
+=
(
deltaX
/
3
d
)
%
360
;
cameraYRot
+=
(
deltaY
/
3
d
)
%
360
;
cameraZRotation
.
setAngle
(
cameraXRot
);
cameraXRotation
.
setAngle
(
cameraYRot
);
alignNorthArrow
();
}
if
(
me
.
getButton
()
==
MouseButton
.
SECONDARY
)
{
double
translationSpeed
=
Math
.
abs
(
camera
.
getTranslateZ
())
/
1000
;
double
deltaX
=
me
.
getScreenX
()
-
dragX
;
double
deltaY
=
me
.
getScreenY
()
-
dragY
;
dragX
=
me
.
getScreenX
();
dragY
=
me
.
getScreenY
();
camera
.
setTranslateX
(
camera
.
getTranslateX
()
-
deltaX
*
translationSpeed
);
camera
.
setTranslateY
(
camera
.
getTranslateY
()
-
deltaY
*
translationSpeed
);
}
});
}
private
void
buildCamera
()
{
camera
=
new
PerspectiveCamera
(
true
);
camera
.
setNearClip
(
0.1
);
camera
.
setFarClip
(
10000
d
);
camera
.
setTranslateZ
(
translateZ
);
cameraZRotation
.
setAngle
(
cameraXRot
);
cameraXRotation
.
setAngle
(
cameraYRot
);
}
public
void
alignNorthArrow
()
{
double
absRotZ
=
cameraZRotation
.
getAngle
()
-
360
*
Math
.
floor
(
cameraZRotation
.
getAngle
()
/
360
);
double
absRotX
=
cameraXRotation
.
getAngle
()
-
360
*
Math
.
floor
(
cameraXRotation
.
getAngle
()
/
360
);
double
azimuth
=
glViewport
.
camera
().
azimuthDeg
();
double
elevation
=
glViewport
.
camera
().
elevationDeg
();
double
absRotZ
=
azimuth
-
360
*
Math
.
floor
(
azimuth
/
360
);
double
absRotX
=
elevation
-
360
*
Math
.
floor
(
elevation
/
360
);
double
northRot
;
if
(
80
<=
absRotX
&&
absRotX
<=
260
)
{
...
...
@@ -973,16 +896,11 @@ public class MainWindow extends Application {
}
public
void
resetCamera
()
{
cameraXRot
=
CAMERA_INITIAL_X_ANGLE
;
cameraYRot
=
CAMERA_INITIAL_Y_ANGLE
;
cameraZRotation
.
setAngle
(
cameraXRot
);
cameraXRotation
.
setAngle
(
cameraYRot
);
alignNorthArrow
();
camera
.
setTranslateX
(
0
);
camera
.
setTranslateY
(
0
);
glViewport
.
resetCamera
();
if
(
controller
.
getOriginBB
()
!=
null
)
{
zoomOutForBoundingBox
(
controller
.
getOriginBB
());
}
alignNorthArrow
();
}
public
void
addFileNameToTitle
(
String
fileName
)
{
...
...
@@ -1050,8 +968,14 @@ public class MainWindow extends Application {
return
mainToolBar
.
getGridButton
();
}
public
Group
getMeshGroup
()
{
return
meshGroup
;
public
GLViewport
getGlViewport
()
{
return
glViewport
;
}
/** Clears whatever geometry is currently displayed in the 3D view. */
public
void
clearMeshView
()
{
glViewport
.
setScene
(
null
);
glViewport
.
setIndexSet
(
new
int
[
0
]);
}
public
ToggleButton
getCullingButton
()
{
...
...
@@ -1132,20 +1056,16 @@ public class MainWindow extends Application {
}
public
void
takeViewScreenshot
()
throws
IOException
{
WritableImage
snapshot
=
g
eomScene
.
snapshot
(
null
,
null
);
WritableImage
snapshot
=
g
lViewport
.
getNode
()
.
snapshot
(
null
,
null
);
File
outputFile
=
new
File
(
"img.png"
);
BufferedImage
bImage
=
SwingFXUtils
.
fromFXImage
(
snapshot
,
null
);
ImageIO
.
write
(
bImage
,
"png"
,
outputFile
);
}
public
void
zoomOutForBoundingBox
(
BoundingBox
b
)
{
double
longestSide
=
b
.
getDiagonalLength
()
*
0.4
;
double
d
=
longestSide
/
Math
.
tan
(
Math
.
toRadians
(
30
)
/
2
);
translateZ
=
-
d
;
camera
.
setTranslateZ
(
translateZ
);
camera
.
setTranslateX
(
0
);
camera
.
setTranslateY
(
0
);
highlightController
.
changeScaling
(-
translateZ
);
glViewport
.
camera
().
zoomOutForBoundingBox
(
b
.
getDiagonalLength
());
glViewport
.
getNode
().
repaint
();
highlightController
.
changeScaling
(
glViewport
.
camera
().
distance
());
controller
.
setOriginBB
(
b
);
}
...
...
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java
View file @
854aab9b
...
...
@@ -4,7 +4,6 @@ import de.hft.stuttgart.citydoctor2.check.CheckError;
import
de.hft.stuttgart.citydoctor2.check.Checkable
;
import
de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector
;
import
de.hft.stuttgart.citydoctor2.datastructure.*
;
import
de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter
;
import
de.hft.stuttgart.citydoctor2.gui.tree.*
;
import
de.hft.stuttgart.citydoctor2.gui.tree.node.EdgeNode
;
import
de.hft.stuttgart.citydoctor2.gui.tree.node.ErrorNode
;
...
...
@@ -12,12 +11,15 @@ import de.hft.stuttgart.citydoctor2.gui.tree.node.GenericAttributeNode;
import
de.hft.stuttgart.citydoctor2.gui.tree.node.LinearRingNode
;
import
de.hft.stuttgart.citydoctor2.gui.tree.node.PolygonNode
;
import
de.hft.stuttgart.citydoctor2.gui.tree.node.VertexNode
;
import
de.hft.stuttgart.citydoctor2.gui.gl.GLViewport
;
import
de.hft.stuttgart.citydoctor2.gui.gl.IndexSetBuilder
;
import
de.hft.stuttgart.citydoctor2.gui.gl.SceneBuilder
;
import
de.hft.stuttgart.citydoctor2.gui.gl.SceneData
;
import
de.hft.stuttgart.citydoctor2.gui.gl.TriangleMeta
;
import
de.hft.stuttgart.citydoctor2.math.Triangle3d
;
import
javafx.application.Platform
;
import
javafx.scene.control.TreeItem
;
import
javafx.scene.paint.Color
;
import
javafx.scene.shape.CullFace
;
import
javafx.scene.shape.DrawMode
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
...
...
@@ -28,11 +30,15 @@ public class Renderer {
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
Renderer
.
class
);
private
TriangulatedGeometry
current
TriGeom
;
private
SceneData
current
Scene
;
private
Geometry
currentGeometry
;
private
CullFace
currentCulling
=
CullFace
.
NONE
;
private
DrawMode
currentDrawMode
=
DrawMode
.
FILL
;
private
boolean
culling
=
false
;
private
boolean
wireframe
=
false
;
// LOD visibility, indexed by Lod.ordinal() (LOD0..LOD4); all enabled by default
private
final
boolean
[]
lodEnabled
=
{
true
,
true
,
true
,
true
,
true
};
private
boolean
roofsHidden
=
false
;
private
MainWindow
mainWindow
;
private
HighlightController
highlightController
;
...
...
@@ -40,8 +46,6 @@ public class Renderer {
private
LoadingInfoDialog
loadingDialog
;
private
List
<
ViewFilter
>
lodFilters
=
new
ArrayList
<>();
private
ViewFilter
roofFilter
;
private
Runnable
refresher
;
private
Runnable
errorUpdater
;
...
...
@@ -50,138 +54,74 @@ public class Renderer {
this
.
highlightController
=
highlightController
;
loadingDialog
=
new
LoadingInfoDialog
(
mainWindow
.
getMainStage
());
errVisitor
=
new
ListErrorVisitor
(
highlightController
);
setupLodFilters
();
}
private
void
setupLodFilters
()
{
lodFilters
.
add
(
new
ViewFilter
()
{
@Override
public
boolean
useGeometry
(
CityObject
co
,
Geometry
geom
)
{
return
geom
.
getLod
()
==
Lod
.
LOD1
;
}
});
lodFilters
.
add
(
new
ViewFilter
()
{
@Override
public
boolean
useGeometry
(
CityObject
co
,
Geometry
geom
)
{
return
geom
.
getLod
()
==
Lod
.
LOD2
;
}
});
lodFilters
.
add
(
new
ViewFilter
()
{
@Override
public
boolean
useGeometry
(
CityObject
co
,
Geometry
geom
)
{
return
geom
.
getLod
()
==
Lod
.
LOD3
;
}
});
lodFilters
.
add
(
new
ViewFilter
()
{
@Override
protected
boolean
useGeometry
(
CityObject
co
,
Geometry
geom
)
{
return
geom
.
getLod
()
==
Lod
.
LOD0
;
}
});
lodFilters
.
add
(
new
ViewFilter
()
{
@Override
public
boolean
useGeometry
(
CityObject
co
,
Geometry
geom
)
{
return
geom
.
getLod
()
==
Lod
.
LOD4
;
}
});
roofFilter
=
new
ViewFilter
()
{
@Override
public
boolean
allowedToUse
(
CityObject
co
,
Geometry
geom
)
{
if
(!
this
.
isEnabled
())
{
return
true
;
}
return
this
.
useGeometry
(
co
,
geom
);
}
@Override
public
boolean
useGeometry
(
CityObject
co
,
Geometry
geom
)
{
if
(
co
instanceof
BoundarySurface
bs
)
{
return
bs
.
getType
()
!=
BoundarySurfaceType
.
ROOF
;
}
return
true
;
}
private
boolean
triangleVisible
(
TriangleMeta
m
)
{
int
lod
=
m
.
lod
();
boolean
lodOk
=
lod
<
0
||
lod
>=
lodEnabled
.
length
||
lodEnabled
[
lod
];
if
(!
lodOk
)
{
return
false
;
}
return
!
roofsHidden
||
!
m
.
roof
();
}
};
roofFilter
.
setEnable
(
false
);
private
void
rebuildIndexSet
()
{
if
(
currentScene
==
null
)
{
return
;
}
mainWindow
.
getGlViewport
().
setIndexSet
(
IndexSetBuilder
.
build
(
currentScene
.
triangleMetas
(),
this
::
triangleVisible
));
}
public
void
enableLod1
()
{
lodFilters
.
get
(
0
).
enable
();
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
setLodEnabled
(
Lod
.
LOD1
.
ordinal
(),
true
);
}
public
void
disableLod1
()
{
lodFilters
.
get
(
0
).
disable
();
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
setLodEnabled
(
Lod
.
LOD1
.
ordinal
(),
false
);
}
public
void
enableLod2
()
{
lodFilters
.
get
(
1
).
enable
();
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
setLodEnabled
(
Lod
.
LOD2
.
ordinal
(),
true
);
}
public
void
disableLod2
()
{
lodFilters
.
get
(
1
).
disable
();
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
setLodEnabled
(
Lod
.
LOD2
.
ordinal
(),
false
);
}
public
void
enableLod3
()
{
lodFilters
.
get
(
2
).
enable
();
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
setLodEnabled
(
Lod
.
LOD3
.
ordinal
(),
true
);
}
public
void
disableLod3
()
{
lodFilters
.
get
(
2
).
disable
();
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
setLodEnabled
(
Lod
.
LOD3
.
ordinal
(),
false
);
}
public
void
enableLod4
()
{
lodFilters
.
get
(
3
).
enable
();
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
setLodEnabled
(
Lod
.
LOD4
.
ordinal
(),
true
);
}
public
void
disableLod4
()
{
lodFilters
.
get
(
3
).
disable
();
if
(
refresher
!=
null
)
{
refresher
.
run
();
setLodEnabled
(
Lod
.
LOD4
.
ordinal
(),
false
);
}
private
void
setLodEnabled
(
int
lodOrdinal
,
boolean
enabled
)
{
// legacy LOD0 followed LOD3 button; keep LOD0 tied to LOD3 toggle for parity
lodEnabled
[
lodOrdinal
]
=
enabled
;
if
(
lodOrdinal
==
Lod
.
LOD3
.
ordinal
())
{
lodEnabled
[
Lod
.
LOD0
.
ordinal
()]
=
enabled
;
}
Platform
.
runLater
(
this
::
rebuildIndexSet
);
}
public
void
hideRoofs
()
{
roofFilter
.
setEnable
(
true
);
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
roofsHidden
=
true
;
Platform
.
runLater
(
this
::
rebuildIndexSet
);
}
public
void
showRoofs
()
{
roofFilter
.
setEnable
(
false
);
if
(
refresher
!=
null
)
{
refresher
.
run
();
}
roofsHidden
=
false
;
Platform
.
runLater
(
this
::
rebuildIndexSet
);
}
public
void
setupRefresher
(
CityObject
co
)
{
...
...
@@ -207,18 +147,7 @@ public class Renderer {
private
void
addPolygons
(
CityObject
co
,
Set
<
ConcretePolygon
>
polygons
)
{
for
(
Geometry
geom
:
co
.
getGeometries
())
{
boolean
used
=
false
;
for
(
ViewFilter
filter
:
lodFilters
)
{
if
(
filter
.
allowedToUse
(
co
,
geom
))
{
used
=
true
;
break
;
}
}
if
(
used
)
{
if
(
roofFilter
.
allowedToUse
(
co
,
geom
))
{
addConcretePolygons
(
polygons
,
geom
);
}
}
addConcretePolygons
(
polygons
,
geom
);
}
}
...
...
@@ -244,15 +173,14 @@ public class Renderer {
public
void
render
(
Geometry
geom
)
{
refresher
=
()
->
{
Platform
.
runLater
(
this
::
clearGeometryTrees
);
currentTriGeom
=
TriangulatedGeometry
.
of
(
geom
);
SceneData
scene
=
SceneBuilder
.
fromPolygons
(
geom
.
getPolygons
(),
Color
.
WHITE
);
if
(
geom
.
getEdges
()
==
null
&&
currentGeometry
!=
null
)
{
// if there are no edges available low memory mode is enabled
// clear the old geometry of all meta information
currentGeometry
.
clearMetaData
();
}
errVisitor
.
setGeometry
(
currentTriGeom
);
Platform
.
runLater
(()
->
{
setupRenderState
(
);
applyScene
(
scene
);
if
(
geom
.
getEdges
()
==
null
)
{
// create edges and vertices so they can be listed in the gui
geom
.
prepareForChecking
();
...
...
@@ -349,9 +277,8 @@ public class Renderer {
private
void
render
(
Collection
<?
extends
Polygon
>
polygons
)
{
Platform
.
runLater
(
this
::
clearGeometryTrees
);
currentTriGeom
=
TriangulatedGeometry
.
of
(
polygons
);
errVisitor
.
setGeometry
(
currentTriGeom
);
setupRenderState
();
SceneData
scene
=
SceneBuilder
.
fromPolygons
(
polygons
,
Color
.
WHITE
);
Platform
.
runLater
(()
->
applyScene
(
scene
));
}
private
void
clearGeometryTrees
()
{
...
...
@@ -375,10 +302,9 @@ public class Renderer {
clearAttributes
();
});
Thread
t
=
new
Thread
(()
->
{
currentTriGeom
=
TriangulatedGeometry
.
of
(
model
,
lodFilters
);
errVisitor
.
setGeometry
(
currentTriGeom
);
SceneData
scene
=
SceneBuilder
.
fromModel
(
model
);
Platform
.
runLater
(()
->
{
setupRenderState
(
);
applyScene
(
scene
);
mainWindow
.
zoomOutForBoundingBox
(
BoundingBox
.
of
(
model
));
loadingDialog
.
hide
();
});
...
...
@@ -430,14 +356,10 @@ public class Renderer {
CityObjectCollector
collector
=
new
CityObjectCollector
();
cos
.
forEach
(
cityObject
->
cityObject
.
accept
(
collector
));
collector
.
getCityObjects
().
forEach
(
cityObject
->
addPolygons
(
cityObject
,
polygons
));
if
(
baseColor
!=
null
)
{
currentTriGeom
=
TriangulatedGeometry
.
of
(
polygons
,
baseColor
);
}
else
{
currentTriGeom
=
TriangulatedGeometry
.
of
(
polygons
);
}
errVisitor
.
setGeometry
(
currentTriGeom
);
Color
base
=
baseColor
!=
null
?
baseColor
:
Color
.
WHITE
;
SceneData
scene
=
SceneBuilder
.
fromPolygons
(
polygons
,
base
);
Platform
.
runLater
(()
->
{
setupRenderState
(
);
applyScene
(
scene
);
mainWindow
.
zoomOutForBoundingBox
(
BoundingBox
.
of
(
polygons
));
loadingDialog
.
hide
();
});
...
...
@@ -455,49 +377,44 @@ public class Renderer {
private
void
addConcretePolygons
(
Set
<
ConcretePolygon
>
polygons
,
Geometry
geom
)
{
for
(
Polygon
p
:
geom
.
getPolygons
())
{
if
(
p
.
getOriginal
().
getPartOfSurface
()
!=
null
&&
!
roofFilter
.
allowedToUse
(
p
.
getOriginal
().
getPartOfSurface
(),
p
.
getParent
()))
continue
;
polygons
.
add
(
p
.
getOriginal
());
}
}
private
void
setupRenderState
()
{
currentTriGeom
.
setCullFace
(
currentCulling
);
currentTriGeom
.
setDrawMode
(
currentDrawMode
);
Platform
.
runLater
(()
->
{
mainWindow
.
getMeshGroup
().
getChildren
().
clear
();
mainWindow
.
getMeshGroup
().
getChildren
().
addAll
(
currentTriGeom
.
getMeshes
());
});
/**
* Installs the freshly built scene into the viewport (FX thread), wires the highlight center
* supplier, applies the current wireframe/cull state and rebuilds the visible index set.
*/
private
void
applyScene
(
SceneData
scene
)
{
currentScene
=
scene
;
GLViewport
viewport
=
mainWindow
.
getGlViewport
();
viewport
.
setScene
(
scene
);
highlightController
.
setCenterSupplier
(
()
->
currentScene
==
null
?
new
double
[
3
]
:
currentScene
.
center
());
viewport
.
setWireframe
(
wireframe
);
viewport
.
setCulling
(
culling
);
rebuildIndexSet
();
mainWindow
.
getGridButton
().
setDisable
(
false
);
mainWindow
.
getCullingButton
().
setDisable
(
false
);
}
public
void
showWireFrame
(
boolean
show
)
{
if
(
currentTriGeom
!=
null
)
{
if
(
show
)
{
currentDrawMode
=
DrawMode
.
LINE
;
}
else
{
currentDrawMode
=
DrawMode
.
FILL
;
}
currentTriGeom
.
setDrawMode
(
currentDrawMode
);
}
wireframe
=
show
;
mainWindow
.
getGlViewport
().
setWireframe
(
show
);
}
public
void
enableCulling
(
boolean
enable
)
{
if
(
currentTriGeom
!=
null
)
{
if
(
enable
)
{
currentCulling
=
CullFace
.
BACK
;
}
else
{
currentCulling
=
CullFace
.
NONE
;
}
currentTriGeom
.
setCullFace
(
currentCulling
);
}
culling
=
enable
;
mainWindow
.
getGlViewport
().
setCulling
(
enable
);
}
public
void
clearCurrentRender
()
{
// don't render anything
currentScene
=
null
;
Platform
.
runLater
(()
->
{
mainWindow
.
getMeshGroup
().
getChildren
().
clear
();
GLViewport
viewport
=
mainWindow
.
getGlViewport
();
viewport
.
setScene
(
null
);
viewport
.
setIndexSet
(
new
int
[
0
]);
clearGeometryTrees
();
clearAttributes
();
});
...
...
@@ -508,35 +425,35 @@ public class Renderer {
}
public
void
highlight
(
Polygon
p
)
{
highlightController
.
highlight
(
p
,
currentTriGeom
);
highlightController
.
highlight
(
p
);
}
public
void
highlight
(
LinearRing
lr
)
{
highlightController
.
highlight
(
lr
,
currentTriGeom
);
highlightController
.
highlight
(
lr
);
}
public
void
highlight
(
Edge
e
)
{
highlightController
.
highlight
(
e
,
currentTriGeom
);
highlightController
.
highlight
(
e
);
}
public
void
highlight
(
Vertex
v
)
{
highlightController
.
highlight
(
v
,
currentTriGeom
);
highlightController
.
highlight
(
v
);
}
public
void
highlight
(
List
<
LinearRing
>
highlightedRings
)
{
highlightController
.
highlight
(
highlightedRings
,
currentTriGeom
);
highlightController
.
highlight
(
highlightedRings
);
}
public
void
highlightEdges
(
List
<
Edge
>
edges
)
{
highlightController
.
highlightEdges
(
edges
,
currentTriGeom
);
highlightController
.
highlightEdges
(
edges
);
}
public
void
highlightPolygons
(
List
<
List
<
Polygon
>>
components
)
{
highlightController
.
highlightPolygons
(
components
,
currentTriGeom
);
highlightController
.
highlightPolygons
(
components
);
}
public
void
addHighlight
(
Vertex
vertex
,
Color
c
)
{
highlightController
.
addHighlight
(
vertex
,
currentTriGeom
,
c
);
highlightController
.
addHighlight
(
vertex
,
c
);
}
public
void
highlight
(
CheckError
err
)
{
...
...
@@ -560,11 +477,11 @@ public class Renderer {
}
public
void
addHighlight
(
Polygon
p
)
{
highlightController
.
addHighlight
(
p
,
currentTriGeom
);
highlightController
.
addHighlight
(
p
);
}
public
void
addHighlight
(
Triangle3d
t
)
{
highlightController
.
highlight
(
t
,
currentTriGeom
);
highlightController
.
highlight
(
t
);
}
public
void
reset
()
{
...
...
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java
deleted
100644 → 0
View file @
760147a4
package
de.hft.stuttgart.citydoctor2.gui
;
import
de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.*
;
import
de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter
;
import
de.hft.stuttgart.citydoctor2.math.Triangle3d
;
import
de.hft.stuttgart.citydoctor2.math.UnitVector3d
;
import
de.hft.stuttgart.citydoctor2.math.Vector3d
;
import
de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon
;
import
javafx.scene.paint.Color
;
import
javafx.scene.paint.PhongMaterial
;
import
javafx.scene.shape.*
;
import
java.util.*
;
import
java.util.stream.Stream
;
public
class
TriangulatedGeometry
{
private
static
final
PhongMaterial
GRID_MAT
=
new
PhongMaterial
(
Color
.
BLACK
);
// random vector for calculating normal angles, for color determination
private
static
final
UnitVector3d
AXIS
=
new
Vector3d
(
19
,
0.8
,
1.5
).
normalize
();
private
Vector3d
movedBy
;
private
List
<
MeshView
>
meshes
;
private
List
<
PhongMaterial
>
materials
;
public
static
TriangulatedGeometry
of
(
Geometry
geom
)
{
return
of
(
geom
.
getPolygons
());
}
public
static
TriangulatedGeometry
of
(
Collection
<?
extends
Polygon
>
polygons
,
Color
basePolygonColor
)
{
TriangulatedGeometry
triGeom
=
new
TriangulatedGeometry
();
triGeom
.
materials
=
new
ArrayList
<>();
triGeom
.
meshes
=
new
ArrayList
<>();
List
<
Vector3d
>
points
=
new
ArrayList
<>();
for
(
Polygon
p
:
polygons
)
{
points
.
addAll
(
p
.
getExteriorRing
().
getVertices
());
}
triGeom
.
movedBy
=
triGeom
.
findCenter
(
points
);
addPolygonDataToTriGeom
(
polygons
,
basePolygonColor
,
triGeom
);
return
triGeom
;
}
private
static
void
addPolygonDataToTriGeom
(
Collection
<?
extends
Polygon
>
polygons
,
Color
basePolygonColor
,
TriangulatedGeometry
triGeom
)
{
for
(
Polygon
p
:
polygons
)
{
TesselatedPolygon
tp
=
p
.
tesselate
();
TriangleMesh
triMesh
=
new
TriangleMesh
(
VertexFormat
.
POINT_TEXCOORD
);
Map
<
Vector3d
,
Integer
>
indexMap
=
new
HashMap
<>();
List
<
Vector3d
>
vertices
=
new
ArrayList
<>();
int
index
=
0
;
for
(
Triangle3d
t
:
tp
.
getTriangles
())
{
index
=
triGeom
.
filterDuplicates
(
triMesh
,
indexMap
,
vertices
,
index
,
t
.
getP1
());
index
=
triGeom
.
filterDuplicates
(
triMesh
,
indexMap
,
vertices
,
index
,
t
.
getP2
());
index
=
triGeom
.
filterDuplicates
(
triMesh
,
indexMap
,
vertices
,
index
,
t
.
getP3
());
}
for
(
Vector3d
point
:
vertices
)
{
float
x
=
(
float
)
(
point
.
getX
()
-
triGeom
.
movedBy
.
getX
());
float
y
=
(
float
)
(
point
.
getY
()
-
triGeom
.
movedBy
.
getY
());
float
z
=
(
float
)
(
point
.
getZ
()
-
triGeom
.
movedBy
.
getZ
());
triMesh
.
getPoints
().
addAll
(
x
,
y
,
z
);
}
triMesh
.
getTexCoords
().
addAll
(
0
,
0
);
MeshView
view
=
new
MeshView
(
triMesh
);
view
.
setUserData
(
new
PolygonClickDispatcher
(
p
));
PhongMaterial
mat
=
triGeom
.
calculateMaterial
(
p
,
basePolygonColor
);
triGeom
.
materials
.
add
(
mat
);
triGeom
.
meshes
.
add
(
view
);
}
}
public
static
TriangulatedGeometry
of
(
CityDoctorModel
model
,
List
<
ViewFilter
>
filters
)
{
List
<
Vector3d
>
points
=
new
ArrayList
<>();
addPointsFromCityObject
(
model
.
createFeatureStream
().
toList
(),
points
);
TriangulatedGeometry
triGeom
=
new
TriangulatedGeometry
();
triGeom
.
materials
=
new
ArrayList
<>();
triGeom
.
meshes
=
new
ArrayList
<>();
triGeom
.
movedBy
=
triGeom
.
findCenter
(
points
);
addPolygonDataFromCityObjects
(
model
.
getBuildings
(),
triGeom
,
Color
.
WHITE
,
filters
);
addPolygonDataFromCityObjects
(
model
.
getBridges
(),
triGeom
,
Color
.
LIGHTSTEELBLUE
,
filters
);
addPolygonDataFromCityObjects
(
model
.
getTunnels
(),
triGeom
,
Color
.
SLATEGRAY
,
filters
);
addPolygonDataFromCityObjects
(
model
.
getLand
(),
triGeom
,
Color
.
TAN
,
filters
);
addPolygonDataFromCityObjects
(
model
.
getTransportation
(),
triGeom
,
Color
.
DIMGRAY
,
filters
);
addPolygonDataFromCityObjects
(
model
.
getVegetation
(),
triGeom
,
Color
.
LIGHTGREEN
,
filters
);
addPolygonDataFromCityObjects
(
model
.
getWater
(),
triGeom
,
Color
.
LIGHTSKYBLUE
,
filters
);
addPolygonDataFromCityObjects
(
model
.
getCityFurniture
(),
triGeom
,
Color
.
BLUEVIOLET
,
filters
);
addPolygonDataFromCityObjects
(
model
.
getOtherCityObjects
(),
triGeom
,
Color
.
BLACK
,
filters
);
return
triGeom
;
}
private
static
void
addPolygonDataFromCityObjects
(
Stream
<?
extends
CityObject
>
cos
,
TriangulatedGeometry
triGeom
,
Color
color
,
List
<
ViewFilter
>
filters
)
{
CityObjectCollector
collector
=
new
CityObjectCollector
();
cos
.
forEach
(
cityObject
->
cityObject
.
accept
(
collector
));
collector
.
getCityObjects
().
forEach
(
cityObject
->
addPolygonData
(
cityObject
,
triGeom
,
color
,
filters
));
}
private
static
void
addPolygonData
(
CityObject
co
,
TriangulatedGeometry
triGeom
,
Color
color
,
List
<
ViewFilter
>
filters
)
{
for
(
Geometry
geom
:
co
.
getGeometries
())
{
if
(
isGeometryFiltered
(
co
,
geom
,
filters
))
{
continue
;
}
List
<
Polygon
>
polygons
=
new
ArrayList
<>();
for
(
Polygon
p
:
geom
.
getPolygons
())
{
if
(
p
.
isLink
())
{
continue
;
}
polygons
.
add
(
p
);
}
addPolygonDataToTriGeom
(
polygons
,
color
,
triGeom
);
}
}
private
static
boolean
isGeometryFiltered
(
CityObject
co
,
Geometry
geom
,
List
<
ViewFilter
>
filters
)
{
for
(
ViewFilter
filter
:
filters
)
{
if
(
filter
.
allowedToUse
(
co
,
geom
))
{
return
false
;
}
}
return
true
;
}
private
static
void
addPointsFromCityObject
(
List
<?
extends
CityObject
>
cos
,
List
<
Vector3d
>
points
)
{
CityObjectCollector
collector
=
new
CityObjectCollector
();
cos
.
forEach
(
cityObject
->
cityObject
.
accept
(
collector
));
collector
.
getCityObjects
().
forEach
(
cityObject
->
addPoints
(
cityObject
,
points
));
}
private
static
void
addPoints
(
CityObject
co
,
List
<
Vector3d
>
points
)
{
for
(
Geometry
geom
:
co
.
getGeometries
())
{
for
(
Polygon
p
:
geom
.
getPolygons
())
{
if
(
p
.
isLink
())
{
continue
;
}
points
.
addAll
(
p
.
getExteriorRing
().
getVertices
());
}
}
}
public
static
TriangulatedGeometry
of
(
Collection
<?
extends
Polygon
>
polygons
)
{
return
of
(
polygons
,
Color
.
WHITE
);
}
private
int
filterDuplicates
(
TriangleMesh
triMesh
,
Map
<
Vector3d
,
Integer
>
indexMap
,
List
<
Vector3d
>
vertices
,
int
index
,
Vector3d
v
)
{
Integer
vertexIndex
=
indexMap
.
get
(
v
);
if
(
vertexIndex
==
null
)
{
indexMap
.
put
(
v
,
index
);
vertices
.
add
(
v
);
vertexIndex
=
index
;
index
++;
}
triMesh
.
getFaces
().
addAll
(
vertexIndex
,
0
);
return
index
;
}
private
PhongMaterial
calculateMaterial
(
Polygon
p
,
Color
baseColor
)
{
Vector3d
normal
=
p
.
calculateNormalNormalized
();
if
(
p
.
getRenderColor
()
!=
null
&&
p
.
getRenderColor
()
!=
Color
.
WHITE
)
{
baseColor
=
p
.
getRenderColor
();
}
double
cos
=
normal
.
dot
(
AXIS
);
double
acos
=
Math
.
acos
(
cos
);
// normalize to range [0.3, 0.9]
acos
=
acos
/
Math
.
PI
;
acos
=
acos
*
0.6
+
0.3
;
Color
derivedColor
=
baseColor
.
deriveColor
(
0
,
1.0
,
acos
,
1.0
);
return
new
PhongMaterial
(
derivedColor
);
}
private
Vector3d
findCenter
(
List
<
Vector3d
>
points
)
{
double
xMin
=
Double
.
MAX_VALUE
;
double
yMin
=
Double
.
MAX_VALUE
;
double
zMin
=
Double
.
MAX_VALUE
;
double
xMax
=
Double
.
NEGATIVE_INFINITY
;
double
yMax
=
Double
.
NEGATIVE_INFINITY
;
double
zMax
=
Double
.
NEGATIVE_INFINITY
;
for
(
Vector3d
point
:
points
)
{
if
(
point
.
getX
()
<
xMin
)
{
xMin
=
point
.
getX
();
}
if
(
point
.
getX
()
>
xMax
)
{
xMax
=
point
.
getX
();
}
if
(
point
.
getY
()
<
yMin
)
{
yMin
=
point
.
getY
();
}
if
(
point
.
getY
()
>
yMax
)
{
yMax
=
point
.
getY
();
}
if
(
point
.
getZ
()
<
zMin
)
{
zMin
=
point
.
getZ
();
}
if
(
point
.
getZ
()
>
zMax
)
{
zMax
=
point
.
getZ
();
}
}
// center
double
x
=
(
xMax
-
xMin
)
/
2
+
xMin
;
double
y
=
(
yMax
-
yMin
)
/
2
+
yMin
;
double
z
=
(
zMax
-
zMin
)
/
2
+
zMin
;
return
new
Vector3d
(
x
,
y
,
z
);
}
public
Vector3d
getMovedBy
()
{
return
movedBy
;
}
public
void
setCullFace
(
CullFace
currentCulling
)
{
for
(
MeshView
mesh
:
meshes
)
{
mesh
.
setCullFace
(
currentCulling
);
}
}
public
void
setDrawMode
(
DrawMode
currentDrawMode
)
{
if
(
currentDrawMode
==
DrawMode
.
LINE
)
{
for
(
MeshView
mesh
:
meshes
)
{
mesh
.
setDrawMode
(
currentDrawMode
);
mesh
.
setMaterial
(
GRID_MAT
);
}
}
else
if
(
currentDrawMode
==
DrawMode
.
FILL
)
{
for
(
int
i
=
0
;
i
<
meshes
.
size
();
i
++)
{
MeshView
mesh
=
meshes
.
get
(
i
);
mesh
.
setDrawMode
(
currentDrawMode
);
mesh
.
setMaterial
(
materials
.
get
(
i
));
}
}
}
public
List
<
MeshView
>
getMeshes
()
{
return
meshes
;
}
}
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ValidationView.java
View file @
854aab9b
...
...
@@ -46,7 +46,7 @@ public class ValidationView extends View {
public
void
onHide
()
{
Platform
.
runLater
(()
->
{
mainWindow
.
unselectEverything
();
mainWindow
.
getMeshGroup
().
getChildren
().
clear
();
mainWindow
.
clearMeshView
();
mainWindow
.
clearHighlights
();
mainWindow
.
getErrorTree
().
getRoot
().
getChildren
().
clear
();
mainWindow
.
getPolygonsView
().
getRoot
().
getChildren
().
clear
();
...
...
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/gl/FloatList.java
0 → 100644
View file @
854aab9b
package
de.hft.stuttgart.citydoctor2.gui.gl
;
import
java.util.Arrays
;
/** Growable primitive float array to avoid boxing when building large vertex buffers. */
public
class
FloatList
{
private
float
[]
data
;
private
int
size
;
public
FloatList
()
{
this
(
1024
);
}
public
FloatList
(
int
initialCapacity
)
{
data
=
new
float
[
Math
.
max
(
1
,
initialCapacity
)];
}
public
void
add
(
float
v
)
{
ensure
(
size
+
1
);
data
[
size
++]
=
v
;
}
public
void
add
(
float
a
,
float
b
,
float
c
)
{
ensure
(
size
+
3
);
data
[
size
++]
=
a
;
data
[
size
++]
=
b
;
data
[
size
++]
=
c
;
}
private
void
ensure
(
int
capacity
)
{
if
(
capacity
>
data
.
length
)
{
int
newCap
=
data
.
length
;
while
(
newCap
<
capacity
)
{
newCap
<<=
1
;
}
data
=
Arrays
.
copyOf
(
data
,
newCap
);
}
}
public
int
size
()
{
return
size
;
}
/** Resets the list to empty without releasing the backing array. */
public
void
clear
()
{
size
=
0
;
}
public
float
[]
toArray
()
{
return
Arrays
.
copyOf
(
data
,
size
);
}
}
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/gl/GLViewport.java
0 → 100644
View file @
854aab9b
package
de.hft.stuttgart.citydoctor2.gui.gl
;
import
com.huskerdev.grapl.gl.GLProfile
;
import
com.huskerdev.openglfx.canvas.GLCanvas
;
import
com.huskerdev.openglfx.jogl.JOGLExecutor
;
import
com.huskerdev.openglfx.jogl.events.JOGLRenderEvent
;
import
com.jogamp.opengl.GL
;
import
com.jogamp.opengl.GL3
;
import
java.util.concurrent.atomic.AtomicReference
;
import
java.util.function.Consumer
;
import
javafx.animation.AnimationTimer
;
import
javafx.application.Platform
;
import
javafx.scene.input.MouseButton
;
import
javafx.scene.paint.Color
;
/** OpenGL viewport embedded in JavaFX via openglfx. Replaces the legacy SubScene. */
public
class
GLViewport
{
private
static
final
Color
BACKGROUND
=
Color
.
AZURE
;
private
final
GLCanvas
canvas
;
private
final
OrbitCamera
camera
=
new
OrbitCamera
();
private
final
GlScene
scene
=
new
GlScene
();
private
final
PickingFramebuffer
picking
=
new
PickingFramebuffer
();
private
final
HighlightOverlay
overlay
=
new
HighlightOverlay
();
private
ShaderProgram
sceneProgram
;
private
ShaderProgram
pickProgram
;
private
ShaderProgram
pointProgram
;
private
boolean
initialized
;
// state set off-thread, consumed in render callback
private
final
AtomicReference
<
SceneData
>
pendingScene
=
new
AtomicReference
<>();
private
final
AtomicReference
<
int
[]>
pendingIndexSet
=
new
AtomicReference
<>();
// last index set seen; retained so it can be (re)applied after the scene is uploaded or the GL
// context is recreated (the index buffer only exists once the scene's buffers have been generated)
private
volatile
int
[]
lastIndices
;
private
volatile
boolean
wireframe
=
false
;
private
volatile
boolean
cull
=
false
;
private
volatile
float
pointScale
=
20
f
;
private
volatile
float
lineScale
=
4
f
;
// highlight marker data, uploaded on the GL thread when dirty
private
volatile
float
[]
hlPoints
=
new
float
[
0
];
private
volatile
float
[]
hlPointColors
=
new
float
[
0
];
private
volatile
float
[]
hlLines
=
new
float
[
0
];
private
volatile
float
[]
hlLineColors
=
new
float
[
0
];
private
volatile
boolean
highlightsDirty
=
false
;
// pick request: logical (JavaFX) screen coords + callback (invoked on FX thread with resolved Object)
private
volatile
double
[]
pickRequest
;
private
volatile
Consumer
<
Object
>
pickCallback
;
private
SceneData
currentScene
;
// drag state
private
double
lastX
;
private
double
lastY
;
// optional hook invoked after a camera interaction (orbit/pan/zoom) so a coupled viewport can be
// kept in sync and redrawn (used by the dual-viewport healer)
private
Runnable
interactionListener
;
// on-demand render request. openglfx can drop a repaint() that arrives while a frame is rendering
// (its requestRepaint is an unconditional notify with no pending flag), so a single repaint is not
// reliable. This flag + pump re-deliver repaint() each pulse until a frame consumes the request,
// then the pump stops itself so an idle viewport has no per-pulse cost.
private
volatile
boolean
renderRequested
;
private
final
AnimationTimer
repaintPump
=
new
AnimationTimer
()
{
@Override
public
void
handle
(
long
now
)
{
if
(
renderRequested
)
{
canvas
.
repaint
();
}
else
{
stop
();
}
}
};
public
GLViewport
()
{
// args: executor, profile, flipY=false, msaa=4, fps=0.0
// fps=0 -> on-demand rendering: a frame is drawn only when repaint() is called (every state
// change / input does so), so an idle viewport allocates and renders nothing.
canvas
=
new
GLCanvas
(
new
JOGLExecutor
(),
GLProfile
.
CORE
,
false
,
4
,
0.0
);
canvas
.
addOnInitEvent
(
ev
->
{
// GL context (re)created: shaders/buffers from any previous context are gone. Drop our
// handles and re-queue the current scene so it is re-uploaded into the new context.
initialized
=
false
;
scene
.
markContextLost
();
if
(
currentScene
!=
null
)
{
pendingScene
.
set
(
currentScene
);
}
});
canvas
.
addOnRenderEvent
(
ev
->
render
((
JOGLRenderEvent
)
ev
));
canvas
.
addOnReshapeEvent
(
ev
->
{
/* aspect handled per-frame from width/height */
});
// on-demand mode: repaint when the canvas is resized so the framebuffer tracks the new size
canvas
.
widthProperty
().
addListener
((
obs
,
ov
,
nv
)
->
requestRender
());
canvas
.
heightProperty
().
addListener
((
obs
,
ov
,
nv
)
->
requestRender
());
installInputHandlers
();
}
public
GLCanvas
getNode
()
{
return
canvas
;
}
// ---- public API (called from FX / worker threads) ----
public
void
setScene
(
SceneData
data
)
{
pendingScene
.
set
(
data
);
requestRender
();
}
public
void
setIndexSet
(
int
[]
indices
)
{
pendingIndexSet
.
set
(
indices
);
requestRender
();
}
public
void
setWireframe
(
boolean
on
)
{
wireframe
=
on
;
requestRender
();
}
public
void
setCulling
(
boolean
on
)
{
cull
=
on
;
requestRender
();
}
public
void
setPointScale
(
float
scale
)
{
pointScale
=
scale
;
requestRender
();
}
/** points: xyz, pointColors rgb-per-point; lines: xyz pairs, lineColors rgb-per-line-vertex. */
public
void
setHighlights
(
float
[]
points
,
float
[]
pointColors
,
float
[]
lines
,
float
[]
lineColors
)
{
this
.
hlPoints
=
points
;
this
.
hlPointColors
=
pointColors
;
this
.
hlLines
=
lines
;
this
.
hlLineColors
=
lineColors
;
this
.
highlightsDirty
=
true
;
requestRender
();
}
public
OrbitCamera
camera
()
{
return
camera
;
}
/** Hook invoked after a camera interaction so a coupled viewport can sync and redraw. */
public
void
setInteractionListener
(
Runnable
listener
)
{
this
.
interactionListener
=
listener
;
}
/** Public redraw trigger (e.g. for a coupled viewport). */
public
void
requestRedraw
()
{
requestRender
();
}
/** Clears the displayed geometry (draws nothing) until a new scene is set. */
public
void
clearScene
()
{
currentScene
=
null
;
lastIndices
=
new
int
[
0
];
setIndexSet
(
new
int
[
0
]);
}
/**
* Requests a redraw. Sets the render flag, wakes the canvas immediately (best effort), and starts
* the pump so the request is re-delivered until a frame actually consumes it. Call on the FX thread.
*/
private
void
requestRender
()
{
renderRequested
=
true
;
canvas
.
repaint
();
repaintPump
.
start
();
// idempotent while running
}
public
void
requestPick
(
double
x
,
double
y
,
Consumer
<
Object
>
callback
)
{
pickRequest
=
new
double
[]{
x
,
y
};
pickCallback
=
callback
;
requestRender
();
}
// ---- render callback (GL thread) ----
private
void
render
(
JOGLRenderEvent
ev
)
{
// consume the render request; if new state arrives during this frame, requestRender re-arms it
renderRequested
=
false
;
GL3
gl
=
ev
.
getGl
();
int
w
=
ev
.
width
;
int
h
=
ev
.
height
;
int
defaultFbo
=
ev
.
fbo
;
if
(!
initialized
)
{
sceneProgram
=
new
ShaderProgram
(
gl
,
"scene.vert"
,
"scene.frag"
);
pickProgram
=
new
ShaderProgram
(
gl
,
"pick.vert"
,
"pick.frag"
);
pointProgram
=
new
ShaderProgram
(
gl
,
"point.vert"
,
"point.geom"
,
"point.frag"
);
overlay
.
init
(
gl
);
gl
.
glEnable
(
GL
.
GL_DEPTH_TEST
);
gl
.
glEnable
(
GL
.
GL_MULTISAMPLE
);
// anti-aliasing for the multisampled framebuffer
initialized
=
true
;
}
SceneData
newScene
=
pendingScene
.
getAndSet
(
null
);
boolean
uploadedNow
=
false
;
if
(
newScene
!=
null
)
{
scene
.
upload
(
gl
,
newScene
);
currentScene
=
newScene
;
uploadedNow
=
true
;
}
int
[]
newIndices
=
pendingIndexSet
.
getAndSet
(
null
);
if
(
newIndices
!=
null
)
{
lastIndices
=
newIndices
;
}
// Apply the index set only once the scene's buffers exist (the element buffer is created by
// upload). Re-apply after a fresh upload so the indices survive a scene swap or context loss.
if
(
scene
.
hasData
()
&&
lastIndices
!=
null
&&
(
newIndices
!=
null
||
uploadedNow
))
{
scene
.
setIndexSet
(
gl
,
lastIndices
);
}
if
(
highlightsDirty
)
{
overlay
.
setPoints
(
gl
,
hlPoints
,
hlPointColors
);
overlay
.
setLines
(
gl
,
hlLines
,
hlLineColors
);
highlightsDirty
=
false
;
}
camera
.
setAspect
(
h
==
0
?
1
f
:
(
float
)
w
/
h
);
float
[]
mvp
=
camera
.
viewProjection
();
gl
.
glBindFramebuffer
(
GL
.
GL_FRAMEBUFFER
,
defaultFbo
);
gl
.
glViewport
(
0
,
0
,
w
,
h
);
gl
.
glClearColor
((
float
)
BACKGROUND
.
getRed
(),
(
float
)
BACKGROUND
.
getGreen
(),
(
float
)
BACKGROUND
.
getBlue
(),
1
f
);
gl
.
glClear
(
GL
.
GL_COLOR_BUFFER_BIT
|
GL
.
GL_DEPTH_BUFFER_BIT
);
if
(
cull
)
{
gl
.
glEnable
(
GL
.
GL_CULL_FACE
);
}
else
{
gl
.
glDisable
(
GL
.
GL_CULL_FACE
);
}
gl
.
glPolygonMode
(
GL
.
GL_FRONT_AND_BACK
,
wireframe
?
GL3
.
GL_LINE
:
GL3
.
GL_FILL
);
sceneProgram
.
use
(
gl
);
gl
.
glUniformMatrix4fv
(
sceneProgram
.
uniform
(
gl
,
"uMVP"
),
1
,
false
,
mvp
,
0
);
scene
.
draw
(
gl
);
// wireframe applies to the geometry only; highlight markers always render filled
gl
.
glPolygonMode
(
GL
.
GL_FRONT_AND_BACK
,
GL3
.
GL_FILL
);
// highlight markers sit at the same depth as geometry edges (and the wireframe lines); use
// LEQUAL so the later-drawn markers win depth ties instead of being rejected by GL_LESS
gl
.
glDepthFunc
(
GL
.
GL_LEQUAL
);
// edge markers reuse the flat scene program (pos + colour)
overlay
.
drawLines
(
gl
,
lineScale
);
// vertex markers use the sphere-impostor program (geometry shader -> camera-facing quads)
pointProgram
.
use
(
gl
);
gl
.
glUniformMatrix4fv
(
pointProgram
.
uniform
(
gl
,
"uMVP"
),
1
,
false
,
mvp
,
0
);
gl
.
glUniform2f
(
pointProgram
.
uniform
(
gl
,
"uViewport"
),
(
float
)
w
,
(
float
)
h
);
gl
.
glUniform1f
(
pointProgram
.
uniform
(
gl
,
"uPointSize"
),
pointScale
);
overlay
.
drawPoints
(
gl
);
gl
.
glDepthFunc
(
GL
.
GL_LESS
);
// restore for the next frame's scene pass
double
[]
req
=
pickRequest
;
if
(
req
!=
null
&&
currentScene
!=
null
)
{
picking
.
resize
(
gl
,
w
,
h
);
picking
.
bind
(
gl
);
gl
.
glClearColor
(
0
,
0
,
0
,
1
);
gl
.
glClear
(
GL
.
GL_COLOR_BUFFER_BIT
|
GL
.
GL_DEPTH_BUFFER_BIT
);
gl
.
glPolygonMode
(
GL
.
GL_FRONT_AND_BACK
,
GL3
.
GL_FILL
);
pickProgram
.
use
(
gl
);
gl
.
glUniformMatrix4fv
(
pickProgram
.
uniform
(
gl
,
"uMVP"
),
1
,
false
,
mvp
,
0
);
scene
.
draw
(
gl
);
// click coords are logical (JavaFX) px; the picking buffer is physical px. Scale by the
// framebuffer/canvas ratio so HiDPI displays read the pixel actually under the cursor.
double
cw
=
canvas
.
getWidth
();
double
ch
=
canvas
.
getHeight
();
float
sx
=
cw
>
0
?
(
float
)
(
w
/
cw
)
:
1
f
;
float
sy
=
ch
>
0
?
(
float
)
(
h
/
ch
)
:
1
f
;
int
px
=
Math
.
round
((
float
)
req
[
0
]
*
sx
);
int
py
=
Math
.
round
((
float
)
req
[
1
]
*
sy
);
int
id
=
picking
.
readId
(
gl
,
px
,
py
);
Object
target
=
currentScene
.
pickRegistry
().
resolve
(
id
);
Consumer
<
Object
>
cb
=
pickCallback
;
pickRequest
=
null
;
pickCallback
=
null
;
gl
.
glBindFramebuffer
(
GL
.
GL_FRAMEBUFFER
,
defaultFbo
);
if
(
cb
!=
null
)
{
Platform
.
runLater
(()
->
cb
.
accept
(
target
));
}
}
}
// ---- input ----
private
void
installInputHandlers
()
{
canvas
.
setOnMousePressed
(
e
->
{
lastX
=
e
.
getX
();
lastY
=
e
.
getY
();
});
canvas
.
setOnMouseDragged
(
e
->
{
double
dx
=
e
.
getX
()
-
lastX
;
double
dy
=
e
.
getY
()
-
lastY
;
lastX
=
e
.
getX
();
lastY
=
e
.
getY
();
if
(
e
.
getButton
()
==
MouseButton
.
PRIMARY
)
{
camera
.
orbit
(
dx
/
3.0
,
dy
/
3.0
);
}
else
if
(
e
.
getButton
()
==
MouseButton
.
SECONDARY
)
{
double
speed
=
Math
.
abs
(
camera
.
distance
())
/
1000.0
;
camera
.
pan
(-
dx
*
speed
,
dy
*
speed
);
}
requestRender
();
notifyInteraction
();
});
canvas
.
setOnScroll
(
e
->
{
camera
.
zoom
(
e
.
getDeltaY
()
<
0
?
1.05
:
0.95
);
requestRender
();
notifyInteraction
();
});
}
public
void
resetCamera
()
{
camera
.
reset
();
requestRender
();
}
private
void
notifyInteraction
()
{
if
(
interactionListener
!=
null
)
{
interactionListener
.
run
();
}
}
}
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/gl/GlScene.java
0 → 100644
View file @
854aab9b
package
de.hft.stuttgart.citydoctor2.gui.gl
;
import
com.jogamp.opengl.GL
;
import
com.jogamp.opengl.GL3
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.nio.FloatBuffer
;
import
java.nio.IntBuffer
;
/** GPU resources for the global scene mesh. Single VBO (pos+color+id), one index buffer redrawn per frame. */
public
class
GlScene
{
private
int
vao
=
0
;
private
int
vboPos
;
private
int
vboColor
;
private
int
vboId
;
private
int
ebo
;
private
int
indexCount
;
private
boolean
uploaded
;
public
void
upload
(
GL3
gl
,
SceneData
data
)
{
if
(!
uploaded
)
{
int
[]
ids
=
new
int
[
3
];
gl
.
glGenVertexArrays
(
1
,
ids
,
0
);
vao
=
ids
[
0
];
int
[]
buffers
=
new
int
[
4
];
gl
.
glGenBuffers
(
4
,
buffers
,
0
);
vboPos
=
buffers
[
0
];
vboColor
=
buffers
[
1
];
vboId
=
buffers
[
2
];
ebo
=
buffers
[
3
];
}
gl
.
glBindVertexArray
(
vao
);
uploadFloat
(
gl
,
vboPos
,
data
.
positions
(),
0
,
3
);
uploadFloat
(
gl
,
vboColor
,
data
.
colors
(),
1
,
3
);
uploadInt
(
gl
,
vboId
,
data
.
ids
(),
2
);
gl
.
glBindVertexArray
(
0
);
uploaded
=
true
;
}
private
void
uploadFloat
(
GL3
gl
,
int
vbo
,
float
[]
data
,
int
location
,
int
components
)
{
gl
.
glBindBuffer
(
GL
.
GL_ARRAY_BUFFER
,
vbo
);
FloatBuffer
fb
=
toFloatBuffer
(
data
);
gl
.
glBufferData
(
GL
.
GL_ARRAY_BUFFER
,
(
long
)
data
.
length
*
Float
.
BYTES
,
fb
,
GL
.
GL_STATIC_DRAW
);
gl
.
glEnableVertexAttribArray
(
location
);
gl
.
glVertexAttribPointer
(
location
,
components
,
GL
.
GL_FLOAT
,
false
,
0
,
0L
);
}
private
void
uploadInt
(
GL3
gl
,
int
vbo
,
int
[]
data
,
int
location
)
{
gl
.
glBindBuffer
(
GL
.
GL_ARRAY_BUFFER
,
vbo
);
IntBuffer
ib
=
toIntBuffer
(
data
);
gl
.
glBufferData
(
GL
.
GL_ARRAY_BUFFER
,
(
long
)
data
.
length
*
Integer
.
BYTES
,
ib
,
GL
.
GL_STATIC_DRAW
);
gl
.
glEnableVertexAttribArray
(
location
);
gl
.
glVertexAttribIPointer
(
location
,
1
,
GL3
.
GL_INT
,
0
,
0L
);
// integer attribute, not normalized
}
/** Re-uploads which triangles to draw (element indices). Cheap; called on filter/LOD changes. */
public
void
setIndexSet
(
GL3
gl
,
int
[]
indices
)
{
if
(!
uploaded
||
vao
==
0
)
{
// buffers not generated yet; the caller re-applies once the scene is uploaded
return
;
}
gl
.
glBindVertexArray
(
vao
);
gl
.
glBindBuffer
(
GL
.
GL_ELEMENT_ARRAY_BUFFER
,
ebo
);
IntBuffer
ib
=
toIntBuffer
(
indices
);
gl
.
glBufferData
(
GL
.
GL_ELEMENT_ARRAY_BUFFER
,
(
long
)
indices
.
length
*
Integer
.
BYTES
,
ib
,
GL
.
GL_DYNAMIC_DRAW
);
gl
.
glBindVertexArray
(
0
);
indexCount
=
indices
.
length
;
}
public
void
draw
(
GL3
gl
)
{
if
(
vao
==
0
||
indexCount
==
0
)
{
return
;
}
gl
.
glBindVertexArray
(
vao
);
gl
.
glDrawElements
(
GL
.
GL_TRIANGLES
,
indexCount
,
GL
.
GL_UNSIGNED_INT
,
0L
);
gl
.
glBindVertexArray
(
0
);
}
public
boolean
hasData
()
{
return
uploaded
;
}
/**
* Forgets GL handles after the context was lost/recreated, without issuing GL calls (the old
* handles belong to a dead context). The next {@link #upload} regenerates everything.
*/
public
void
markContextLost
()
{
vao
=
0
;
indexCount
=
0
;
uploaded
=
false
;
}
public
void
dispose
(
GL3
gl
)
{
if
(
vao
!=
0
)
{
gl
.
glDeleteVertexArrays
(
1
,
new
int
[]{
vao
},
0
);
gl
.
glDeleteBuffers
(
4
,
new
int
[]{
vboPos
,
vboColor
,
vboId
,
ebo
},
0
);
vao
=
0
;
uploaded
=
false
;
}
}
private
static
FloatBuffer
toFloatBuffer
(
float
[]
data
)
{
ByteBuffer
bb
=
ByteBuffer
.
allocateDirect
(
data
.
length
*
Float
.
BYTES
).
order
(
ByteOrder
.
nativeOrder
());
FloatBuffer
fb
=
bb
.
asFloatBuffer
();
fb
.
put
(
data
).
flip
();
return
fb
;
}
private
static
IntBuffer
toIntBuffer
(
int
[]
data
)
{
ByteBuffer
bb
=
ByteBuffer
.
allocateDirect
(
data
.
length
*
Integer
.
BYTES
).
order
(
ByteOrder
.
nativeOrder
());
IntBuffer
ib
=
bb
.
asIntBuffer
();
ib
.
put
(
data
).
flip
();
return
ib
;
}
}
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/gl/HighlightOverlay.java
0 → 100644
View file @
854aab9b
package
de.hft.stuttgart.citydoctor2.gui.gl
;
import
com.jogamp.opengl.GL
;
import
com.jogamp.opengl.GL3
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
import
java.nio.FloatBuffer
;
import
java.nio.IntBuffer
;
/**
* Dynamic overlay for highlight markers: points for vertices, lines for edges. Positions are in the same
* recentred world space as the scene. Colour rides in the color attribute; id attribute is 0.
*/
public
class
HighlightOverlay
{
private
int
pointVao
;
private
int
pointPos
;
private
int
pointColor
;
private
int
pointId
;
private
int
pointCount
;
private
int
lineVao
;
private
int
linePos
;
private
int
lineColor
;
private
int
lineId
;
private
int
lineVertexCount
;
private
boolean
created
;
public
void
init
(
GL3
gl
)
{
int
[]
v
=
new
int
[
2
];
gl
.
glGenVertexArrays
(
2
,
v
,
0
);
pointVao
=
v
[
0
];
lineVao
=
v
[
1
];
int
[]
b
=
new
int
[
6
];
gl
.
glGenBuffers
(
6
,
b
,
0
);
pointPos
=
b
[
0
];
pointColor
=
b
[
1
];
pointId
=
b
[
2
];
linePos
=
b
[
3
];
lineColor
=
b
[
4
];
lineId
=
b
[
5
];
created
=
true
;
}
/** points: xyz triplets; pointColors: rgb triplets (one per point). */
public
void
setPoints
(
GL3
gl
,
float
[]
points
,
float
[]
pointColors
)
{
pointCount
=
points
.
length
/
3
;
configure
(
gl
,
pointVao
,
pointPos
,
points
,
pointColor
,
pointColors
,
pointId
,
pointCount
);
}
/** lines: xyz pairs of endpoints (6 floats per segment); lineColors: rgb per line vertex. */
public
void
setLines
(
GL3
gl
,
float
[]
lines
,
float
[]
lineColors
)
{
lineVertexCount
=
lines
.
length
/
3
;
configure
(
gl
,
lineVao
,
linePos
,
lines
,
lineColor
,
lineColors
,
lineId
,
lineVertexCount
);
}
private
void
configure
(
GL3
gl
,
int
vao
,
int
posVbo
,
float
[]
pos
,
int
colVbo
,
float
[]
col
,
int
idVbo
,
int
vertexCount
)
{
gl
.
glBindVertexArray
(
vao
);
bindFloat
(
gl
,
posVbo
,
pos
,
0
,
3
);
bindFloat
(
gl
,
colVbo
,
col
,
1
,
3
);
// id attribute all zeros so overlay never matches uSelectedId
int
[]
zeros
=
new
int
[
vertexCount
];
gl
.
glBindBuffer
(
GL
.
GL_ARRAY_BUFFER
,
idVbo
);
gl
.
glBufferData
(
GL
.
GL_ARRAY_BUFFER
,
(
long
)
zeros
.
length
*
Integer
.
BYTES
,
toInt
(
zeros
),
GL
.
GL_DYNAMIC_DRAW
);
gl
.
glEnableVertexAttribArray
(
2
);
gl
.
glVertexAttribIPointer
(
2
,
1
,
GL3
.
GL_INT
,
0
,
0L
);
gl
.
glBindVertexArray
(
0
);
}
private
void
bindFloat
(
GL3
gl
,
int
vbo
,
float
[]
data
,
int
loc
,
int
comp
)
{
gl
.
glBindBuffer
(
GL
.
GL_ARRAY_BUFFER
,
vbo
);
gl
.
glBufferData
(
GL
.
GL_ARRAY_BUFFER
,
(
long
)
data
.
length
*
Float
.
BYTES
,
toFloat
(
data
),
GL
.
GL_DYNAMIC_DRAW
);
gl
.
glEnableVertexAttribArray
(
loc
);
gl
.
glVertexAttribPointer
(
loc
,
comp
,
GL
.
GL_FLOAT
,
false
,
0
,
0L
);
}
/** Draws the edge markers as lines. The caller must bind a program that reads pos+color. */
public
void
drawLines
(
GL3
gl
,
float
lineWidth
)
{
if
(!
created
||
lineVertexCount
==
0
)
{
return
;
}
gl
.
glLineWidth
(
lineWidth
);
gl
.
glBindVertexArray
(
lineVao
);
gl
.
glDrawArrays
(
GL
.
GL_LINES
,
0
,
lineVertexCount
);
gl
.
glBindVertexArray
(
0
);
}
/**
* Draws the vertex markers as GL_POINTS. The caller must bind the sphere-impostor program (a
* geometry shader expands each point into a camera-facing quad shaded as a sphere).
*/
public
void
drawPoints
(
GL3
gl
)
{
if
(!
created
||
pointCount
==
0
)
{
return
;
}
gl
.
glBindVertexArray
(
pointVao
);
gl
.
glDrawArrays
(
GL
.
GL_POINTS
,
0
,
pointCount
);
gl
.
glBindVertexArray
(
0
);
}
public
void
clear
()
{
pointCount
=
0
;
lineVertexCount
=
0
;
}
private
static
FloatBuffer
toFloat
(
float
[]
d
)
{
FloatBuffer
fb
=
ByteBuffer
.
allocateDirect
(
Math
.
max
(
1
,
d
.
length
)
*
Float
.
BYTES
)
.
order
(
ByteOrder
.
nativeOrder
()).
asFloatBuffer
();
fb
.
put
(
d
).
flip
();
return
fb
;
}
private
static
IntBuffer
toInt
(
int
[]
d
)
{
IntBuffer
ib
=
ByteBuffer
.
allocateDirect
(
Math
.
max
(
1
,
d
.
length
)
*
Integer
.
BYTES
)
.
order
(
ByteOrder
.
nativeOrder
()).
asIntBuffer
();
ib
.
put
(
d
).
flip
();
return
ib
;
}
}
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/gl/IndexSetBuilder.java
0 → 100644
View file @
854aab9b
package
de.hft.stuttgart.citydoctor2.gui.gl
;
import
java.util.List
;
import
java.util.function.Predicate
;
/** Builds the element index set (triangle vertex indices) for the triangles passing the filter. */
public
final
class
IndexSetBuilder
{
private
IndexSetBuilder
()
{}
public
static
int
[]
build
(
List
<
TriangleMeta
>
metas
,
Predicate
<
TriangleMeta
>
include
)
{
IntList
out
=
new
IntList
();
for
(
TriangleMeta
m
:
metas
)
{
if
(
include
.
test
(
m
))
{
int
base
=
m
.
triangleIndex
()
*
3
;
out
.
add
(
base
);
out
.
add
(
base
+
1
);
out
.
add
(
base
+
2
);
}
}
return
out
.
toArray
();
}
}
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/gl/IntList.java
0 → 100644
View file @
854aab9b
package
de.hft.stuttgart.citydoctor2.gui.gl
;
import
java.util.Arrays
;
/** Growable primitive int array. */
public
class
IntList
{
private
int
[]
data
;
private
int
size
;
public
IntList
()
{
this
(
1024
);
}
public
IntList
(
int
initialCapacity
)
{
data
=
new
int
[
Math
.
max
(
1
,
initialCapacity
)];
}
public
void
add
(
int
v
)
{
if
(
size
+
1
>
data
.
length
)
{
data
=
Arrays
.
copyOf
(
data
,
data
.
length
<<
1
);
}
data
[
size
++]
=
v
;
}
public
int
get
(
int
index
)
{
return
data
[
index
];
}
public
int
size
()
{
return
size
;
}
public
int
[]
toArray
()
{
return
Arrays
.
copyOf
(
data
,
size
);
}
}
Prev
1
2
3
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment