JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
StereoClientRenderer.java
Go to the documentation of this file.
1/**
2 * Copyright 2014 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.opengl.util.stereo;
29
30import com.jogamp.nativewindow.util.DimensionImmutable;
31import com.jogamp.nativewindow.util.RectangleImmutable;
32import com.jogamp.opengl.GL;
33import com.jogamp.opengl.GL2ES2;
34import com.jogamp.opengl.GLAutoDrawable;
35import com.jogamp.opengl.GLEventListener;
36
37import jogamp.opengl.GLDrawableHelper;
38import jogamp.opengl.GLDrawableHelper.GLEventListenerAction;
39
40import com.jogamp.opengl.FBObject;
41import com.jogamp.opengl.FBObject.Attachment;
42import com.jogamp.opengl.FBObject.TextureAttachment;
43import com.jogamp.opengl.FBObject.Attachment.Type;
44import com.jogamp.opengl.util.CustomGLEventListener;
45
46/**
47 * {@link StereoClientRenderer} utilizing {@link StereoDeviceRenderer}
48 * implementing {@link GLEventListener} for convenience.
49 * <p>
50 * See {@link StereoDeviceRenderer} notes about <a href="StereoDeviceRenderer.html#asymFOVRendering">Correct Asymmetric FOV Rendering</a>.
51 * <p>
52 * Implementation renders {@link StereoGLEventListener}
53 * using one or more {@link FBObject} according to {@link StereoDeviceRenderer#getTextureCount()}.
54 * </p>
55 */
56public class StereoClientRenderer implements GLEventListener {
57 private final GLDrawableHelper helper;
58 private final StereoDeviceRenderer deviceRenderer;
59 private final boolean ownsDevice;
60 private final FBObject[] fbos;
61 private final int magFilter;
62 private final int minFilter;
63
64 private int numSamples;
65 private final TextureAttachment[] fboTexs;
66
67 public StereoClientRenderer(final StereoDeviceRenderer deviceRenderer, final boolean ownsDevice,
68 final int magFilter, final int minFilter, final int numSamples) {
69 final int fboCount = deviceRenderer.getTextureCount();
70 if( 0 > fboCount || 2 < fboCount ) {
71 throw new IllegalArgumentException("fboCount must be within [0..2], has "+fboCount+", due to "+deviceRenderer);
72 }
73 this.helper = new GLDrawableHelper();
74 this.deviceRenderer = deviceRenderer;
75 this.ownsDevice = ownsDevice;
76 this.magFilter = magFilter;
77 this.minFilter = minFilter;
78
79 this.numSamples = numSamples;
80
81 this.fbos = new FBObject[fboCount];
82 for(int i=0; i<fboCount; i++) {
83 this.fbos[i] = new FBObject();
84 }
85 this.fboTexs = new TextureAttachment[fboCount];
86 }
87
88 private void initFBOs(final GL gl, final DimensionImmutable[] sizes) {
89 for(int i=0; i<fbos.length; i++) {
90 fbos[i].init(gl, sizes[i].getWidth(), sizes[i].getHeight(), numSamples);
91 if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) {
92 throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]);
93 }
94 numSamples = fbos[i].getNumSamples();
95
96 if(numSamples>0) {
97 fbos[i].attachColorbuffer(gl, 0, true); // MSAA requires alpha
99 final FBObject ssink = new FBObject();
100 {
101 ssink.init(gl, sizes[i].getWidth(), sizes[i].getHeight(), 0);
102 ssink.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE);
103 ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS);
104 }
105 fbos[i].setSamplingSink(ssink);
106 fbos[i].resetSamplingSink(gl); // validate
107 fboTexs[i] = fbos[i].getSamplingSink().getTextureAttachment();
108 } else {
109 fboTexs[i] = fbos[i].attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE);
110 fbos[i].attachRenderbuffer(gl, Type.DEPTH, FBObject.DEFAULT_BITS);
111 }
112 fbos[i].unbind(gl);
113 System.err.println("FBO["+i+"]: "+fbos[i]);
114 }
115
116 }
117
118 @SuppressWarnings("unused")
119 private void resetFBOs(final GL gl, final DimensionImmutable size) {
120 for(int i=0; i<fbos.length; i++) {
121 fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples);
122 if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) {
123 throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]);
124 }
125 numSamples = fbos[i].getNumSamples();
126 if(numSamples>0) {
127 fboTexs[i] = fbos[i].getSamplingSink().getTextureAttachment();
128 } else {
129 fboTexs[i] = fbos[i].getColorbuffer(0).getTextureAttachment();
130 }
131 }
132 }
133
134 public final StereoDeviceRenderer getStereoDeviceRenderer() { return deviceRenderer; }
135
136 public final void addGLEventListener(final StereoGLEventListener l) {
137 helper.addGLEventListener(l);
138 }
139 public final void removeGLEventListener(final StereoGLEventListener l) {
140 helper.removeGLEventListener(l);
141 }
142
143 @Override
144 public void init(final GLAutoDrawable drawable) {
145 final GL2ES2 gl = drawable.getGL().getGL2ES2();
146 deviceRenderer.init(gl);
147
148 // We will do some offscreen rendering, setup FBO...
149 final DimensionImmutable[] textureSize = deviceRenderer.getTextureCount() > 1 ?
150 deviceRenderer.getEyeSurfaceSize() :
151 new DimensionImmutable[] { deviceRenderer.getTotalSurfaceSize() };
152 initFBOs(gl, textureSize);
153 helper.init(drawable, false);
154
155 gl.setSwapInterval(1);
156 }
157
158 @Override
159 public void dispose(final GLAutoDrawable drawable) {
160 final GL2ES2 gl = drawable.getGL().getGL2ES2();
161 helper.disposeAllGLEventListener(drawable, false);
162 for(int i=0; i<fbos.length; i++) {
163 fbos[i].destroy(gl);
164 fboTexs[i] = null;
165 }
166 if( ownsDevice ) {
167 deviceRenderer.dispose(gl);
168 }
169 }
170
171 @Override
172 public void display(final GLAutoDrawable drawable) {
173 final GL2ES2 gl = drawable.getGL().getGL2ES2();
174
175 deviceRenderer.beginFrame(gl);
176
177 if(0 < numSamples) {
179 }
180
181 final int fboCount = fbos.length;
182 final int displayRepeatFlags;
183 if( 1 >= fboCount ) {
184 displayRepeatFlags = CustomGLEventListener.DISPLAY_DONTCLEAR;
185 } else {
186 displayRepeatFlags = 0;
187 }
188
189 final int[] eyeOrder = deviceRenderer.getDevice().getEyeRenderOrder();
190 final int eyeCount = eyeOrder.length;
191
192 final ViewerPose viewerPose = deviceRenderer.updateViewerPose();
193
194 if( 1 == fboCount ) {
195 fbos[0].bind(gl);
196 }
197
198 for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) {
199 final int eyeName = eyeOrder[eyeNum];
200 if( 1 < fboCount ) {
201 fbos[eyeName].bind(gl);
202 }
203
204 final StereoDeviceRenderer.Eye eye = deviceRenderer.getEye(eyeName);
205 final RectangleImmutable viewport = eye.getViewport();
206 gl.glViewport(viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight());
207
208 final int displayFlags = eyeNum > 0 ? CustomGLEventListener.DISPLAY_REPEAT | displayRepeatFlags : 0;
209 final GLEventListenerAction reshapeDisplayAction = new GLEventListenerAction() {
210 public void run(final GLAutoDrawable drawable, final GLEventListener listener) {
211 final StereoGLEventListener sl = (StereoGLEventListener) listener;
212 sl.reshapeForEye(drawable, viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight(),
213 eye.getEyeParameter(), viewerPose);
214 sl.display(drawable, displayFlags);
215 } };
216 helper.runForAllGLEventListener(drawable, reshapeDisplayAction);
217
218 if( 1 < fboCount ) {
219 fbos[eyeName].unbind(gl);
220 }
221 }
222
223 if( 1 == fboCount ) {
224 fbos[0].unbind(gl);
225 }
226 // restore viewport
227 gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
228
229 if( deviceRenderer.ppAvailable() ) {
230 deviceRenderer.ppBegin(gl);
231 if( 1 == fboCount ) {
232 fbos[0].use(gl, fboTexs[0]);
233 for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) {
234 deviceRenderer.ppOneEye(gl, eyeOrder[eyeNum]);
235 }
236 fbos[0].unuse(gl);
237 } else {
238 for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) {
239 final int eyeName = eyeOrder[eyeNum];
240 fbos[eyeName].use(gl, fboTexs[eyeName]);
241 deviceRenderer.ppOneEye(gl, eyeName);
242 fbos[eyeName].unuse(gl);
243 }
244 }
245 deviceRenderer.ppEnd(gl);
246 }
247
248 if( !drawable.getAutoSwapBufferMode() ) {
249 drawable.swapBuffers();
250 }
251 deviceRenderer.endFrame(gl);
252 }
253
254 @Override
255 public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
256 if( !drawable.getAutoSwapBufferMode() ) {
257 final GL2ES2 gl = drawable.getGL().getGL2ES2();
258 gl.glViewport(0, 0, width, height);
259 }
260 }
261}
Core utility class simplifying usage of framebuffer objects (FBO) with all GLProfiles.
Definition: FBObject.java:53
final void attachRenderbuffer(final GL gl, final Attachment.Type atype, final int reqBits)
Attaches one depth, stencil or packed-depth-stencil buffer to this FBO's instance,...
Definition: FBObject.java:1691
final void bind(final GL gl)
Bind this FBO, i.e.
Definition: FBObject.java:2540
final TextureAttachment attachTexture2D(final GL gl, final int attachmentPoint, final boolean alpha)
Attaches a Colorbuffer, i.e.
Definition: FBObject.java:1387
final int getNumSamples()
Returns the number of samples for multisampling (MSAA).
Definition: FBObject.java:2757
FBObject setSamplingSink(final FBObject newSamplingSink)
Setting this FBO sampling sink.
Definition: FBObject.java:2505
final void destroy(final GL gl)
Definition: FBObject.java:2253
final void unuse(final GL gl)
Unbind texture, ie bind 'non' texture 0.
Definition: FBObject.java:2684
final void use(final GL gl, final TextureAttachment ta)
Synchronize the sampling sink and bind the given TextureAttachment, if not null.
Definition: FBObject.java:2672
final Colorbuffer getSamplingSink()
Return the multisampling Colorbuffer sink, if using multisampling.
Definition: FBObject.java:2782
static final int DEFAULT_BITS
Request default bit count for depth- or stencil buffer (depth 24 bits, stencil 8 bits),...
Definition: FBObject.java:1648
final boolean resetSamplingSink(final GL gl)
Manually validates the MSAA sampling sink, if used.
Definition: FBObject.java:2345
final ColorAttachment attachColorbuffer(final GL gl, final int attachmentPoint, final boolean alpha)
Attaches a newly created and initialized Colorbuffer, i.e.
Definition: FBObject.java:1496
final void unbind(final GL gl)
Unbind this FBO, i.e.
Definition: FBObject.java:2563
final boolean reset(final GL gl, int newWidth, int newHeight, int newSamples)
Resets this FBO's instance.
Definition: FBObject.java:1140
void init(final GL gl, final int newWidth, final int newHeight, final int newSamples)
Initializes this FBO's instance.
Definition: FBObject.java:1008
final Colorbuffer getColorbuffer(final int attachmentPoint)
Return the Colorbuffer attachment at attachmentPoint if it is attached to this FBO,...
Definition: FBObject.java:886
StereoClientRenderer utilizing StereoDeviceRenderer implementing GLEventListener for convenience.
StereoClientRenderer(final StereoDeviceRenderer deviceRenderer, final boolean ownsDevice, final int magFilter, final int minFilter, final int numSamples)
final void addGLEventListener(final StereoGLEventListener l)
void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height)
Called by the drawable during the first repaint after the component has been resized.
void display(final GLAutoDrawable drawable)
Called by the drawable to initiate OpenGL rendering by the client.
void init(final GLAutoDrawable drawable)
Called by the drawable immediately after the OpenGL context is initialized.
final void removeGLEventListener(final StereoGLEventListener l)
void dispose(final GLAutoDrawable drawable)
Notifies the listener to perform the release of all OpenGL resources per GLContext,...
position and orientation of viewer.
Definition: ViewerPose.java:36
Immutable Dimension Interface, consisting of it's read only components:
Immutable Rectangle interface, with its position on the top-left.
int getX()
x-position, left of rectangle.
int getY()
y-position, top of rectangle.
TextureAttachment getTextureAttachment()
Casts this object to a TextureAttachment reference, see isTextureAttachment().
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
GL getGL()
Returns the GL pipeline object this GLAutoDrawable uses.
boolean getAutoSwapBufferMode()
Indicates whether automatic buffer swapping is enabled for this drawable.
GL2ES2 getGL2ES2()
Casts this object to the GL2ES2 interface.
void setSwapInterval(int interval)
Set the swap interval of the current context and attached onscreen GLDrawable.
int getSurfaceWidth()
Returns the width of this GLDrawable's surface client area in pixel units.
int getSurfaceHeight()
Returns the height of this GLDrawable's surface client area in pixel units.
void swapBuffers()
Swaps the front and back buffers of this drawable.
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.
static final int GL_MULTISAMPLE
Common in ES1, GL2 and GL3.
Definition: GL.java:1249
void glEnable(int cap)
Entry point to C language function: void {@native glEnable}(GLenum cap) Part of GL_ES_VERSION_2_0,...
void glViewport(int x, int y, int width, int height)
Entry point to C language function: void {@native glViewport}(GLint x, GLint y, GLsizei width,...
static final int GL_CLAMP_TO_EDGE
GL_ES_VERSION_2_0, GL_VERSION_1_2, GL_VERSION_ES_1_0, GL_SGIS_texture_edge_clamp Alias for: GL_CLAMP_...
Definition: GL.java:775
Extended GLEventListener interface supporting more fine grained control over the implementation.
void display(final GLAutoDrawable drawable, final int flags)
Extended display method, allowing to pass a display flag, e.g.
static final int DISPLAY_DONTCLEAR
display flag: Do not clear any target buffer, e.g.
Stereoscopic device rendering interface.
void endFrame(final GL gl)
Notifying that the frame has been rendered completely.
DimensionImmutable[] getEyeSurfaceSize()
Returns the surface size for each eye's a single image in pixel units.
Eye getEye(final int eyeNum)
Returns the Eye instance for the denoted eyeNum.
void init(final GL gl)
Initialize OpenGL related resources.
void ppEnd(final GL gl)
End stereoscopic post-processing, see ppAvailable().
ViewerPose updateViewerPose()
Updates the ViewerPose and returns it.
void beginFrame(final GL gl)
Notifying that a new frame is about to start.
boolean ppAvailable()
Returns true if stereoscopic post-processing is required and available, otherwise false.
void ppOneEye(final GL gl, final int eyeNum)
Performs stereoscopic post-processing for one eye, see ppAvailable().
void ppBegin(final GL gl)
Begin stereoscopic post-processing, see ppAvailable().
StereoDevice getDevice()
Returns the StereoDevice of this StereoDeviceRenderer instance.
int getTextureCount()
Returns the used texture-image count for post-processing, see ppAvailable().
void dispose(final GL gl)
Release all OpenGL related resources.
DimensionImmutable getTotalSurfaceSize()
Returns the total surface size required for the complete images in pixel units.
int[] getEyeRenderOrder()
Returns an array of the preferred eye rendering order.
Extended GLEventListener and CustomGLEventListener interface supporting stereoscopic client rendering...
void reshapeForEye(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height, final EyeParameter eyeParam, final ViewerPose viewerPose)
Stereo capable specialization of reshape(GLAutoDrawable, int, int, int, int) for one StereoDeviceRend...