1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.renderer.lwjgl; 33 34 import com.jme3.light.LightList; 35 import com.jme3.material.RenderState; 36 import com.jme3.material.RenderState.StencilOperation; 37 import com.jme3.material.RenderState.TestFunction; 38 import com.jme3.math.*; 39 import com.jme3.renderer.*; 40 import com.jme3.scene.Mesh; 41 import com.jme3.scene.Mesh.Mode; 42 import com.jme3.scene.VertexBuffer; 43 import com.jme3.scene.VertexBuffer.Format; 44 import com.jme3.scene.VertexBuffer.Type; 45 import com.jme3.scene.VertexBuffer.Usage; 46 import com.jme3.shader.Attribute; 47 import com.jme3.shader.Shader; 48 import com.jme3.shader.Shader.ShaderSource; 49 import com.jme3.shader.Shader.ShaderType; 50 import com.jme3.shader.Uniform; 51 import com.jme3.texture.FrameBuffer; 52 import com.jme3.texture.FrameBuffer.RenderBuffer; 53 import com.jme3.texture.Image; 54 import com.jme3.texture.Texture; 55 import com.jme3.texture.Texture.WrapAxis; 56 import com.jme3.util.BufferUtils; 57 import com.jme3.util.IntMap; 58 import com.jme3.util.IntMap.Entry; 59 import com.jme3.util.ListMap; 60 import com.jme3.util.NativeObjectManager; 61 import com.jme3.util.SafeArrayList; 62 import java.nio.*; 63 import java.util.EnumSet; 64 import java.util.List; 65 import java.util.logging.Level; 66 import java.util.logging.Logger; 67 import jme3tools.converters.MipMapGenerator; 68 import static org.lwjgl.opengl.ARBTextureMultisample.*; 69 import static org.lwjgl.opengl.EXTFramebufferBlit.*; 70 import static org.lwjgl.opengl.EXTFramebufferMultisample.*; 71 import static org.lwjgl.opengl.EXTFramebufferObject.*; 72 import static org.lwjgl.opengl.GL11.*; 73 import static org.lwjgl.opengl.GL12.*; 74 import static org.lwjgl.opengl.GL13.*; 75 import static org.lwjgl.opengl.GL14.*; 76 import static org.lwjgl.opengl.GL15.*; 77 import static org.lwjgl.opengl.GL20.*; 78 import org.lwjgl.opengl.*; 79 //import static org.lwjgl.opengl.ARBDrawInstanced.*; 80 81 public class LwjglRenderer implements Renderer { 82 83 private static final Logger logger = Logger.getLogger(LwjglRenderer.class.getName()); 84 private static final boolean VALIDATE_SHADER = false; 85 private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250); 86 private final StringBuilder stringBuf = new StringBuilder(250); 87 private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1); 88 private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16); 89 private final RenderContext context = new RenderContext(); 90 private final NativeObjectManager objManager = new NativeObjectManager(); 91 private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class); 92 // current state 93 private Shader boundShader; 94 private int initialDrawBuf, initialReadBuf; 95 private int glslVer; 96 private int vertexTextureUnits; 97 private int fragTextureUnits; 98 private int vertexUniforms; 99 private int fragUniforms; 100 private int vertexAttribs; 101 private int maxFBOSamples; 102 private int maxFBOAttachs; 103 private int maxMRTFBOAttachs; 104 private int maxRBSize; 105 private int maxTexSize; 106 private int maxCubeTexSize; 107 private int maxVertCount; 108 private int maxTriCount; 109 private int maxColorTexSamples; 110 private int maxDepthTexSamples; 111 private boolean tdc; 112 private FrameBuffer lastFb = null; 113 private FrameBuffer mainFbOverride = null; 114 private final Statistics statistics = new Statistics(); 115 private int vpX, vpY, vpW, vpH; 116 private int clipX, clipY, clipW, clipH; 117 118 public LwjglRenderer() { 119 } 120 121 protected void updateNameBuffer() { 122 int len = stringBuf.length(); 123 124 nameBuf.position(0); 125 nameBuf.limit(len); 126 for (int i = 0; i < len; i++) { 127 nameBuf.put((byte) stringBuf.charAt(i)); 128 } 129 130 nameBuf.rewind(); 131 } 132 133 public Statistics getStatistics() { 134 return statistics; 135 } 136 137 public EnumSet<Caps> getCaps() { 138 return caps; 139 } 140 141 @SuppressWarnings("fallthrough") 142 public void initialize() { 143 ContextCapabilities ctxCaps = GLContext.getCapabilities(); 144 if (ctxCaps.OpenGL20) { 145 caps.add(Caps.OpenGL20); 146 if (ctxCaps.OpenGL21) { 147 caps.add(Caps.OpenGL21); 148 if (ctxCaps.OpenGL30) { 149 caps.add(Caps.OpenGL30); 150 if (ctxCaps.OpenGL31) { 151 caps.add(Caps.OpenGL31); 152 if (ctxCaps.OpenGL32) { 153 caps.add(Caps.OpenGL32); 154 } 155 } 156 } 157 } 158 } 159 160 String versionStr = null; 161 if (ctxCaps.OpenGL20) { 162 versionStr = glGetString(GL_SHADING_LANGUAGE_VERSION); 163 } 164 if (versionStr == null || versionStr.equals("")) { 165 glslVer = -1; 166 throw new UnsupportedOperationException("GLSL and OpenGL2 is " 167 + "required for the LWJGL " 168 + "renderer!"); 169 } 170 171 // Fix issue in TestRenderToMemory when GL_FRONT is the main 172 // buffer being used. 173 initialDrawBuf = glGetInteger(GL_DRAW_BUFFER); 174 initialReadBuf = glGetInteger(GL_READ_BUFFER); 175 176 // XXX: This has to be GL_BACK for canvas on Mac 177 // Since initialDrawBuf is GL_FRONT for pbuffer, gotta 178 // change this value later on ... 179 // initialDrawBuf = GL_BACK; 180 // initialReadBuf = GL_BACK; 181 182 int spaceIdx = versionStr.indexOf(" "); 183 if (spaceIdx >= 1) { 184 versionStr = versionStr.substring(0, spaceIdx); 185 } 186 187 float version = Float.parseFloat(versionStr); 188 glslVer = (int) (version * 100); 189 190 switch (glslVer) { 191 default: 192 if (glslVer < 400) { 193 break; 194 } 195 196 // so that future OpenGL revisions wont break jme3 197 198 // fall through intentional 199 case 400: 200 case 330: 201 case 150: 202 caps.add(Caps.GLSL150); 203 case 140: 204 caps.add(Caps.GLSL140); 205 case 130: 206 caps.add(Caps.GLSL130); 207 case 120: 208 caps.add(Caps.GLSL120); 209 case 110: 210 caps.add(Caps.GLSL110); 211 case 100: 212 caps.add(Caps.GLSL100); 213 break; 214 } 215 216 if (!caps.contains(Caps.GLSL100)) { 217 logger.log(Level.WARNING, "Force-adding GLSL100 support, since OpenGL2 is supported."); 218 caps.add(Caps.GLSL100); 219 } 220 221 glGetInteger(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16); 222 vertexTextureUnits = intBuf16.get(0); 223 logger.log(Level.FINER, "VTF Units: {0}", vertexTextureUnits); 224 if (vertexTextureUnits > 0) { 225 caps.add(Caps.VertexTextureFetch); 226 } 227 228 glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16); 229 fragTextureUnits = intBuf16.get(0); 230 logger.log(Level.FINER, "Texture Units: {0}", fragTextureUnits); 231 232 glGetInteger(GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16); 233 vertexUniforms = intBuf16.get(0); 234 logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms); 235 236 glGetInteger(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16); 237 fragUniforms = intBuf16.get(0); 238 logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms); 239 240 glGetInteger(GL_MAX_VERTEX_ATTRIBS, intBuf16); 241 vertexAttribs = intBuf16.get(0); 242 logger.log(Level.FINER, "Vertex Attributes: {0}", vertexAttribs); 243 244 glGetInteger(GL_SUBPIXEL_BITS, intBuf16); 245 int subpixelBits = intBuf16.get(0); 246 logger.log(Level.FINER, "Subpixel Bits: {0}", subpixelBits); 247 248 glGetInteger(GL_MAX_ELEMENTS_VERTICES, intBuf16); 249 maxVertCount = intBuf16.get(0); 250 logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount); 251 252 glGetInteger(GL_MAX_ELEMENTS_INDICES, intBuf16); 253 maxTriCount = intBuf16.get(0); 254 logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount); 255 256 glGetInteger(GL_MAX_TEXTURE_SIZE, intBuf16); 257 maxTexSize = intBuf16.get(0); 258 logger.log(Level.FINER, "Maximum Texture Resolution: {0}", maxTexSize); 259 260 glGetInteger(GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16); 261 maxCubeTexSize = intBuf16.get(0); 262 logger.log(Level.FINER, "Maximum CubeMap Resolution: {0}", maxCubeTexSize); 263 264 if (ctxCaps.GL_ARB_color_buffer_float) { 265 // XXX: Require both 16 and 32 bit float support for FloatColorBuffer. 266 if (ctxCaps.GL_ARB_half_float_pixel) { 267 caps.add(Caps.FloatColorBuffer); 268 } 269 } 270 271 if (ctxCaps.GL_ARB_depth_buffer_float) { 272 caps.add(Caps.FloatDepthBuffer); 273 } 274 275 if (ctxCaps.GL_ARB_draw_instanced) { 276 caps.add(Caps.MeshInstancing); 277 } 278 279 if (ctxCaps.GL_ARB_fragment_program) { 280 caps.add(Caps.ARBprogram); 281 } 282 283 if (ctxCaps.GL_ARB_texture_buffer_object) { 284 caps.add(Caps.TextureBuffer); 285 } 286 287 if (ctxCaps.GL_ARB_texture_float) { 288 if (ctxCaps.GL_ARB_half_float_pixel) { 289 caps.add(Caps.FloatTexture); 290 } 291 } 292 293 if (ctxCaps.GL_ARB_vertex_array_object) { 294 caps.add(Caps.VertexBufferArray); 295 } 296 297 if (ctxCaps.GL_ARB_texture_non_power_of_two) { 298 caps.add(Caps.NonPowerOfTwoTextures); 299 } else { 300 logger.log(Level.WARNING, "Your graphics card does not " 301 + "support non-power-of-2 textures. " 302 + "Some features might not work."); 303 } 304 305 boolean latc = ctxCaps.GL_EXT_texture_compression_latc; 306 boolean atdc = ctxCaps.GL_ATI_texture_compression_3dc; 307 if (latc || atdc) { 308 caps.add(Caps.TextureCompressionLATC); 309 if (atdc && !latc) { 310 tdc = true; 311 } 312 } 313 314 if (ctxCaps.GL_EXT_packed_float) { 315 caps.add(Caps.PackedFloatColorBuffer); 316 if (ctxCaps.GL_ARB_half_float_pixel) { 317 // because textures are usually uploaded as RGB16F 318 // need half-float pixel 319 caps.add(Caps.PackedFloatTexture); 320 } 321 } 322 323 if (ctxCaps.GL_EXT_texture_array) { 324 caps.add(Caps.TextureArray); 325 } 326 327 if (ctxCaps.GL_EXT_texture_shared_exponent) { 328 caps.add(Caps.SharedExponentTexture); 329 } 330 331 if (ctxCaps.GL_EXT_framebuffer_object) { 332 caps.add(Caps.FrameBuffer); 333 334 glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16); 335 maxRBSize = intBuf16.get(0); 336 logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize); 337 338 glGetInteger(GL_MAX_COLOR_ATTACHMENTS_EXT, intBuf16); 339 maxFBOAttachs = intBuf16.get(0); 340 logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs); 341 342 if (ctxCaps.GL_EXT_framebuffer_multisample) { 343 caps.add(Caps.FrameBufferMultisample); 344 345 glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16); 346 maxFBOSamples = intBuf16.get(0); 347 logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples); 348 } 349 350 if (ctxCaps.GL_ARB_texture_multisample) { 351 caps.add(Caps.TextureMultisample); 352 353 glGetInteger(GL_MAX_COLOR_TEXTURE_SAMPLES, intBuf16); 354 maxColorTexSamples = intBuf16.get(0); 355 logger.log(Level.FINER, "Texture Multisample Color Samples: {0}", maxColorTexSamples); 356 357 glGetInteger(GL_MAX_DEPTH_TEXTURE_SAMPLES, intBuf16); 358 maxDepthTexSamples = intBuf16.get(0); 359 logger.log(Level.FINER, "Texture Multisample Depth Samples: {0}", maxDepthTexSamples); 360 } 361 362 if (ctxCaps.GL_ARB_draw_buffers) { 363 caps.add(Caps.FrameBufferMRT); 364 glGetInteger(ARBDrawBuffers.GL_MAX_DRAW_BUFFERS_ARB, intBuf16); 365 maxMRTFBOAttachs = intBuf16.get(0); 366 logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs); 367 } 368 } 369 370 if (ctxCaps.GL_ARB_multisample) { 371 glGetInteger(ARBMultisample.GL_SAMPLE_BUFFERS_ARB, intBuf16); 372 boolean available = intBuf16.get(0) != 0; 373 glGetInteger(ARBMultisample.GL_SAMPLES_ARB, intBuf16); 374 int samples = intBuf16.get(0); 375 logger.log(Level.FINER, "Samples: {0}", samples); 376 boolean enabled = glIsEnabled(ARBMultisample.GL_MULTISAMPLE_ARB); 377 if (samples > 0 && available && !enabled) { 378 glEnable(ARBMultisample.GL_MULTISAMPLE_ARB); 379 } 380 } 381 382 logger.log(Level.INFO, "Caps: {0}", caps); 383 } 384 385 public void invalidateState() { 386 context.reset(); 387 boundShader = null; 388 lastFb = null; 389 390 initialDrawBuf = glGetInteger(GL_DRAW_BUFFER); 391 initialReadBuf = glGetInteger(GL_READ_BUFFER); 392 } 393 394 public void resetGLObjects() { 395 logger.log(Level.INFO, "Reseting objects and invalidating state"); 396 objManager.resetObjects(); 397 statistics.clearMemory(); 398 invalidateState(); 399 } 400 401 public void cleanup() { 402 logger.log(Level.INFO, "Deleting objects and invalidating state"); 403 objManager.deleteAllObjects(this); 404 statistics.clearMemory(); 405 invalidateState(); 406 } 407 408 private void checkCap(Caps cap) { 409 if (!caps.contains(cap)) { 410 throw new UnsupportedOperationException("Required capability missing: " + cap.name()); 411 } 412 } 413 414 /*********************************************************************\ 415 |* Render State *| 416 \*********************************************************************/ 417 public void setDepthRange(float start, float end) { 418 glDepthRange(start, end); 419 } 420 421 public void clearBuffers(boolean color, boolean depth, boolean stencil) { 422 int bits = 0; 423 if (color) { 424 //See explanations of the depth below, we must enable color write to be able to clear the color buffer 425 if (context.colorWriteEnabled == false) { 426 glColorMask(true, true, true, true); 427 context.colorWriteEnabled = true; 428 } 429 bits = GL_COLOR_BUFFER_BIT; 430 } 431 if (depth) { 432 433 //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false 434 //here s some link on openl board 435 //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223 436 //if depth clear is requested, we enable the depthMask 437 if (context.depthWriteEnabled == false) { 438 glDepthMask(true); 439 context.depthWriteEnabled = true; 440 } 441 bits |= GL_DEPTH_BUFFER_BIT; 442 } 443 if (stencil) { 444 bits |= GL_STENCIL_BUFFER_BIT; 445 } 446 if (bits != 0) { 447 glClear(bits); 448 } 449 } 450 451 public void setBackgroundColor(ColorRGBA color) { 452 glClearColor(color.r, color.g, color.b, color.a); 453 } 454 455 public void setAlphaToCoverage(boolean value) { 456 if (value) { 457 glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); 458 } else { 459 glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); 460 } 461 } 462 463 public void applyRenderState(RenderState state) { 464 if (state.isWireframe() && !context.wireframe) { 465 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 466 context.wireframe = true; 467 } else if (!state.isWireframe() && context.wireframe) { 468 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 469 context.wireframe = false; 470 } 471 472 if (state.isDepthTest() && !context.depthTestEnabled) { 473 glEnable(GL_DEPTH_TEST); 474 glDepthFunc(GL_LEQUAL); 475 context.depthTestEnabled = true; 476 } else if (!state.isDepthTest() && context.depthTestEnabled) { 477 glDisable(GL_DEPTH_TEST); 478 context.depthTestEnabled = false; 479 } 480 481 if (state.isAlphaTest() && !context.alphaTestEnabled) { 482 glEnable(GL_ALPHA_TEST); 483 glAlphaFunc(GL_GREATER, state.getAlphaFallOff()); 484 context.alphaTestEnabled = true; 485 } else if (!state.isAlphaTest() && context.alphaTestEnabled) { 486 glDisable(GL_ALPHA_TEST); 487 context.alphaTestEnabled = false; 488 } 489 490 if (state.isDepthWrite() && !context.depthWriteEnabled) { 491 glDepthMask(true); 492 context.depthWriteEnabled = true; 493 } else if (!state.isDepthWrite() && context.depthWriteEnabled) { 494 glDepthMask(false); 495 context.depthWriteEnabled = false; 496 } 497 498 if (state.isColorWrite() && !context.colorWriteEnabled) { 499 glColorMask(true, true, true, true); 500 context.colorWriteEnabled = true; 501 } else if (!state.isColorWrite() && context.colorWriteEnabled) { 502 glColorMask(false, false, false, false); 503 context.colorWriteEnabled = false; 504 } 505 506 if (state.isPointSprite() && !context.pointSprite) { 507 // Only enable/disable sprite 508 if (context.boundTextures[0] != null){ 509 if (context.boundTextureUnit != 0){ 510 glActiveTexture(GL_TEXTURE0); 511 context.boundTextureUnit = 0; 512 } 513 glEnable(GL_POINT_SPRITE); 514 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); 515 } 516 context.pointSprite = true; 517 } else if (!state.isPointSprite() && context.pointSprite) { 518 if (context.boundTextures[0] != null){ 519 if (context.boundTextureUnit != 0){ 520 glActiveTexture(GL_TEXTURE0); 521 context.boundTextureUnit = 0; 522 } 523 glDisable(GL_POINT_SPRITE); 524 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); 525 context.pointSprite = false; 526 } 527 } 528 529 if (state.isPolyOffset()) { 530 if (!context.polyOffsetEnabled) { 531 glEnable(GL_POLYGON_OFFSET_FILL); 532 glPolygonOffset(state.getPolyOffsetFactor(), 533 state.getPolyOffsetUnits()); 534 context.polyOffsetEnabled = true; 535 context.polyOffsetFactor = state.getPolyOffsetFactor(); 536 context.polyOffsetUnits = state.getPolyOffsetUnits(); 537 } else { 538 if (state.getPolyOffsetFactor() != context.polyOffsetFactor 539 || state.getPolyOffsetUnits() != context.polyOffsetUnits) { 540 glPolygonOffset(state.getPolyOffsetFactor(), 541 state.getPolyOffsetUnits()); 542 context.polyOffsetFactor = state.getPolyOffsetFactor(); 543 context.polyOffsetUnits = state.getPolyOffsetUnits(); 544 } 545 } 546 } else { 547 if (context.polyOffsetEnabled) { 548 glDisable(GL_POLYGON_OFFSET_FILL); 549 context.polyOffsetEnabled = false; 550 context.polyOffsetFactor = 0; 551 context.polyOffsetUnits = 0; 552 } 553 } 554 555 if (state.getFaceCullMode() != context.cullMode) { 556 if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) { 557 glDisable(GL_CULL_FACE); 558 } else { 559 glEnable(GL_CULL_FACE); 560 } 561 562 switch (state.getFaceCullMode()) { 563 case Off: 564 break; 565 case Back: 566 glCullFace(GL_BACK); 567 break; 568 case Front: 569 glCullFace(GL_FRONT); 570 break; 571 case FrontAndBack: 572 glCullFace(GL_FRONT_AND_BACK); 573 break; 574 default: 575 throw new UnsupportedOperationException("Unrecognized face cull mode: " 576 + state.getFaceCullMode()); 577 } 578 579 context.cullMode = state.getFaceCullMode(); 580 } 581 582 if (state.getBlendMode() != context.blendMode) { 583 if (state.getBlendMode() == RenderState.BlendMode.Off) { 584 glDisable(GL_BLEND); 585 } else { 586 glEnable(GL_BLEND); 587 switch (state.getBlendMode()) { 588 case Off: 589 break; 590 case Additive: 591 glBlendFunc(GL_ONE, GL_ONE); 592 break; 593 case AlphaAdditive: 594 glBlendFunc(GL_SRC_ALPHA, GL_ONE); 595 break; 596 case Color: 597 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); 598 break; 599 case Alpha: 600 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 601 break; 602 case PremultAlpha: 603 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 604 break; 605 case Modulate: 606 glBlendFunc(GL_DST_COLOR, GL_ZERO); 607 break; 608 case ModulateX2: 609 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 610 break; 611 default: 612 throw new UnsupportedOperationException("Unrecognized blend mode: " 613 + state.getBlendMode()); 614 } 615 } 616 617 context.blendMode = state.getBlendMode(); 618 } 619 620 if (context.stencilTest != state.isStencilTest() 621 || context.frontStencilStencilFailOperation != state.getFrontStencilStencilFailOperation() 622 || context.frontStencilDepthFailOperation != state.getFrontStencilDepthFailOperation() 623 || context.frontStencilDepthPassOperation != state.getFrontStencilDepthPassOperation() 624 || context.backStencilStencilFailOperation != state.getBackStencilStencilFailOperation() 625 || context.backStencilDepthFailOperation != state.getBackStencilDepthFailOperation() 626 || context.backStencilDepthPassOperation != state.getBackStencilDepthPassOperation() 627 || context.frontStencilFunction != state.getFrontStencilFunction() 628 || context.backStencilFunction != state.getBackStencilFunction()) { 629 630 context.frontStencilStencilFailOperation = state.getFrontStencilStencilFailOperation(); //terrible looking, I know 631 context.frontStencilDepthFailOperation = state.getFrontStencilDepthFailOperation(); 632 context.frontStencilDepthPassOperation = state.getFrontStencilDepthPassOperation(); 633 context.backStencilStencilFailOperation = state.getBackStencilStencilFailOperation(); 634 context.backStencilDepthFailOperation = state.getBackStencilDepthFailOperation(); 635 context.backStencilDepthPassOperation = state.getBackStencilDepthPassOperation(); 636 context.frontStencilFunction = state.getFrontStencilFunction(); 637 context.backStencilFunction = state.getBackStencilFunction(); 638 639 if (state.isStencilTest()) { 640 glEnable(GL_STENCIL_TEST); 641 glStencilOpSeparate(GL_FRONT, 642 convertStencilOperation(state.getFrontStencilStencilFailOperation()), 643 convertStencilOperation(state.getFrontStencilDepthFailOperation()), 644 convertStencilOperation(state.getFrontStencilDepthPassOperation())); 645 glStencilOpSeparate(GL_BACK, 646 convertStencilOperation(state.getBackStencilStencilFailOperation()), 647 convertStencilOperation(state.getBackStencilDepthFailOperation()), 648 convertStencilOperation(state.getBackStencilDepthPassOperation())); 649 glStencilFuncSeparate(GL_FRONT, 650 convertTestFunction(state.getFrontStencilFunction()), 651 0, Integer.MAX_VALUE); 652 glStencilFuncSeparate(GL_BACK, 653 convertTestFunction(state.getBackStencilFunction()), 654 0, Integer.MAX_VALUE); 655 } else { 656 glDisable(GL_STENCIL_TEST); 657 } 658 } 659 } 660 661 private int convertStencilOperation(StencilOperation stencilOp) { 662 switch (stencilOp) { 663 case Keep: 664 return GL_KEEP; 665 case Zero: 666 return GL_ZERO; 667 case Replace: 668 return GL_REPLACE; 669 case Increment: 670 return GL_INCR; 671 case IncrementWrap: 672 return GL_INCR_WRAP; 673 case Decrement: 674 return GL_DECR; 675 case DecrementWrap: 676 return GL_DECR_WRAP; 677 case Invert: 678 return GL_INVERT; 679 default: 680 throw new UnsupportedOperationException("Unrecognized stencil operation: " + stencilOp); 681 } 682 } 683 684 private int convertTestFunction(TestFunction testFunc) { 685 switch (testFunc) { 686 case Never: 687 return GL_NEVER; 688 case Less: 689 return GL_LESS; 690 case LessOrEqual: 691 return GL_LEQUAL; 692 case Greater: 693 return GL_GREATER; 694 case GreaterOrEqual: 695 return GL_GEQUAL; 696 case Equal: 697 return GL_EQUAL; 698 case NotEqual: 699 return GL_NOTEQUAL; 700 case Always: 701 return GL_ALWAYS; 702 default: 703 throw new UnsupportedOperationException("Unrecognized test function: " + testFunc); 704 } 705 } 706 707 /*********************************************************************\ 708 |* Camera and World transforms *| 709 \*********************************************************************/ 710 public void setViewPort(int x, int y, int w, int h) { 711 if (x != vpX || vpY != y || vpW != w || vpH != h) { 712 glViewport(x, y, w, h); 713 vpX = x; 714 vpY = y; 715 vpW = w; 716 vpH = h; 717 } 718 } 719 720 public void setClipRect(int x, int y, int width, int height) { 721 if (!context.clipRectEnabled) { 722 glEnable(GL_SCISSOR_TEST); 723 context.clipRectEnabled = true; 724 } 725 if (clipX != x || clipY != y || clipW != width || clipH != height) { 726 glScissor(x, y, width, height); 727 clipX = x; 728 clipY = y; 729 clipW = width; 730 clipH = height; 731 } 732 } 733 734 public void clearClipRect() { 735 if (context.clipRectEnabled) { 736 glDisable(GL_SCISSOR_TEST); 737 context.clipRectEnabled = false; 738 739 clipX = 0; 740 clipY = 0; 741 clipW = 0; 742 clipH = 0; 743 } 744 } 745 746 public void onFrame() { 747 objManager.deleteUnused(this); 748 // statistics.clearFrame(); 749 } 750 751 public void setWorldMatrix(Matrix4f worldMatrix) { 752 } 753 754 public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) { 755 } 756 757 /*********************************************************************\ 758 |* Shaders *| 759 \*********************************************************************/ 760 protected void updateUniformLocation(Shader shader, Uniform uniform) { 761 stringBuf.setLength(0); 762 stringBuf.append(uniform.getName()).append('\0'); 763 updateNameBuffer(); 764 int loc = glGetUniformLocation(shader.getId(), nameBuf); 765 if (loc < 0) { 766 uniform.setLocation(-1); 767 // uniform is not declared in shader 768 logger.log(Level.INFO, "Uniform {0} is not declared in shader {1}.", new Object[]{uniform.getName(), shader.getSources()}); 769 } else { 770 uniform.setLocation(loc); 771 } 772 } 773 774 protected void bindProgram(Shader shader){ 775 int shaderId = shader.getId(); 776 if (context.boundShaderProgram != shaderId) { 777 glUseProgram(shaderId); 778 statistics.onShaderUse(shader, true); 779 boundShader = shader; 780 context.boundShaderProgram = shaderId; 781 } else { 782 statistics.onShaderUse(shader, false); 783 } 784 } 785 786 protected void updateUniform(Shader shader, Uniform uniform) { 787 int shaderId = shader.getId(); 788 789 assert uniform.getName() != null; 790 assert shader.getId() > 0; 791 792 bindProgram(shader); 793 794 int loc = uniform.getLocation(); 795 if (loc == -1) { 796 return; 797 } 798 799 if (loc == -2) { 800 // get uniform location 801 updateUniformLocation(shader, uniform); 802 if (uniform.getLocation() == -1) { 803 // not declared, ignore 804 uniform.clearUpdateNeeded(); 805 return; 806 } 807 loc = uniform.getLocation(); 808 } 809 810 if (uniform.getVarType() == null) { 811 return; // value not set yet.. 812 } 813 statistics.onUniformSet(); 814 815 uniform.clearUpdateNeeded(); 816 FloatBuffer fb; 817 switch (uniform.getVarType()) { 818 case Float: 819 Float f = (Float) uniform.getValue(); 820 glUniform1f(loc, f.floatValue()); 821 break; 822 case Vector2: 823 Vector2f v2 = (Vector2f) uniform.getValue(); 824 glUniform2f(loc, v2.getX(), v2.getY()); 825 break; 826 case Vector3: 827 Vector3f v3 = (Vector3f) uniform.getValue(); 828 glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ()); 829 break; 830 case Vector4: 831 Object val = uniform.getValue(); 832 if (val instanceof ColorRGBA) { 833 ColorRGBA c = (ColorRGBA) val; 834 glUniform4f(loc, c.r, c.g, c.b, c.a); 835 } else if (val instanceof Vector4f) { 836 Vector4f c = (Vector4f) val; 837 glUniform4f(loc, c.x, c.y, c.z, c.w); 838 } else { 839 Quaternion c = (Quaternion) uniform.getValue(); 840 glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW()); 841 } 842 break; 843 case Boolean: 844 Boolean b = (Boolean) uniform.getValue(); 845 glUniform1i(loc, b.booleanValue() ? GL_TRUE : GL_FALSE); 846 break; 847 case Matrix3: 848 fb = (FloatBuffer) uniform.getValue(); 849 assert fb.remaining() == 9; 850 glUniformMatrix3(loc, false, fb); 851 break; 852 case Matrix4: 853 fb = (FloatBuffer) uniform.getValue(); 854 assert fb.remaining() == 16; 855 glUniformMatrix4(loc, false, fb); 856 break; 857 case FloatArray: 858 fb = (FloatBuffer) uniform.getValue(); 859 glUniform1(loc, fb); 860 break; 861 case Vector2Array: 862 fb = (FloatBuffer) uniform.getValue(); 863 glUniform2(loc, fb); 864 break; 865 case Vector3Array: 866 fb = (FloatBuffer) uniform.getValue(); 867 glUniform3(loc, fb); 868 break; 869 case Vector4Array: 870 fb = (FloatBuffer) uniform.getValue(); 871 glUniform4(loc, fb); 872 break; 873 case Matrix4Array: 874 fb = (FloatBuffer) uniform.getValue(); 875 glUniformMatrix4(loc, false, fb); 876 break; 877 case Int: 878 Integer i = (Integer) uniform.getValue(); 879 glUniform1i(loc, i.intValue()); 880 break; 881 default: 882 throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType()); 883 } 884 } 885 886 protected void updateShaderUniforms(Shader shader) { 887 ListMap<String, Uniform> uniforms = shader.getUniformMap(); 888 // for (Uniform uniform : shader.getUniforms()){ 889 for (int i = 0; i < uniforms.size(); i++) { 890 Uniform uniform = uniforms.getValue(i); 891 if (uniform.isUpdateNeeded()) { 892 updateUniform(shader, uniform); 893 } 894 } 895 } 896 897 protected void resetUniformLocations(Shader shader) { 898 ListMap<String, Uniform> uniforms = shader.getUniformMap(); 899 // for (Uniform uniform : shader.getUniforms()){ 900 for (int i = 0; i < uniforms.size(); i++) { 901 Uniform uniform = uniforms.getValue(i); 902 uniform.reset(); // e.g check location again 903 } 904 } 905 906 /* 907 * (Non-javadoc) 908 * Only used for fixed-function. Ignored. 909 */ 910 public void setLighting(LightList list) { 911 } 912 913 public int convertShaderType(ShaderType type) { 914 switch (type) { 915 case Fragment: 916 return GL_FRAGMENT_SHADER; 917 case Vertex: 918 return GL_VERTEX_SHADER; 919 // case Geometry: 920 // return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB; 921 default: 922 throw new UnsupportedOperationException("Unrecognized shader type."); 923 } 924 } 925 926 public void updateShaderSourceData(ShaderSource source, String language) { 927 int id = source.getId(); 928 if (id == -1) { 929 // create id 930 id = glCreateShader(convertShaderType(source.getType())); 931 if (id <= 0) { 932 throw new RendererException("Invalid ID received when trying to create shader."); 933 } 934 935 source.setId(id); 936 }else{ 937 throw new RendererException("Cannot recompile shader source"); 938 } 939 940 // upload shader source 941 // merge the defines and source code 942 943 stringBuf.setLength(0); 944 if (language.startsWith("GLSL")) { 945 int version = Integer.parseInt(language.substring(4)); 946 if (version > 100) { 947 stringBuf.append("#version "); 948 stringBuf.append(language.substring(4)); 949 if (version >= 150) { 950 stringBuf.append(" core"); 951 } 952 stringBuf.append("\n"); 953 } 954 } 955 updateNameBuffer(); 956 957 byte[] definesCodeData = source.getDefines().getBytes(); 958 byte[] sourceCodeData = source.getSource().getBytes(); 959 ByteBuffer codeBuf = BufferUtils.createByteBuffer(nameBuf.limit() 960 + definesCodeData.length 961 + sourceCodeData.length); 962 codeBuf.put(nameBuf); 963 codeBuf.put(definesCodeData); 964 codeBuf.put(sourceCodeData); 965 codeBuf.flip(); 966 967 glShaderSource(id, codeBuf); 968 glCompileShader(id); 969 970 glGetShader(id, GL_COMPILE_STATUS, intBuf1); 971 972 boolean compiledOK = intBuf1.get(0) == GL_TRUE; 973 String infoLog = null; 974 975 if (VALIDATE_SHADER || !compiledOK) { 976 // even if compile succeeded, check 977 // log for warnings 978 glGetShader(id, GL_INFO_LOG_LENGTH, intBuf1); 979 int length = intBuf1.get(0); 980 if (length > 3) { 981 // get infos 982 ByteBuffer logBuf = BufferUtils.createByteBuffer(length); 983 glGetShaderInfoLog(id, null, logBuf); 984 byte[] logBytes = new byte[length]; 985 logBuf.get(logBytes, 0, length); 986 // convert to string, etc 987 infoLog = new String(logBytes); 988 } 989 } 990 991 if (compiledOK) { 992 if (infoLog != null) { 993 logger.log(Level.INFO, "{0} compile success\n{1}", 994 new Object[]{source.getName(), infoLog}); 995 } else { 996 logger.log(Level.FINE, "{0} compile success", source.getName()); 997 } 998 } else { 999 logger.log(Level.WARNING, "Bad compile of:\n{0}{1}", 1000 new Object[]{source.getDefines(), source.getSource()}); 1001 if (infoLog != null) { 1002 throw new RendererException("compile error in:" + source + " error:" + infoLog); 1003 } else { 1004 throw new RendererException("compile error in:" + source + " error: <not provided>"); 1005 } 1006 } 1007 1008 source.clearUpdateNeeded(); 1009 // only usable if compiled 1010 source.setUsable(compiledOK); 1011 if (!compiledOK) { 1012 // make sure to dispose id cause all program's 1013 // shaders will be cleared later. 1014 glDeleteShader(id); 1015 } else { 1016 // register for cleanup since the ID is usable 1017 // NOTE: From now on cleanup is handled 1018 // by the parent shader object so no need 1019 // to register. 1020 //objManager.registerForCleanup(source); 1021 } 1022 } 1023 1024 public void updateShaderData(Shader shader) { 1025 int id = shader.getId(); 1026 boolean needRegister = false; 1027 if (id == -1) { 1028 // create program 1029 id = glCreateProgram(); 1030 if (id == 0) { 1031 throw new RendererException("Invalid ID (" + id + ") received when trying to create shader program."); 1032 } 1033 1034 shader.setId(id); 1035 needRegister = true; 1036 } 1037 1038 for (ShaderSource source : shader.getSources()) { 1039 if (source.isUpdateNeeded()) { 1040 updateShaderSourceData(source, shader.getLanguage()); 1041 // shader has been compiled here 1042 } 1043 1044 if (!source.isUsable()) { 1045 // it's useless.. just forget about everything.. 1046 shader.setUsable(false); 1047 shader.clearUpdateNeeded(); 1048 return; 1049 } 1050 glAttachShader(id, source.getId()); 1051 } 1052 1053 if (caps.contains(Caps.OpenGL30)) { 1054 // Check if GLSL version is 1.5 for shader 1055 GL30.glBindFragDataLocation(id, 0, "outFragColor"); 1056 } 1057 1058 // link shaders to program 1059 glLinkProgram(id); 1060 glGetProgram(id, GL_LINK_STATUS, intBuf1); 1061 boolean linkOK = intBuf1.get(0) == GL_TRUE; 1062 String infoLog = null; 1063 1064 if (VALIDATE_SHADER || !linkOK) { 1065 glGetProgram(id, GL_INFO_LOG_LENGTH, intBuf1); 1066 int length = intBuf1.get(0); 1067 if (length > 3) { 1068 // get infos 1069 ByteBuffer logBuf = BufferUtils.createByteBuffer(length); 1070 glGetProgramInfoLog(id, null, logBuf); 1071 1072 // convert to string, etc 1073 byte[] logBytes = new byte[length]; 1074 logBuf.get(logBytes, 0, length); 1075 infoLog = new String(logBytes); 1076 } 1077 } 1078 1079 if (linkOK) { 1080 if (infoLog != null) { 1081 logger.log(Level.INFO, "shader link success. \n{0}", infoLog); 1082 } else { 1083 logger.fine("shader link success"); 1084 } 1085 } else { 1086 if (infoLog != null) { 1087 throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog); 1088 } else { 1089 throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>"); 1090 } 1091 } 1092 1093 shader.clearUpdateNeeded(); 1094 if (!linkOK) { 1095 // failure.. forget about everything 1096 shader.resetSources(); 1097 shader.setUsable(false); 1098 deleteShader(shader); 1099 } else { 1100 shader.setUsable(true); 1101 if (needRegister) { 1102 objManager.registerForCleanup(shader); 1103 statistics.onNewShader(); 1104 } else { 1105 // OpenGL spec: uniform locations may change after re-link 1106 resetUniformLocations(shader); 1107 } 1108 } 1109 } 1110 1111 public void setShader(Shader shader) { 1112 if (shader == null) { 1113 throw new IllegalArgumentException("shader cannot be null"); 1114 // if (context.boundShaderProgram > 0) { 1115 // glUseProgram(0); 1116 // statistics.onShaderUse(null, true); 1117 // context.boundShaderProgram = 0; 1118 // boundShader = null; 1119 // } 1120 } else { 1121 if (shader.isUpdateNeeded()) { 1122 updateShaderData(shader); 1123 } 1124 1125 // NOTE: might want to check if any of the 1126 // sources need an update? 1127 1128 if (!shader.isUsable()) { 1129 return; 1130 } 1131 1132 assert shader.getId() > 0; 1133 1134 updateShaderUniforms(shader); 1135 bindProgram(shader); 1136 } 1137 } 1138 1139 public void deleteShaderSource(ShaderSource source) { 1140 if (source.getId() < 0) { 1141 logger.warning("Shader source is not uploaded to GPU, cannot delete."); 1142 return; 1143 } 1144 source.setUsable(false); 1145 source.clearUpdateNeeded(); 1146 glDeleteShader(source.getId()); 1147 source.resetObject(); 1148 } 1149 1150 public void deleteShader(Shader shader) { 1151 if (shader.getId() == -1) { 1152 logger.warning("Shader is not uploaded to GPU, cannot delete."); 1153 return; 1154 } 1155 1156 for (ShaderSource source : shader.getSources()) { 1157 if (source.getId() != -1) { 1158 glDetachShader(shader.getId(), source.getId()); 1159 deleteShaderSource(source); 1160 } 1161 } 1162 1163 // kill all references so sources can be collected 1164 // if needed. 1165 shader.resetSources(); 1166 glDeleteProgram(shader.getId()); 1167 1168 statistics.onDeleteShader(); 1169 } 1170 1171 /*********************************************************************\ 1172 |* Framebuffers *| 1173 \*********************************************************************/ 1174 public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) { 1175 copyFrameBuffer(src, dst, true); 1176 } 1177 1178 public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) { 1179 if (GLContext.getCapabilities().GL_EXT_framebuffer_blit) { 1180 int srcW = 0; 1181 int srcH = 0; 1182 int dstW = 0; 1183 int dstH = 0; 1184 int prevFBO = context.boundFBO; 1185 1186 if (src != null && src.isUpdateNeeded()) { 1187 updateFrameBuffer(src); 1188 } 1189 1190 if (dst != null && dst.isUpdateNeeded()) { 1191 updateFrameBuffer(dst); 1192 } 1193 1194 if (src == null) { 1195 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); 1196 // srcW = viewWidth; 1197 // srcH = viewHeight; 1198 } else { 1199 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, src.getId()); 1200 srcW = src.getWidth(); 1201 srcH = src.getHeight(); 1202 } 1203 if (dst == null) { 1204 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); 1205 // dstW = viewWidth; 1206 // dstH = viewHeight; 1207 } else { 1208 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dst.getId()); 1209 dstW = dst.getWidth(); 1210 dstH = dst.getHeight(); 1211 } 1212 int mask = GL_COLOR_BUFFER_BIT; 1213 if (copyDepth) { 1214 mask |= GL_DEPTH_BUFFER_BIT; 1215 } 1216 glBlitFramebufferEXT(0, 0, srcW, srcH, 1217 0, 0, dstW, dstH, mask, 1218 GL_NEAREST); 1219 1220 1221 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prevFBO); 1222 try { 1223 checkFrameBufferError(); 1224 } catch (IllegalStateException ex) { 1225 logger.log(Level.SEVERE, "Source FBO:\n{0}", src); 1226 logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst); 1227 throw ex; 1228 } 1229 } else { 1230 throw new RendererException("EXT_framebuffer_blit required."); 1231 // TODO: support non-blit copies? 1232 } 1233 } 1234 1235 private String getTargetBufferName(int buffer){ 1236 switch (buffer){ 1237 case GL_NONE: return "NONE"; 1238 case GL_FRONT: return "GL_FRONT"; 1239 case GL_BACK: return "GL_BACK"; 1240 default: 1241 if ( buffer >= GL_COLOR_ATTACHMENT0_EXT 1242 && buffer <= GL_COLOR_ATTACHMENT15_EXT){ 1243 return "GL_COLOR_ATTACHMENT" + 1244 (buffer - GL_COLOR_ATTACHMENT0_EXT); 1245 }else{ 1246 return "UNKNOWN? " + buffer; 1247 } 1248 } 1249 } 1250 1251 private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name){ 1252 System.out.println("== Renderbuffer " + name + " =="); 1253 System.out.println("RB ID: " + rb.getId()); 1254 System.out.println("Is proper? " + glIsRenderbufferEXT(rb.getId())); 1255 1256 int attachment = convertAttachmentSlot(rb.getSlot()); 1257 1258 int type = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT, 1259 attachment, 1260 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT); 1261 1262 int rbName = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT, 1263 attachment, 1264 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT); 1265 1266 switch (type){ 1267 case GL_NONE: 1268 System.out.println("Type: None"); 1269 return; // note: return from method as other queries will be invalid 1270 case GL_TEXTURE: 1271 System.out.println("Type: Texture"); 1272 break; 1273 case GL_RENDERBUFFER_EXT: 1274 System.out.println("Type: Buffer"); 1275 System.out.println("RB ID: " + rbName); 1276 break; 1277 } 1278 1279 1280 1281 } 1282 1283 private void printRealFrameBufferInfo(FrameBuffer fb) { 1284 boolean doubleBuffer = glGetBoolean(GL_DOUBLEBUFFER); 1285 String drawBuf = getTargetBufferName(glGetInteger(GL_DRAW_BUFFER)); 1286 String readBuf = getTargetBufferName(glGetInteger(GL_READ_BUFFER)); 1287 1288 int fbId = fb.getId(); 1289 int curDrawBinding = glGetInteger(ARBFramebufferObject.GL_DRAW_FRAMEBUFFER_BINDING); 1290 int curReadBinding = glGetInteger(ARBFramebufferObject.GL_READ_FRAMEBUFFER_BINDING); 1291 1292 System.out.println("=== OpenGL FBO State ==="); 1293 System.out.println("Context doublebuffered? " + doubleBuffer); 1294 System.out.println("FBO ID: " + fbId); 1295 System.out.println("Is proper? " + glIsFramebufferEXT(fbId)); 1296 System.out.println("Is bound to draw? " + (fbId == curDrawBinding)); 1297 System.out.println("Is bound to read? " + (fbId == curReadBinding)); 1298 System.out.println("Draw buffer: " + drawBuf); 1299 System.out.println("Read buffer: " + readBuf); 1300 1301 if (context.boundFBO != fbId){ 1302 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbId); 1303 context.boundFBO = fbId; 1304 } 1305 1306 if (fb.getDepthBuffer() != null){ 1307 printRealRenderBufferInfo(fb, fb.getDepthBuffer(), "Depth"); 1308 } 1309 for (int i = 0; i < fb.getNumColorBuffers(); i++){ 1310 printRealRenderBufferInfo(fb, fb.getColorBuffer(i), "Color" + i); 1311 } 1312 } 1313 1314 private void checkFrameBufferError() { 1315 int status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 1316 switch (status) { 1317 case GL_FRAMEBUFFER_COMPLETE_EXT: 1318 break; 1319 case GL_FRAMEBUFFER_UNSUPPORTED_EXT: 1320 //Choose different formats 1321 throw new IllegalStateException("Framebuffer object format is " 1322 + "unsupported by the video hardware."); 1323 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: 1324 throw new IllegalStateException("Framebuffer has erronous attachment."); 1325 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: 1326 throw new IllegalStateException("Framebuffer doesn't have any renderbuffers attached."); 1327 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: 1328 throw new IllegalStateException("Framebuffer attachments must have same dimensions."); 1329 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: 1330 throw new IllegalStateException("Framebuffer attachments must have same formats."); 1331 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: 1332 throw new IllegalStateException("Incomplete draw buffer."); 1333 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: 1334 throw new IllegalStateException("Incomplete read buffer."); 1335 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: 1336 throw new IllegalStateException("Incomplete multisample buffer."); 1337 default: 1338 //Programming error; will fail on all hardware 1339 throw new IllegalStateException("Some video driver error " 1340 + "or programming error occured. " 1341 + "Framebuffer object status is invalid. "); 1342 } 1343 } 1344 1345 private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) { 1346 int id = rb.getId(); 1347 if (id == -1) { 1348 glGenRenderbuffersEXT(intBuf1); 1349 id = intBuf1.get(0); 1350 rb.setId(id); 1351 } 1352 1353 if (context.boundRB != id) { 1354 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id); 1355 context.boundRB = id; 1356 } 1357 1358 if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) { 1359 throw new RendererException("Resolution " + fb.getWidth() 1360 + ":" + fb.getHeight() + " is not supported."); 1361 } 1362 1363 TextureUtil.checkFormatSupported(rb.getFormat()); 1364 1365 if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) { 1366 int samples = fb.getSamples(); 1367 if (maxFBOSamples < samples) { 1368 samples = maxFBOSamples; 1369 } 1370 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 1371 samples, 1372 TextureUtil.convertTextureFormat(rb.getFormat()), 1373 fb.getWidth(), 1374 fb.getHeight()); 1375 } else { 1376 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 1377 TextureUtil.convertTextureFormat(rb.getFormat()), 1378 fb.getWidth(), 1379 fb.getHeight()); 1380 } 1381 } 1382 1383 private int convertAttachmentSlot(int attachmentSlot) { 1384 // can also add support for stencil here 1385 if (attachmentSlot == -100) { 1386 return GL_DEPTH_ATTACHMENT_EXT; 1387 } else if (attachmentSlot < 0 || attachmentSlot >= 16) { 1388 throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot); 1389 } 1390 1391 return GL_COLOR_ATTACHMENT0_EXT + attachmentSlot; 1392 } 1393 1394 public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) { 1395 Texture tex = rb.getTexture(); 1396 Image image = tex.getImage(); 1397 if (image.isUpdateNeeded()) { 1398 updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels(), 0); 1399 1400 // NOTE: For depth textures, sets nearest/no-mips mode 1401 // Required to fix "framebuffer unsupported" 1402 // for old NVIDIA drivers! 1403 setupTextureParams(tex); 1404 } 1405 1406 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, 1407 convertAttachmentSlot(rb.getSlot()), 1408 convertTextureType(tex.getType(), image.getMultiSamples()), 1409 image.getId(), 1410 0); 1411 } 1412 1413 public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) { 1414 boolean needAttach; 1415 if (rb.getTexture() == null) { 1416 // if it hasn't been created yet, then attach is required. 1417 needAttach = rb.getId() == -1; 1418 updateRenderBuffer(fb, rb); 1419 } else { 1420 needAttach = false; 1421 updateRenderTexture(fb, rb); 1422 } 1423 if (needAttach) { 1424 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, 1425 convertAttachmentSlot(rb.getSlot()), 1426 GL_RENDERBUFFER_EXT, 1427 rb.getId()); 1428 } 1429 } 1430 1431 public void updateFrameBuffer(FrameBuffer fb) { 1432 int id = fb.getId(); 1433 if (id == -1) { 1434 // create FBO 1435 glGenFramebuffersEXT(intBuf1); 1436 id = intBuf1.get(0); 1437 fb.setId(id); 1438 objManager.registerForCleanup(fb); 1439 1440 statistics.onNewFrameBuffer(); 1441 } 1442 1443 if (context.boundFBO != id) { 1444 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); 1445 // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0 1446 context.boundDrawBuf = 0; 1447 context.boundFBO = id; 1448 } 1449 1450 FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer(); 1451 if (depthBuf != null) { 1452 updateFrameBufferAttachment(fb, depthBuf); 1453 } 1454 1455 for (int i = 0; i < fb.getNumColorBuffers(); i++) { 1456 FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i); 1457 updateFrameBufferAttachment(fb, colorBuf); 1458 } 1459 1460 fb.clearUpdateNeeded(); 1461 } 1462 1463 public Vector2f[] getFrameBufferSamplePositions(FrameBuffer fb) { 1464 if (fb.getSamples() <= 1) { 1465 throw new IllegalArgumentException("Framebuffer must be multisampled"); 1466 } 1467 1468 setFrameBuffer(fb); 1469 1470 Vector2f[] samplePositions = new Vector2f[fb.getSamples()]; 1471 FloatBuffer samplePos = BufferUtils.createFloatBuffer(2); 1472 for (int i = 0; i < samplePositions.length; i++) { 1473 glGetMultisample(GL_SAMPLE_POSITION, i, samplePos); 1474 samplePos.clear(); 1475 samplePositions[i] = new Vector2f(samplePos.get(0) - 0.5f, 1476 samplePos.get(1) - 0.5f); 1477 } 1478 return samplePositions; 1479 } 1480 1481 public void setMainFrameBufferOverride(FrameBuffer fb){ 1482 mainFbOverride = fb; 1483 } 1484 1485 public void setFrameBuffer(FrameBuffer fb) { 1486 if (fb == null && mainFbOverride != null){ 1487 fb = mainFbOverride; 1488 } 1489 1490 if (lastFb == fb) { 1491 if (fb == null || !fb.isUpdateNeeded()){ 1492 return; 1493 } 1494 } 1495 1496 // generate mipmaps for last FB if needed 1497 if (lastFb != null) { 1498 for (int i = 0; i < lastFb.getNumColorBuffers(); i++) { 1499 RenderBuffer rb = lastFb.getColorBuffer(i); 1500 Texture tex = rb.getTexture(); 1501 if (tex != null 1502 && tex.getMinFilter().usesMipMapLevels()) { 1503 setTexture(0, rb.getTexture()); 1504 1505 int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples()); 1506 glEnable(textureType); 1507 glGenerateMipmapEXT(textureType); 1508 glDisable(textureType); 1509 } 1510 } 1511 } 1512 1513 if (fb == null) { 1514 // unbind any fbos 1515 if (context.boundFBO != 0) { 1516 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 1517 statistics.onFrameBufferUse(null, true); 1518 1519 context.boundFBO = 0; 1520 } 1521 // select back buffer 1522 if (context.boundDrawBuf != -1) { 1523 glDrawBuffer(initialDrawBuf); 1524 context.boundDrawBuf = -1; 1525 } 1526 if (context.boundReadBuf != -1) { 1527 glReadBuffer(initialReadBuf); 1528 context.boundReadBuf = -1; 1529 } 1530 1531 lastFb = null; 1532 } else { 1533 if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null){ 1534 throw new IllegalArgumentException("The framebuffer: " + fb + 1535 "\nDoesn't have any color/depth buffers"); 1536 } 1537 1538 if (fb.isUpdateNeeded()) { 1539 updateFrameBuffer(fb); 1540 } 1541 1542 if (context.boundFBO != fb.getId()) { 1543 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb.getId()); 1544 statistics.onFrameBufferUse(fb, true); 1545 1546 // update viewport to reflect framebuffer's resolution 1547 setViewPort(0, 0, fb.getWidth(), fb.getHeight()); 1548 1549 context.boundFBO = fb.getId(); 1550 } else { 1551 statistics.onFrameBufferUse(fb, false); 1552 } 1553 if (fb.getNumColorBuffers() == 0) { 1554 // make sure to select NONE as draw buf 1555 // no color buffer attached. select NONE 1556 if (context.boundDrawBuf != -2) { 1557 glDrawBuffer(GL_NONE); 1558 context.boundDrawBuf = -2; 1559 } 1560 if (context.boundReadBuf != -2) { 1561 glReadBuffer(GL_NONE); 1562 context.boundReadBuf = -2; 1563 } 1564 } else { 1565 if (fb.isMultiTarget()) { 1566 if (fb.getNumColorBuffers() > maxMRTFBOAttachs) { 1567 throw new RendererException("Framebuffer has more" 1568 + " targets than are supported" 1569 + " on the system!"); 1570 } 1571 1572 if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) { 1573 intBuf16.clear(); 1574 for (int i = 0; i < fb.getNumColorBuffers(); i++) { 1575 intBuf16.put(GL_COLOR_ATTACHMENT0_EXT + i); 1576 } 1577 1578 intBuf16.flip(); 1579 glDrawBuffers(intBuf16); 1580 context.boundDrawBuf = 100 + fb.getNumColorBuffers(); 1581 } 1582 } else { 1583 RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex()); 1584 // select this draw buffer 1585 if (context.boundDrawBuf != rb.getSlot()) { 1586 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot()); 1587 context.boundDrawBuf = rb.getSlot(); 1588 } 1589 } 1590 } 1591 1592 assert fb.getId() >= 0; 1593 assert context.boundFBO == fb.getId(); 1594 1595 lastFb = fb; 1596 1597 try { 1598 checkFrameBufferError(); 1599 } catch (IllegalStateException ex) { 1600 logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb); 1601 printRealFrameBufferInfo(fb); 1602 throw ex; 1603 } 1604 } 1605 } 1606 1607 public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { 1608 if (fb != null) { 1609 RenderBuffer rb = fb.getColorBuffer(); 1610 if (rb == null) { 1611 throw new IllegalArgumentException("Specified framebuffer" 1612 + " does not have a colorbuffer"); 1613 } 1614 1615 setFrameBuffer(fb); 1616 if (context.boundReadBuf != rb.getSlot()) { 1617 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + rb.getSlot()); 1618 context.boundReadBuf = rb.getSlot(); 1619 } 1620 } else { 1621 setFrameBuffer(null); 1622 } 1623 1624 glReadPixels(vpX, vpY, vpW, vpH, /*GL_RGBA*/ GL_BGRA, GL_UNSIGNED_BYTE, byteBuf); 1625 } 1626 1627 private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) { 1628 intBuf1.put(0, rb.getId()); 1629 glDeleteRenderbuffersEXT(intBuf1); 1630 } 1631 1632 public void deleteFrameBuffer(FrameBuffer fb) { 1633 if (fb.getId() != -1) { 1634 if (context.boundFBO == fb.getId()) { 1635 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 1636 context.boundFBO = 0; 1637 } 1638 1639 if (fb.getDepthBuffer() != null) { 1640 deleteRenderBuffer(fb, fb.getDepthBuffer()); 1641 } 1642 if (fb.getColorBuffer() != null) { 1643 deleteRenderBuffer(fb, fb.getColorBuffer()); 1644 } 1645 1646 intBuf1.put(0, fb.getId()); 1647 glDeleteFramebuffersEXT(intBuf1); 1648 fb.resetObject(); 1649 1650 statistics.onDeleteFrameBuffer(); 1651 } 1652 } 1653 1654 /*********************************************************************\ 1655 |* Textures *| 1656 \*********************************************************************/ 1657 private int convertTextureType(Texture.Type type, int samples) { 1658 switch (type) { 1659 case TwoDimensional: 1660 if (samples > 1) { 1661 return ARBTextureMultisample.GL_TEXTURE_2D_MULTISAMPLE; 1662 } else { 1663 return GL_TEXTURE_2D; 1664 } 1665 case TwoDimensionalArray: 1666 if (samples > 1) { 1667 return ARBTextureMultisample.GL_TEXTURE_2D_MULTISAMPLE_ARRAY; 1668 } else { 1669 return EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT; 1670 } 1671 case ThreeDimensional: 1672 return GL_TEXTURE_3D; 1673 case CubeMap: 1674 return GL_TEXTURE_CUBE_MAP; 1675 default: 1676 throw new UnsupportedOperationException("Unknown texture type: " + type); 1677 } 1678 } 1679 1680 private int convertMagFilter(Texture.MagFilter filter) { 1681 switch (filter) { 1682 case Bilinear: 1683 return GL_LINEAR; 1684 case Nearest: 1685 return GL_NEAREST; 1686 default: 1687 throw new UnsupportedOperationException("Unknown mag filter: " + filter); 1688 } 1689 } 1690 1691 private int convertMinFilter(Texture.MinFilter filter) { 1692 switch (filter) { 1693 case Trilinear: 1694 return GL_LINEAR_MIPMAP_LINEAR; 1695 case BilinearNearestMipMap: 1696 return GL_LINEAR_MIPMAP_NEAREST; 1697 case NearestLinearMipMap: 1698 return GL_NEAREST_MIPMAP_LINEAR; 1699 case NearestNearestMipMap: 1700 return GL_NEAREST_MIPMAP_NEAREST; 1701 case BilinearNoMipMaps: 1702 return GL_LINEAR; 1703 case NearestNoMipMaps: 1704 return GL_NEAREST; 1705 default: 1706 throw new UnsupportedOperationException("Unknown min filter: " + filter); 1707 } 1708 } 1709 1710 private int convertWrapMode(Texture.WrapMode mode) { 1711 switch (mode) { 1712 case BorderClamp: 1713 return GL_CLAMP_TO_BORDER; 1714 case Clamp: 1715 return GL_CLAMP; 1716 case EdgeClamp: 1717 return GL_CLAMP_TO_EDGE; 1718 case Repeat: 1719 return GL_REPEAT; 1720 case MirroredRepeat: 1721 return GL_MIRRORED_REPEAT; 1722 default: 1723 throw new UnsupportedOperationException("Unknown wrap mode: " + mode); 1724 } 1725 } 1726 1727 @SuppressWarnings("fallthrough") 1728 private void setupTextureParams(Texture tex) { 1729 Image image = tex.getImage(); 1730 int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1); 1731 1732 // filter things 1733 int minFilter = convertMinFilter(tex.getMinFilter()); 1734 int magFilter = convertMagFilter(tex.getMagFilter()); 1735 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter); 1736 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter); 1737 1738 if (tex.getAnisotropicFilter() > 1) { 1739 if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic) { 1740 glTexParameterf(target, 1741 EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, 1742 tex.getAnisotropicFilter()); 1743 } 1744 } 1745 1746 if (context.pointSprite) { 1747 return; // Attempt to fix glTexParameter crash for some ATI GPUs 1748 } 1749 // repeat modes 1750 switch (tex.getType()) { 1751 case ThreeDimensional: 1752 case CubeMap: // cubemaps use 3D coords 1753 glTexParameteri(target, GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); 1754 case TwoDimensional: 1755 case TwoDimensionalArray: 1756 glTexParameteri(target, GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); 1757 // fall down here is intentional.. 1758 // case OneDimensional: 1759 glTexParameteri(target, GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); 1760 break; 1761 default: 1762 throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); 1763 } 1764 1765 // R to Texture compare mode 1766 if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { 1767 glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); 1768 glTexParameteri(target, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY); 1769 if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { 1770 glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_GEQUAL); 1771 } else { 1772 glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); 1773 } 1774 } 1775 } 1776 1777 public void updateTexImageData(Image img, Texture.Type type, boolean mips, int unit) { 1778 int texId = img.getId(); 1779 if (texId == -1) { 1780 // create texture 1781 glGenTextures(intBuf1); 1782 texId = intBuf1.get(0); 1783 img.setId(texId); 1784 objManager.registerForCleanup(img); 1785 1786 statistics.onNewTexture(); 1787 } 1788 1789 // bind texture 1790 int target = convertTextureType(type, img.getMultiSamples()); 1791 if (context.boundTextureUnit != unit) { 1792 glActiveTexture(GL_TEXTURE0 + unit); 1793 context.boundTextureUnit = unit; 1794 } 1795 if (context.boundTextures[unit] != img) { 1796 glBindTexture(target, texId); 1797 context.boundTextures[unit] = img; 1798 1799 statistics.onTextureUse(img, true); 1800 } 1801 1802 if (!img.hasMipmaps() && mips) { 1803 // No pregenerated mips available, 1804 // generate from base level if required 1805 if (!GLContext.getCapabilities().OpenGL30) { 1806 glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE); 1807 } 1808 } else { 1809 // glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 ); 1810 if (img.getMipMapSizes() != null) { 1811 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length); 1812 } 1813 } 1814 1815 int imageSamples = img.getMultiSamples(); 1816 if (imageSamples > 1) { 1817 if (img.getFormat().isDepthFormat()) { 1818 img.setMultiSamples(Math.min(maxDepthTexSamples, imageSamples)); 1819 } else { 1820 img.setMultiSamples(Math.min(maxColorTexSamples, imageSamples)); 1821 } 1822 } 1823 1824 // Yes, some OpenGL2 cards (GeForce 5) still dont support NPOT. 1825 if (!GLContext.getCapabilities().GL_ARB_texture_non_power_of_two) { 1826 if (img.getWidth() != 0 && img.getHeight() != 0) { 1827 if (!FastMath.isPowerOfTwo(img.getWidth()) 1828 || !FastMath.isPowerOfTwo(img.getHeight())) { 1829 if (img.getData(0) == null) { 1830 throw new RendererException("non-power-of-2 framebuffer textures are not supported by the video hardware"); 1831 } else { 1832 MipMapGenerator.resizeToPowerOf2(img); 1833 } 1834 } 1835 } 1836 } 1837 1838 // Check if graphics card doesn't support multisample textures 1839 if (!GLContext.getCapabilities().GL_ARB_texture_multisample) { 1840 if (img.getMultiSamples() > 1) { 1841 throw new RendererException("Multisample textures not supported by graphics hardware"); 1842 } 1843 } 1844 1845 if (target == GL_TEXTURE_CUBE_MAP) { 1846 List<ByteBuffer> data = img.getData(); 1847 if (data.size() != 6) { 1848 logger.log(Level.WARNING, "Invalid texture: {0}\n" 1849 + "Cubemap textures must contain 6 data units.", img); 1850 return; 1851 } 1852 for (int i = 0; i < 6; i++) { 1853 TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc); 1854 } 1855 } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) { 1856 List<ByteBuffer> data = img.getData(); 1857 // -1 index specifies prepare data for 2D Array 1858 TextureUtil.uploadTexture(img, target, -1, 0, tdc); 1859 for (int i = 0; i < data.size(); i++) { 1860 // upload each slice of 2D array in turn 1861 // this time with the appropriate index 1862 TextureUtil.uploadTexture(img, target, i, 0, tdc); 1863 } 1864 } else { 1865 TextureUtil.uploadTexture(img, target, 0, 0, tdc); 1866 } 1867 1868 if (img.getMultiSamples() != imageSamples) { 1869 img.setMultiSamples(imageSamples); 1870 } 1871 1872 if (GLContext.getCapabilities().OpenGL30) { 1873 if (!img.hasMipmaps() && mips && img.getData() != null) { 1874 // XXX: Required for ATI 1875 glEnable(target); 1876 glGenerateMipmapEXT(target); 1877 glDisable(target); 1878 } 1879 } 1880 1881 img.clearUpdateNeeded(); 1882 } 1883 1884 public void setTexture(int unit, Texture tex) { 1885 Image image = tex.getImage(); 1886 if (image.isUpdateNeeded()) { 1887 updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels(), unit); 1888 } 1889 1890 int texId = image.getId(); 1891 assert texId != -1; 1892 1893 Image[] textures = context.boundTextures; 1894 1895 int type = convertTextureType(tex.getType(), image.getMultiSamples()); 1896 // if (!context.textureIndexList.moveToNew(unit)) { 1897 // if (context.boundTextureUnit != unit){ 1898 // glActiveTexture(GL_TEXTURE0 + unit); 1899 // context.boundTextureUnit = unit; 1900 // } 1901 // glEnable(type); 1902 // } 1903 1904 if (context.boundTextureUnit != unit) { 1905 glActiveTexture(GL_TEXTURE0 + unit); 1906 context.boundTextureUnit = unit; 1907 } 1908 if (textures[unit] != image) { 1909 glBindTexture(type, texId); 1910 textures[unit] = image; 1911 1912 statistics.onTextureUse(image, true); 1913 } else { 1914 statistics.onTextureUse(image, false); 1915 } 1916 1917 setupTextureParams(tex); 1918 } 1919 1920 public void clearTextureUnits() { 1921 // IDList textureList = context.textureIndexList; 1922 // Image[] textures = context.boundTextures; 1923 // for (int i = 0; i < textureList.oldLen; i++) { 1924 // int idx = textureList.oldList[i]; 1925 // if (context.boundTextureUnit != idx){ 1926 // glActiveTexture(GL_TEXTURE0 + idx); 1927 // context.boundTextureUnit = idx; 1928 // } 1929 // glDisable(convertTextureType(textures[idx].getType())); 1930 // textures[idx] = null; 1931 // } 1932 // context.textureIndexList.copyNewToOld(); 1933 } 1934 1935 public void deleteImage(Image image) { 1936 int texId = image.getId(); 1937 if (texId != -1) { 1938 intBuf1.put(0, texId); 1939 intBuf1.position(0).limit(1); 1940 glDeleteTextures(intBuf1); 1941 image.resetObject(); 1942 1943 statistics.onDeleteTexture(); 1944 } 1945 } 1946 1947 /*********************************************************************\ 1948 |* Vertex Buffers and Attributes *| 1949 \*********************************************************************/ 1950 private int convertUsage(Usage usage) { 1951 switch (usage) { 1952 case Static: 1953 return GL_STATIC_DRAW; 1954 case Dynamic: 1955 return GL_DYNAMIC_DRAW; 1956 case Stream: 1957 return GL_STREAM_DRAW; 1958 default: 1959 throw new UnsupportedOperationException("Unknown usage type."); 1960 } 1961 } 1962 1963 private int convertFormat(Format format) { 1964 switch (format) { 1965 case Byte: 1966 return GL_BYTE; 1967 case UnsignedByte: 1968 return GL_UNSIGNED_BYTE; 1969 case Short: 1970 return GL_SHORT; 1971 case UnsignedShort: 1972 return GL_UNSIGNED_SHORT; 1973 case Int: 1974 return GL_INT; 1975 case UnsignedInt: 1976 return GL_UNSIGNED_INT; 1977 case Half: 1978 return NVHalfFloat.GL_HALF_FLOAT_NV; 1979 // return ARBHalfFloatVertex.GL_HALF_FLOAT; 1980 case Float: 1981 return GL_FLOAT; 1982 case Double: 1983 return GL_DOUBLE; 1984 default: 1985 throw new UnsupportedOperationException("Unknown buffer format."); 1986 1987 } 1988 } 1989 1990 public void updateBufferData(VertexBuffer vb) { 1991 int bufId = vb.getId(); 1992 boolean created = false; 1993 if (bufId == -1) { 1994 // create buffer 1995 glGenBuffers(intBuf1); 1996 bufId = intBuf1.get(0); 1997 vb.setId(bufId); 1998 objManager.registerForCleanup(vb); 1999 2000 //statistics.onNewVertexBuffer(); 2001 2002 created = true; 2003 } 2004 2005 // bind buffer 2006 int target; 2007 if (vb.getBufferType() == VertexBuffer.Type.Index) { 2008 target = GL_ELEMENT_ARRAY_BUFFER; 2009 if (context.boundElementArrayVBO != bufId) { 2010 glBindBuffer(target, bufId); 2011 context.boundElementArrayVBO = bufId; 2012 //statistics.onVertexBufferUse(vb, true); 2013 }else{ 2014 //statistics.onVertexBufferUse(vb, false); 2015 } 2016 } else { 2017 target = GL_ARRAY_BUFFER; 2018 if (context.boundArrayVBO != bufId) { 2019 glBindBuffer(target, bufId); 2020 context.boundArrayVBO = bufId; 2021 //statistics.onVertexBufferUse(vb, true); 2022 }else{ 2023 //statistics.onVertexBufferUse(vb, false); 2024 } 2025 } 2026 2027 int usage = convertUsage(vb.getUsage()); 2028 vb.getData().rewind(); 2029 2030 if (created || vb.hasDataSizeChanged()) { 2031 // upload data based on format 2032 switch (vb.getFormat()) { 2033 case Byte: 2034 case UnsignedByte: 2035 glBufferData(target, (ByteBuffer) vb.getData(), usage); 2036 break; 2037 // case Half: 2038 case Short: 2039 case UnsignedShort: 2040 glBufferData(target, (ShortBuffer) vb.getData(), usage); 2041 break; 2042 case Int: 2043 case UnsignedInt: 2044 glBufferData(target, (IntBuffer) vb.getData(), usage); 2045 break; 2046 case Float: 2047 glBufferData(target, (FloatBuffer) vb.getData(), usage); 2048 break; 2049 case Double: 2050 glBufferData(target, (DoubleBuffer) vb.getData(), usage); 2051 break; 2052 default: 2053 throw new UnsupportedOperationException("Unknown buffer format."); 2054 } 2055 } else { 2056 switch (vb.getFormat()) { 2057 case Byte: 2058 case UnsignedByte: 2059 glBufferSubData(target, 0, (ByteBuffer) vb.getData()); 2060 break; 2061 case Short: 2062 case UnsignedShort: 2063 glBufferSubData(target, 0, (ShortBuffer) vb.getData()); 2064 break; 2065 case Int: 2066 case UnsignedInt: 2067 glBufferSubData(target, 0, (IntBuffer) vb.getData()); 2068 break; 2069 case Float: 2070 glBufferSubData(target, 0, (FloatBuffer) vb.getData()); 2071 break; 2072 case Double: 2073 glBufferSubData(target, 0, (DoubleBuffer) vb.getData()); 2074 break; 2075 default: 2076 throw new UnsupportedOperationException("Unknown buffer format."); 2077 } 2078 } 2079 // }else{ 2080 // if (created || vb.hasDataSizeChanged()){ 2081 // glBufferData(target, vb.getData().capacity() * vb.getFormat().getComponentSize(), usage); 2082 // } 2083 // 2084 // ByteBuffer buf = glMapBuffer(target, 2085 // GL_WRITE_ONLY, 2086 // vb.getMappedData()); 2087 // 2088 // if (buf != vb.getMappedData()){ 2089 // buf = buf.order(ByteOrder.nativeOrder()); 2090 // vb.setMappedData(buf); 2091 // } 2092 // 2093 // buf.clear(); 2094 // 2095 // switch (vb.getFormat()){ 2096 // case Byte: 2097 // case UnsignedByte: 2098 // buf.put( (ByteBuffer) vb.getData() ); 2099 // break; 2100 // case Short: 2101 // case UnsignedShort: 2102 // buf.asShortBuffer().put( (ShortBuffer) vb.getData() ); 2103 // break; 2104 // case Int: 2105 // case UnsignedInt: 2106 // buf.asIntBuffer().put( (IntBuffer) vb.getData() ); 2107 // break; 2108 // case Float: 2109 // buf.asFloatBuffer().put( (FloatBuffer) vb.getData() ); 2110 // break; 2111 // case Double: 2112 // break; 2113 // default: 2114 // throw new RuntimeException("Unknown buffer format."); 2115 // } 2116 // 2117 // glUnmapBuffer(target); 2118 // } 2119 2120 vb.clearUpdateNeeded(); 2121 } 2122 2123 public void deleteBuffer(VertexBuffer vb) { 2124 int bufId = vb.getId(); 2125 if (bufId != -1) { 2126 // delete buffer 2127 intBuf1.put(0, bufId); 2128 intBuf1.position(0).limit(1); 2129 glDeleteBuffers(intBuf1); 2130 vb.resetObject(); 2131 2132 //statistics.onDeleteVertexBuffer(); 2133 } 2134 } 2135 2136 public void clearVertexAttribs() { 2137 IDList attribList = context.attribIndexList; 2138 for (int i = 0; i < attribList.oldLen; i++) { 2139 int idx = attribList.oldList[i]; 2140 glDisableVertexAttribArray(idx); 2141 context.boundAttribs[idx] = null; 2142 } 2143 context.attribIndexList.copyNewToOld(); 2144 } 2145 2146 public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { 2147 if (vb.getBufferType() == VertexBuffer.Type.Index) { 2148 throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib"); 2149 } 2150 2151 int programId = context.boundShaderProgram; 2152 if (programId > 0) { 2153 Attribute attrib = boundShader.getAttribute(vb.getBufferType()); 2154 int loc = attrib.getLocation(); 2155 if (loc == -1) { 2156 return; // not defined 2157 } 2158 if (loc == -2) { 2159 stringBuf.setLength(0); 2160 stringBuf.append("in").append(vb.getBufferType().name()).append('\0'); 2161 updateNameBuffer(); 2162 loc = glGetAttribLocation(programId, nameBuf); 2163 2164 // not really the name of it in the shader (inPosition\0) but 2165 // the internal name of the enum (Position). 2166 if (loc < 0) { 2167 attrib.setLocation(-1); 2168 return; // not available in shader. 2169 } else { 2170 attrib.setLocation(loc); 2171 } 2172 } 2173 2174 if (vb.isUpdateNeeded() && idb == null) { 2175 updateBufferData(vb); 2176 } 2177 2178 VertexBuffer[] attribs = context.boundAttribs; 2179 if (!context.attribIndexList.moveToNew(loc)) { 2180 glEnableVertexAttribArray(loc); 2181 //System.out.println("Enabled ATTRIB IDX: "+loc); 2182 } 2183 if (attribs[loc] != vb) { 2184 // NOTE: Use id from interleaved buffer if specified 2185 int bufId = idb != null ? idb.getId() : vb.getId(); 2186 assert bufId != -1; 2187 if (context.boundArrayVBO != bufId) { 2188 glBindBuffer(GL_ARRAY_BUFFER, bufId); 2189 context.boundArrayVBO = bufId; 2190 //statistics.onVertexBufferUse(vb, true); 2191 }else{ 2192 //statistics.onVertexBufferUse(vb, false); 2193 } 2194 2195 glVertexAttribPointer(loc, 2196 vb.getNumComponents(), 2197 convertFormat(vb.getFormat()), 2198 vb.isNormalized(), 2199 vb.getStride(), 2200 vb.getOffset()); 2201 2202 attribs[loc] = vb; 2203 } 2204 } else { 2205 throw new IllegalStateException("Cannot render mesh without shader bound"); 2206 } 2207 } 2208 2209 public void setVertexAttrib(VertexBuffer vb) { 2210 setVertexAttrib(vb, null); 2211 } 2212 2213 public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) { 2214 if (count > 1) { 2215 ARBDrawInstanced.glDrawArraysInstancedARB(convertElementMode(mode), 0, 2216 vertCount, count); 2217 } else { 2218 glDrawArrays(convertElementMode(mode), 0, vertCount); 2219 } 2220 } 2221 2222 public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { 2223 if (indexBuf.getBufferType() != VertexBuffer.Type.Index) { 2224 throw new IllegalArgumentException("Only index buffers are allowed as triangle lists."); 2225 } 2226 2227 if (indexBuf.isUpdateNeeded()) { 2228 updateBufferData(indexBuf); 2229 } 2230 2231 int bufId = indexBuf.getId(); 2232 assert bufId != -1; 2233 2234 if (context.boundElementArrayVBO != bufId) { 2235 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufId); 2236 context.boundElementArrayVBO = bufId; 2237 //statistics.onVertexBufferUse(indexBuf, true); 2238 }else{ 2239 //statistics.onVertexBufferUse(indexBuf, true); 2240 } 2241 2242 int vertCount = mesh.getVertexCount(); 2243 boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); 2244 2245 if (mesh.getMode() == Mode.Hybrid) { 2246 int[] modeStart = mesh.getModeStart(); 2247 int[] elementLengths = mesh.getElementLengths(); 2248 2249 int elMode = convertElementMode(Mode.Triangles); 2250 int fmt = convertFormat(indexBuf.getFormat()); 2251 int elSize = indexBuf.getFormat().getComponentSize(); 2252 int listStart = modeStart[0]; 2253 int stripStart = modeStart[1]; 2254 int fanStart = modeStart[2]; 2255 int curOffset = 0; 2256 for (int i = 0; i < elementLengths.length; i++) { 2257 if (i == stripStart) { 2258 elMode = convertElementMode(Mode.TriangleStrip); 2259 } else if (i == fanStart) { 2260 elMode = convertElementMode(Mode.TriangleStrip); 2261 } 2262 int elementLength = elementLengths[i]; 2263 2264 if (useInstancing) { 2265 ARBDrawInstanced.glDrawElementsInstancedARB(elMode, 2266 elementLength, 2267 fmt, 2268 curOffset, 2269 count); 2270 } else { 2271 glDrawRangeElements(elMode, 2272 0, 2273 vertCount, 2274 elementLength, 2275 fmt, 2276 curOffset); 2277 } 2278 2279 curOffset += elementLength * elSize; 2280 } 2281 } else { 2282 if (useInstancing) { 2283 ARBDrawInstanced.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()), 2284 indexBuf.getData().limit(), 2285 convertFormat(indexBuf.getFormat()), 2286 0, 2287 count); 2288 } else { 2289 glDrawRangeElements(convertElementMode(mesh.getMode()), 2290 0, 2291 vertCount, 2292 indexBuf.getData().limit(), 2293 convertFormat(indexBuf.getFormat()), 2294 0); 2295 } 2296 } 2297 } 2298 2299 /*********************************************************************\ 2300 |* Render Calls *| 2301 \*********************************************************************/ 2302 public int convertElementMode(Mesh.Mode mode) { 2303 switch (mode) { 2304 case Points: 2305 return GL_POINTS; 2306 case Lines: 2307 return GL_LINES; 2308 case LineLoop: 2309 return GL_LINE_LOOP; 2310 case LineStrip: 2311 return GL_LINE_STRIP; 2312 case Triangles: 2313 return GL_TRIANGLES; 2314 case TriangleFan: 2315 return GL_TRIANGLE_FAN; 2316 case TriangleStrip: 2317 return GL_TRIANGLE_STRIP; 2318 default: 2319 throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode); 2320 } 2321 } 2322 2323 public void updateVertexArray(Mesh mesh) { 2324 int id = mesh.getId(); 2325 if (id == -1) { 2326 IntBuffer temp = intBuf1; 2327 ARBVertexArrayObject.glGenVertexArrays(temp); 2328 id = temp.get(0); 2329 mesh.setId(id); 2330 } 2331 2332 if (context.boundVertexArray != id) { 2333 ARBVertexArrayObject.glBindVertexArray(id); 2334 context.boundVertexArray = id; 2335 } 2336 2337 VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); 2338 if (interleavedData != null && interleavedData.isUpdateNeeded()) { 2339 updateBufferData(interleavedData); 2340 } 2341 2342 IntMap<VertexBuffer> buffers = mesh.getBuffers(); 2343 for (Entry<VertexBuffer> entry : buffers) { 2344 VertexBuffer vb = entry.getValue(); 2345 2346 if (vb.getBufferType() == Type.InterleavedData 2347 || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers 2348 || vb.getBufferType() == Type.Index) { 2349 continue; 2350 } 2351 2352 if (vb.getStride() == 0) { 2353 // not interleaved 2354 setVertexAttrib(vb); 2355 } else { 2356 // interleaved 2357 setVertexAttrib(vb, interleavedData); 2358 } 2359 } 2360 } 2361 2362 private void renderMeshVertexArray(Mesh mesh, int lod, int count) { 2363 if (mesh.getId() == -1){ 2364 updateVertexArray(mesh); 2365 }else{ 2366 // TODO: Check if it was updated 2367 } 2368 2369 if (context.boundVertexArray != mesh.getId()) { 2370 ARBVertexArrayObject.glBindVertexArray(mesh.getId()); 2371 context.boundVertexArray = mesh.getId(); 2372 } 2373 2374 // IntMap<VertexBuffer> buffers = mesh.getBuffers(); 2375 VertexBuffer indices = null; 2376 if (mesh.getNumLodLevels() > 0) { 2377 indices = mesh.getLodLevel(lod); 2378 } else { 2379 indices = mesh.getBuffer(Type.Index); 2380 } 2381 if (indices != null) { 2382 drawTriangleList(indices, mesh, count); 2383 } else { 2384 drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); 2385 } 2386 clearVertexAttribs(); 2387 clearTextureUnits(); 2388 } 2389 2390 private void renderMeshDefault(Mesh mesh, int lod, int count) { 2391 VertexBuffer indices = null; 2392 2393 VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); 2394 if (interleavedData != null && interleavedData.isUpdateNeeded()) { 2395 updateBufferData(interleavedData); 2396 } 2397 2398 // IntMap<VertexBuffer> buffers = mesh.getBuffers(); 2399 SafeArrayList<VertexBuffer> buffersList = mesh.getBufferList(); 2400 2401 if (mesh.getNumLodLevels() > 0) { 2402 indices = mesh.getLodLevel(lod); 2403 } else { 2404 indices = mesh.getBuffer(Type.Index); 2405 } 2406 2407 // for (Entry<VertexBuffer> entry : buffers) { 2408 // VertexBuffer vb = entry.getValue(); 2409 for (VertexBuffer vb : mesh.getBufferList().getArray()){ 2410 if (vb.getBufferType() == Type.InterleavedData 2411 || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers 2412 || vb.getBufferType() == Type.Index) { 2413 continue; 2414 } 2415 2416 if (vb.getStride() == 0) { 2417 // not interleaved 2418 setVertexAttrib(vb); 2419 } else { 2420 // interleaved 2421 setVertexAttrib(vb, interleavedData); 2422 } 2423 } 2424 2425 if (indices != null) { 2426 drawTriangleList(indices, mesh, count); 2427 } else { 2428 drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); 2429 } 2430 clearVertexAttribs(); 2431 clearTextureUnits(); 2432 } 2433 2434 public void renderMesh(Mesh mesh, int lod, int count) { 2435 if (mesh.getVertexCount() == 0) { 2436 return; 2437 } 2438 2439 if (context.pointSprite && mesh.getMode() != Mode.Points){ 2440 // XXX: Hack, disable point sprite mode if mesh not in point mode 2441 if (context.boundTextures[0] != null){ 2442 if (context.boundTextureUnit != 0){ 2443 glActiveTexture(GL_TEXTURE0); 2444 context.boundTextureUnit = 0; 2445 } 2446 glDisable(GL_POINT_SPRITE); 2447 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); 2448 context.pointSprite = false; 2449 } 2450 } 2451 2452 if (context.pointSize != mesh.getPointSize()) { 2453 glPointSize(mesh.getPointSize()); 2454 context.pointSize = mesh.getPointSize(); 2455 } 2456 if (context.lineWidth != mesh.getLineWidth()) { 2457 glLineWidth(mesh.getLineWidth()); 2458 context.lineWidth = mesh.getLineWidth(); 2459 } 2460 2461 statistics.onMeshDrawn(mesh, lod); 2462 // if (GLContext.getCapabilities().GL_ARB_vertex_array_object){ 2463 // renderMeshVertexArray(mesh, lod, count); 2464 // }else{ 2465 renderMeshDefault(mesh, lod, count); 2466 // } 2467 } 2468 } 2469