1 module prova.graphics.rendertarget; 2 3 import prova.assets; 4 import prova.attachables; 5 import prova.graphics; 6 import prova.math; 7 import std.typecons; 8 9 /// 10 class RenderTarget 11 { 12 private static uint currentFrameBuffer = -1; 13 14 /// 15 SpriteBatch spriteBatch; 16 /// 17 protected Matrix projection; 18 19 private GraphicsContext _context; 20 private uint renderBufferId; 21 private uint _frameBufferId; 22 private Texture _texture; 23 private int _width; 24 private int _height; 25 private bool begun; 26 27 /// 28 this(GraphicsContext context, int width, int height) 29 { 30 _context = context; 31 spriteBatch = new SpriteBatch(context); 32 _texture = new Texture(width, height); 33 _width = width; 34 _height = height; 35 36 createFrameBuffer(); 37 createRenderBuffer(); 38 } 39 40 private void createFrameBuffer() 41 { 42 glGenFramebuffers(1, &_frameBufferId); 43 bindFrameBuffer(); 44 45 // attach texture to the frame buffer 46 glFramebufferTexture2D( 47 GL_FRAMEBUFFER, 48 GL_COLOR_ATTACHMENT0, 49 GL_TEXTURE_2D, 50 _texture.id, 51 0 52 ); 53 54 glDepthFunc(GL_LEQUAL); 55 glEnable(GL_BLEND); 56 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 57 glViewport(0, 0, width, height); 58 } 59 60 private void createRenderBuffer() 61 { 62 glGenRenderbuffers(1, &renderBufferId); 63 glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId); 64 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); 65 66 bindFrameBuffer(); 67 68 glFramebufferRenderbuffer( 69 GL_FRAMEBUFFER, 70 GL_DEPTH_ATTACHMENT, 71 GL_RENDERBUFFER, 72 renderBufferId 73 ); 74 } 75 76 /// 77 @property GraphicsContext context() 78 { 79 return _context; 80 } 81 82 /// 83 void resize(int width, int height) 84 { 85 _width = width; 86 _height = height; 87 88 texture.recreate(null, width, height); 89 90 glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId); 91 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); 92 93 // update settings 94 glViewport(0, 0, width, height); 95 } 96 97 /// 98 @property int width() 99 { 100 return _width; 101 } 102 103 /// 104 @property int height() 105 { 106 return _height; 107 } 108 109 /// 110 @property Texture texture() 111 { 112 return _texture; 113 } 114 115 /// 116 @property uint frameBufferId() 117 { 118 return _frameBufferId; 119 } 120 121 package(prova) void bindFrameBuffer() 122 { 123 bindFrameBuffer(_frameBufferId); 124 } 125 126 /// Updates projection and prepares batches 127 void begin(Matrix projection) 128 { 129 assert(!begun, "RenderTarget should not already be started"); 130 131 spriteBatch.begin(this, projection); 132 133 this.projection = projection; 134 begun = true; 135 } 136 137 /// Finishes the batch 138 void end() 139 { 140 assert(begun, "RenderTarget not ready, call begin(Matrix projection)"); 141 142 spriteBatch.end(); 143 begun = false; 144 } 145 146 /// Draws mesh using a flat shader 147 void drawMesh(Mesh mesh, Matrix transform, DrawMode mode, Color color) 148 { 149 assert(begun, "RenderTarget not ready, call begin(Matrix projection)"); 150 151 context.flatShader.setMatrix("transform", projection * transform); 152 context.flatShader.setVector4("color", color); 153 context.flatShader.drawMesh(mesh, this, mode); 154 } 155 156 /// 157 void drawSprite(Sprite sprite, Matrix transform) 158 { 159 spriteBatch.batchSprite(sprite, transform); 160 } 161 162 /// 163 void drawSprite(AnimatedSprite sprite, Matrix transform) 164 { 165 spriteBatch.batchSprite(sprite, transform); 166 } 167 168 /// 169 void clear() 170 { 171 bindFrameBuffer(); 172 173 glClearDepth(1); 174 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 175 } 176 177 package(prova) static void bindFrameBuffer(uint id) 178 { 179 if(id == currentFrameBuffer) 180 return; 181 182 glBindFramebuffer(GL_FRAMEBUFFER, id); 183 currentFrameBuffer = id; 184 } 185 186 ~this() 187 { 188 glDeleteFramebuffers(1, &_frameBufferId); 189 glDeleteRenderbuffers(1, &renderBufferId); 190 } 191 }