add QuiverRender to plot 3D quivers with VBO

This commit is contained in:
wyq 2022-12-26 15:28:22 +08:00
parent 6d795bbad3
commit 80475bba09
10 changed files with 349 additions and 63 deletions

View File

@ -5869,7 +5869,7 @@ public class GraphicFactory {
wa.v = v;
wa.w = w;
wa.scale = scale;
wa.setHeadWith(headWidth);
wa.setHeadWidth(headWidth);
wa.setHeadLength(headLength);
wa.setPoint(aPoint);
if (cdata == null) {

View File

@ -2508,46 +2508,39 @@ public class GLPlot extends Plot {
}
break;
case POLYLINE_Z:
boolean useRender = true;
/*ColorBreak cb = graphic.getGraphicN(0).getLegend();
if (cb instanceof StreamlineBreak) {
if (graphic.getGraphicN(0).getShape() instanceof PipeShape)
useRender = false;
} else if (cb instanceof ColorBreakCollection) {
if (((ColorBreakCollection) cb).get(0) instanceof StreamlineBreak) {
if (graphic.getGraphicN(0).getShape() instanceof PipeShape)
useRender = false;
}
}*/
if (useRender) {
if (graphic.getGraphicN(0).getShape() instanceof PipeShape) {
if (!this.renderMap.containsKey(graphic)) {
renderMap.put(graphic, new PipeRender(gl, (GraphicCollection3D) graphic));
}
PipeRender pipeRender = (PipeRender) renderMap.get(graphic);
pipeRender.setTransform(this.transform, this.alwaysUpdateBuffers);
pipeRender.setOrthographic(this.orthographic);
pipeRender.setLighting(this.lighting);
pipeRender.updateMatrix();
pipeRender.draw();
} else {
if (!this.renderMap.containsKey(graphic)) {
renderMap.put(graphic, new LineRender(gl, (GraphicCollection3D) graphic));
}
LineRender lineRender = (LineRender) renderMap.get(graphic);
lineRender.setTransform(this.transform, this.alwaysUpdateBuffers);
lineRender.setOrthographic(this.orthographic);
lineRender.setLighting(this.lighting);
lineRender.updateMatrix();
lineRender.draw();
if (graphic.getGraphicN(0).getShape() instanceof PipeShape) {
if (!this.renderMap.containsKey(graphic)) {
renderMap.put(graphic, new PipeRender(gl, (GraphicCollection3D) graphic));
}
PipeRender pipeRender = (PipeRender) renderMap.get(graphic);
pipeRender.setTransform(this.transform, this.alwaysUpdateBuffers);
pipeRender.setOrthographic(this.orthographic);
pipeRender.setLighting(this.lighting);
pipeRender.updateMatrix();
pipeRender.draw();
} else {
for (int i = 0; i < graphic.getNumGraphics(); i++) {
Graphic gg = graphic.getGraphicN(i);
this.drawGraphic(gl, gg);
if (!this.renderMap.containsKey(graphic)) {
renderMap.put(graphic, new LineRender(gl, (GraphicCollection3D) graphic));
}
LineRender lineRender = (LineRender) renderMap.get(graphic);
lineRender.setTransform(this.transform, this.alwaysUpdateBuffers);
lineRender.setOrthographic(this.orthographic);
lineRender.setLighting(this.lighting);
lineRender.updateMatrix();
lineRender.draw();
}
break;
case WIND_ARROW:
if (!this.renderMap.containsKey(graphic)) {
renderMap.put(graphic, new QuiverRender(gl, (GraphicCollection3D) graphic));
}
QuiverRender quiverRender = (QuiverRender) renderMap.get(graphic);
quiverRender.setTransform(this.transform, this.alwaysUpdateBuffers);
quiverRender.setOrthographic(this.orthographic);
quiverRender.setLighting(this.lighting);
quiverRender.updateMatrix();
quiverRender.draw();
break;
default:
for (int i = 0; i < graphic.getNumGraphics(); i++) {
Graphic gg = graphic.getGraphicN(i);
@ -3745,7 +3738,7 @@ public class GLPlot extends Plot {
//float coneHgt = coneFractionAxially * norm_of_v;
//float coneRadius = coneFractionRadially * norm_of_v;
float coneRadius = shape.getHeadLength() * 0.02f;
float coneHgt = shape.getHeadWith() * 0.02f;
float coneHgt = shape.getHeadWidth() * 0.02f;
// Set location of arrowhead to be at the startpoint of the line
float[] vConeLocation = {x2, y2, z2};

View File

@ -3727,7 +3727,7 @@ public class Plot3DGL extends Plot implements GLEventListener {
//float coneHgt = coneFractionAxially * norm_of_v;
//float coneRadius = coneFractionRadially * norm_of_v;
float coneRadius = shape.getHeadLength() * 0.02f;
float coneHgt = shape.getHeadWith() * 0.02f;
float coneHgt = shape.getHeadWidth() * 0.02f;
// Set location of arrowhead to be at the startpoint of the line
float[] vConeLocation = {x2, y2, z2};

View File

@ -0,0 +1,268 @@
package org.meteoinfo.chart.render.jogl;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.util.GLBuffers;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.meteoinfo.chart.graphic.GraphicCollection3D;
import org.meteoinfo.chart.graphic.cylinder.Cylinder;
import org.meteoinfo.chart.jogl.Program;
import org.meteoinfo.chart.jogl.Transform;
import org.meteoinfo.chart.jogl.Utils;
import org.meteoinfo.geometry.graphic.Graphic;
import org.meteoinfo.geometry.legend.*;
import org.meteoinfo.geometry.shape.PointZ;
import org.meteoinfo.geometry.shape.PolylineZShape;
import org.meteoinfo.geometry.shape.WindArrow3D;
import org.meteoinfo.math.Matrix4f;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
public class QuiverRender extends JOGLGraphicRender {
private GraphicCollection3D graphics;
private Program program;
private IntBuffer vbo;
private int quiverNumber;
private float lineWidth;
private float[] vertexPosition;
private float[] vertexColor;
private int sizePosition;
private int sizeColor;
private IntBuffer vboCone;
private float[] coneVertexPosition;
private float[] coneVertexNormal;
private float[] coneVertexColor;
private int[] coneVertexIndices;
private int sizeConePosition;
private int sizeConeNormal;
private int sizeConeColor;
/**
* Constructor
*
* @param gl The JOGL GL2 object
*/
public QuiverRender(GL2 gl) {
super(gl);
if (useShader) {
try {
this.compileShaders();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Constructor
* @param gl The JOGL GL2 object
* @param graphics The quiver graphics
*/
public QuiverRender(GL2 gl, GraphicCollection3D graphics) {
this(gl);
this.graphics = graphics;
this.quiverNumber = graphics.getNumGraphics();
PointBreak pb = (PointBreak) graphics.getGraphicN(0).getLegend();
this.lineWidth = pb.getOutlineSize();
}
private void updateVertexArrays() {
vertexPosition = new float[quiverNumber * 6];
vertexColor = new float[quiverNumber * 8];
List<Vector3f> vertexPositionList = new ArrayList<>();
List<Vector3f> vertexNormalList = new ArrayList<>();
List<Vector4f> vertexColorList = new ArrayList<>();
List<Integer> vertexIndices = new ArrayList<>();
for (int i = 0, pi = 0, ci = 0; i < quiverNumber; i++, pi+=6, ci+=8) {
Graphic graphic = graphics.getGraphicN(i);
WindArrow3D shape = (WindArrow3D) graphic.getShape();
PointBreak pb = (PointBreak) graphic.getLegend();
PointZ sp = (PointZ) shape.getPoint();
PointZ ep = (PointZ) shape.getEndPoint();
Vector3f v1 = transform.transform((float) sp.X, (float) sp.Y, (float) sp.Z);
Vector3f v2 = transform.transform((float) ep.X, (float) ep.Y, (float) ep.Z);
float[] color = pb.getColor().getRGBComponents(null);
vertexPosition[pi] = v1.x;
vertexPosition[pi + 1] = v1.y;
vertexPosition[pi + 2] = v1.z;
vertexPosition[pi + 3] = v2.x;
vertexPosition[pi + 4] = v2.y;
vertexPosition[pi + 5] = v2.z;
System.arraycopy(color, 0, vertexColor, ci, 4);
System.arraycopy(color, 0, vertexColor, ci + 4, 4);
Cylinder cylinder = new Cylinder(shape.getHeadWidth() * 0.02f,
0, shape.getHeadLength() * 0.02f, 8, 1, true);
Matrix4f matrix = new Matrix4f();
matrix.lookAt(v2.sub(v1, new Vector3f()));
matrix.translate(v2);
List<Vector3f> vertices = cylinder.getVertices();
int n = vertices.size();
int nAdded = vertexPositionList.size();
for (Vector3f v : vertices) {
vertexPositionList.add(matrix.mul(v));
}
List<Vector3f> normals = cylinder.getNormals();
for (Vector3f v : normals) {
vertexNormalList.add(matrix.mul(v));
}
for (int j = 0; j < n; j++) {
vertexColorList.add(new Vector4f(color));
}
if (nAdded == 0) {
vertexIndices.addAll(cylinder.getIndices());
} else {
for (int idx : cylinder.getIndices()) {
vertexIndices.add(idx + nAdded);
}
}
}
int n = vertexPositionList.size();
this.coneVertexPosition = new float[n * 3];
this.coneVertexNormal = new float[n * 3];
this.coneVertexColor = new float[n * 4];
Vector3f v;
Vector4f v4;
for (int i = 0, j = 0, k = 0; i < n; i++, j+=3, k+=4) {
v = vertexPositionList.get(i);
coneVertexPosition[j] = v.x;
coneVertexPosition[j + 1] = v.y;
coneVertexPosition[j + 2] = v.z;
v = vertexNormalList.get(i);
coneVertexNormal[j] = v.x;
coneVertexNormal[j + 1] = v.y;
coneVertexNormal[j + 2] = v.z;
v4 = vertexColorList.get(i);
coneVertexColor[k] = v4.x;
coneVertexColor[k + 1] = v4.y;
coneVertexColor[k + 2] = v4.z;
coneVertexColor[k + 3] = v4.w;
}
coneVertexIndices = vertexIndices.stream().mapToInt(Integer::intValue).toArray();
}
void compileShaders() throws Exception {
String vertexShaderCode = Utils.loadResource("/shaders/surface/vertex.vert");
String fragmentShaderCode = Utils.loadResource("/shaders/surface/surface.frag");
program = new Program("surface", vertexShaderCode, fragmentShaderCode);
}
@Override
public void setTransform(Transform transform, boolean alwaysUpdateBuffers) {
boolean updateBuffer = true;
if (!alwaysUpdateBuffers && this.transform != null && this.transform.equals(transform))
updateBuffer = false;
super.setTransform((Transform) transform.clone());
if (updateBuffer) {
this.updateVertexArrays();
FloatBuffer vertexBuffer = GLBuffers.newDirectFloatBuffer(vertexPosition);
sizePosition = vertexBuffer.capacity() * Float.BYTES;
FloatBuffer colorBuffer = GLBuffers.newDirectFloatBuffer(vertexColor);
sizeColor = colorBuffer.capacity() * Float.BYTES;
int totalSize = sizePosition + sizeColor;
vbo = GLBuffers.newDirectIntBuffer(1);
gl.glGenBuffers(1, vbo);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo.get(0));
gl.glBufferData(GL.GL_ARRAY_BUFFER, totalSize, null, GL.GL_STATIC_DRAW);
gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, sizePosition, vertexBuffer);
gl.glBufferSubData(GL.GL_ARRAY_BUFFER, sizePosition, sizeColor, colorBuffer);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
FloatBuffer coneVertexBuffer = GLBuffers.newDirectFloatBuffer(coneVertexPosition);
sizeConePosition = coneVertexBuffer.capacity() * Float.BYTES;
FloatBuffer coneNormalBuffer = GLBuffers.newDirectFloatBuffer(coneVertexNormal);
sizeConeNormal = coneNormalBuffer.capacity() * Float.BYTES;
FloatBuffer coneColorBuffer = GLBuffers.newDirectFloatBuffer(coneVertexColor);
sizeConeColor = coneColorBuffer.capacity() * Float.BYTES;
vboCone = GLBuffers.newDirectIntBuffer(2);
gl.glGenBuffers(2, vboCone);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboCone.get(0));
gl.glBufferData(GL.GL_ARRAY_BUFFER, sizeConePosition + sizeConeNormal + sizeConeColor,
null, GL.GL_STATIC_DRAW);
gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, sizeConePosition, coneVertexBuffer);
gl.glBufferSubData(GL.GL_ARRAY_BUFFER, sizeConePosition, sizeConeNormal, coneNormalBuffer);
gl.glBufferSubData(GL.GL_ARRAY_BUFFER, sizeConePosition + sizeConeNormal, sizeConeColor,
coneColorBuffer);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
IntBuffer coneIndexBuffer = GLBuffers.newDirectIntBuffer(coneVertexIndices);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, vboCone.get(1));
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, coneIndexBuffer.capacity() * Integer.BYTES,
coneIndexBuffer, GL.GL_STATIC_DRAW);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
void setUniforms() {
}
@Override
public void draw() {
if (useShader) { // not working now
program.use(gl);
setUniforms();
gl.glUseProgram(0);
} else {
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo.get(0));
// enable vertex arrays
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
gl.glEnableClientState(GL2.GL_COLOR_ARRAY);
gl.glColorPointer(4, GL.GL_FLOAT, 0, sizePosition);
boolean lightEnabled = this.lighting.isEnable();
if (lightEnabled) {
this.lighting.stop(gl);
}
gl.glLineWidth(this.lineWidth * this.dpiScale);
gl.glDrawArrays(GL.GL_LINES, 0, quiverNumber);
if (lightEnabled) {
this.lighting.start(gl);
}
// draw wind arrow
gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL2.GL_COLOR_ARRAY);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboCone.get(0));
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, vboCone.get(1));
// enable vertex arrays
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
gl.glEnableClientState(GL2.GL_NORMAL_ARRAY);
gl.glNormalPointer(GL.GL_FLOAT, 0, sizeConePosition);
gl.glEnableClientState(GL2.GL_COLOR_ARRAY);
gl.glColorPointer(4, GL.GL_FLOAT, 0, sizeConePosition + sizeConeNormal);
gl.glDrawElements(GL2.GL_TRIANGLES, coneVertexIndices.length, GL.GL_UNSIGNED_INT, 0);
gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL2.GL_NORMAL_ARRAY);
gl.glDisableClientState(GL2.GL_COLOR_ARRAY);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
}

View File

@ -17,7 +17,7 @@ public class WindArrow3D extends PointZShape {
public double v;
public double w;
public float scale = 1;
private float headWith = 1;
private float headWidth = 1;
private float headLength = 2.5f;
// </editor-fold>
@ -40,16 +40,16 @@ public class WindArrow3D extends PointZShape {
* Get head width
* @return Head width
*/
public float getHeadWith() {
return this.headWith;
public float getHeadWidth() {
return this.headWidth;
}
/**
* Set head width
* @param value Head width
*/
public void setHeadWith(float value) {
this.headWith = value;
public void setHeadWidth(float value) {
this.headWidth = value;
}
/**

View File

@ -1,34 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MeteoInfo File="milconfig.xml" Type="configurefile">
<Path OpenPath="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\quiver">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math\optimize"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math\stats"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo\calc"/>
<Path OpenPath="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\contour">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\isosurface"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\plot"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\volume"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\streamplot"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\streamslice"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map\topology"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\LaSW"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\LaSW\airship"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\quiver"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\city"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\bar"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\contour"/>
</Path>
<File>
<OpenedFiles>
<OpenedFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\mainGUI.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\streamplot\streamplot_pipe_2.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\quiver\quiver3_1.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\quiver\quiver3_2.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\bar\bar_1.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\contour\contourfsclice_1.py"/>
</OpenedFiles>
<RecentFiles>
<RecentFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\mainGUI.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\streamplot\streamplot_pipe_2.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\quiver\quiver3_1.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\quiver\quiver3_2.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\bar\bar_1.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\contour\contourfsclice_1.py"/>
</RecentFiles>
</File>
<Font>
@ -36,5 +36,5 @@
</Font>
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
<Figure DoubleBuffering="true"/>
<Startup MainFormLocation="-7,-7" MainFormSize="1293,685"/>
<Startup MainFormLocation="-7,0" MainFormSize="1362,763"/>
</MeteoInfo>

View File

@ -337,7 +337,7 @@ class Axes3DGL(Axes3D):
"""
self._axes.addZAxis(x, y, left)
def bar(self, x, y, z, width=0.8, bottom=None, cylinder=False, **kwargs):
def bar(self, *args, **kwargs):
"""
Make a 3D bar plot of x, y and z, where x, y and z are sequence like objects of the same lengths.
@ -347,7 +347,7 @@ class Axes3DGL(Axes3D):
:param width: (*float*) Bar width.
:param cylinder: (*bool*) Is cylinder bar or rectangle bar.
:param bottom: (*bool*) Color of the points. Or z values.
:param color: (*Color*) Optional, the color of the bar faces.
:param facecolor: (*Color*) Optional, the color of the bar faces.
:param edgecolor: (*Color*) Optional, the color of the bar edge. Default is black color.
Edge line will not be plotted if ``edgecolor`` is ``None``.
:param linewidth: (*int*) Optional, width of bar edge.
@ -362,16 +362,41 @@ class Axes3DGL(Axes3D):
"""
#Add data series
label = kwargs.pop('label', 'S_0')
xdata = plotutil.getplotdata(x)
ydata = plotutil.getplotdata(y)
zdata = plotutil.getplotdata(z)
if len(args) == 1:
z = np.asarray(args[0])
if z.ndim == 1:
nx, = z.shape
y = np.array([0] * nx)
x = np.arange(nx)
else:
ny, nx = z.shape
x = np.arange(nx)
y = np.arange(ny)
x, y = np.meshgrid(x, y)
elif len(args) == 2:
x = np.asarray(args[0])
z = np.asarray(args[1])
nx, = x.shape
y = np.array([0] * nx)
else:
x = np.asarray(args[0])
y = np.asarray(args[1])
z = np.asarray(args[2])
xdata = x._array
ydata = y._array
zdata = z._array
autowidth = False
width = kwargs.pop('width', 0.8)
width = np.asarray(width)
bottom = kwargs.pop('bottom', None)
if not bottom is None:
bottom = plotutil.getplotdata(bottom)
cylinder = kwargs.pop('cylinder', False)
#Set plot data styles
fcobj = kwargs.pop('color', None)
if fcobj is None:

View File

@ -241,7 +241,7 @@ def bar(x, height, width=0.8, bottom=None, align='center', data=None, **kwargs):
return r
@_copy_docstring_and_deprecators(Axes3D.bar)
def bar3(x, y, z, width=0.8, bottom=None, cylinder=False, **kwargs):
def bar3(*args, **kwargs):
global g_axes
if g_axes is None:
g_axes = axes3d()
@ -249,7 +249,7 @@ def bar3(x, y, z, width=0.8, bottom=None, cylinder=False, **kwargs):
if not isinstance(g_axes, Axes3D):
g_axes = axes3d()
r = g_axes.bar(x, y, z, width, bottom, cylinder, **kwargs)
r = g_axes.bar(*args, **kwargs)
draw_if_interactive()
return r