1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Graphics.Sprite;
25 
26 private {
27 	import derelict.opengl3.gl;
28 	
29 	import Dgame.Graphics.Drawable;
30 	import Dgame.Graphics.Transformable;
31 	import Dgame.Graphics.Texture;
32 	import Dgame.Graphics.Shape;
33 	import Dgame.Graphics.Blend;
34 	import Dgame.Math.Vector2;
35 	import Dgame.Math.Rect;
36 	import Dgame.System.VertexRenderer;
37 }
38 
39 /**
40  * Sprite represents a drawable object and maintains a texture and his position.
41  *
42  * Author: rschuett
43  */
44 class Sprite : Transformable, Drawable, Blendable {
45 protected:
46 	Texture _tex;
47 	Blend _blend;
48 
49 protected:
50 	void _render() const in {
51 		assert(this._tex !is null, "Sprite couldn't rendered, because the Texture is null.");
52 	} body {
53 		glPushMatrix();
54 		scope(exit) glPopMatrix();
55 
56 		super._applyTranslation();
57 
58 		if (!glIsEnabled(GL_TEXTURE_2D))
59 			glEnable(GL_TEXTURE_2D);
60 
61 		glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT);
62 		scope(exit) glPopAttrib();
63 
64 		if (this._blend !is null)
65 			this._blend.applyBlending();
66 
67 		const ShortRect clipRect = this.getClipRect();
68 
69 		float dx = 0f;
70 		float dy = 0f;
71 		float dw = clipRect.width;
72 		float dh = clipRect.height;
73 
74 		float[12] vertices = [
75 			dx,	     dy,      0f,	
76 			dx + dw, dy,      0f,
77 			dx + dw, dy + dh, 0f,
78 			dx,      dy + dh, 0f
79 		];
80 
81 		float[8] texCoords = this._getTextureCoordinates();
82 
83 		VertexRenderer.pointTo(Target.Vertex, &vertices[0]);
84 		VertexRenderer.pointTo(Target.TexCoords, &texCoords[0]);
85 
86 		scope(exit) {
87 			VertexRenderer.disableAllStates();
88 			this._tex.unbind();
89 		}
90 
91 		this._tex.bind();
92 		VertexRenderer.drawArrays(Shape.Type.TriangleFan, vertices.length);
93 	}
94 
95 	float[8] _getTextureCoordinates() const pure nothrow {
96 		return [0f, 0f, 1f, 0f, 1f, 1f, 0f, 1f];
97 	}
98 	
99 public:
100 	/**
101 	 * CTor
102 	 */
103 	this() {
104 		this(null);
105 	}
106 	
107 	/**
108 	 * CTor
109 	 */
110 	this(Texture tex) {
111 		this.setTexture(tex);
112 	}
113 	
114 	/**
115 	 * Calculate, store and return the center point.
116 	 * Usefull for e.g. rotate.
117 	 */
118 	override ref const(Vector2s) calculateCenter() pure nothrow {
119 		super.setCenter(this._tex.width / 2, this._tex.height / 2);
120 		return super.getCenter();
121 	}
122 
123 	/**
124 	 * Check whether the bounding box of this Sprite collide
125 	 * with the bounding box of another Sprite
126 	 */
127 	bool collideWith(const Sprite rhs) const {
128 		const ShortRect rhs_clip = rhs.getClipRect();
129 
130 		return this.collideWith(rhs_clip);
131 	}
132 	
133 	/**
134 	 * Check whether the bounding box of this Sprite collide
135 	 * with the given Rect
136 	 */
137 	bool collideWith(ref const ShortRect rect) const {
138 		return this.getClipRect().intersects(rect);
139 	}
140 	
141 	/**
142 	 * Rvalue version
143 	 */
144 	bool collideWith(const ShortRect rect) const {
145 		return this.collideWith(rect);
146 	}
147 
148 	/**
149 	 * Returns the current Clip Rect, the area,
150 	 * where the Sprite lies with the same size as the Sprite.
151 	 */
152 	ShortRect getClipRect() const pure nothrow {
153 		return ShortRect(cast(short) super.position.x, cast(short) super.position.y,
154 		                 this._tex.width, this._tex.height);
155 	}
156 
157 final:
158 	/**
159 	 * Set (or reset) the current Blend instance.
160 	 */
161 	void setBlend(Blend blend) pure nothrow {
162 		this._blend = blend;
163 	}
164 	
165 	/**
166 	 * Returns the current Blend instance, or null.
167 	 */
168 	inout(Blend) getBlend() inout pure nothrow {
169 		return this._blend;
170 	}
171 
172 	/**
173 	 * Returns the current width (the width of the current texture).
174 	 */
175 	@property
176 	ushort width() const pure nothrow
177 	in {
178 		assert(this._tex !is null);
179 	} body {
180 		return this._tex.width;
181 	}
182 
183 	/**
184 	 * Returns the current height (the height of the current texture).
185 	 */
186 	@property
187 	ushort height() const pure nothrow
188 	in {
189 		assert(this._tex !is null);
190 	} body {
191 		return this._tex.height;
192 	}
193 
194 	/**
195 	 * Check if the current Sprite has already a Texture/Image.
196 	 * If not, nothing can be drawn.
197 	 * But it does not check if the current Texture is valid.
198 	 */
199 	bool hasTexture() const pure nothrow {
200 		return this._tex !is null;
201 	}
202 	
203 	/**
204 	 * Set or replace the current Texture.
205 	 */
206 	void setTexture(Texture tex) in {
207 		assert(tex !is null, "Cannot set a null Texture.");
208 	} body {
209 		this._tex = tex;
210 	}
211 	
212 	/**
213 	 * Returns the current Texture or null if there is none.
214 	 */
215 	inout(Texture) getTexture() inout pure nothrow {
216 		return this._tex;
217 	}
218 }