1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <cstring>
4 #include <assert.h>
5 #include "Rendering/GL/myGL.h"
6 
7 #include "TerrainBase.h"
8 #include "TerrainNode.h"
9 #include "TerrainTexture.h"
10 
11 namespace terrain {
12 
QuadRenderData()13 	QuadRenderData::QuadRenderData()
14 		: normalMap(0)
15 		, normalMapW(0)
16 		, normalMapTexWidth(0)
17 		, vertexSize(0)
18 		, index(0)
19 		, used(false)
20 		, quad(0)
21 	{
22 	}
23 
~QuadRenderData()24 	QuadRenderData::~QuadRenderData()
25 	{
26 		// delete normal map
27 		if (normalMap) {
28 			glDeleteTextures(1, &normalMap);
29 			normalMap = 0;
30 		}
31 	}
32 
GetDataSize()33 	uint QuadRenderData::GetDataSize()
34 	{
35 		return normalMapW * normalMapW * 3 + vertexBuffer.GetSize();
36 	}
37 
RenderDataManager(Heightmap * rhm,QuadMap * rqm)38 	RenderDataManager::RenderDataManager(Heightmap* rhm, QuadMap* rqm)
39 		: normalDataAllocates(0)
40 		, renderDataAllocates(0)
41 		, roothm(rhm)
42 		, rootQMap(rqm)
43 	{
44 	}
45 
~RenderDataManager()46 	RenderDataManager::~RenderDataManager()
47 	{
48 		for (size_t a = 0; a < freeRD.size(); a++)
49 			delete freeRD[a];
50 		for (size_t a = 0; a < qrd.size(); a++)
51 			delete qrd[a];
52 	}
53 
Allocate()54 	QuadRenderData* RenderDataManager::Allocate()
55 	{
56 		QuadRenderData* rd;
57 		if (!freeRD.empty()) {
58 			rd = freeRD.back();
59 			freeRD.pop_back();
60 		}
61 		else rd = new QuadRenderData;
62 
63 		rd->index = qrd.size();
64 		qrd.push_back(rd);
65 
66 		renderDataAllocates++;
67 
68 		return rd;
69 	}
70 
PruneFreeList(int maxFreeRD)71 	void RenderDataManager::PruneFreeList(int maxFreeRD)
72 	{
73 		const size_t maxFreeRDUnsigned = (maxFreeRD < 0) ? 0 : maxFreeRD;
74 		while (freeRD.size() > maxFreeRDUnsigned) {
75 			delete freeRD.back();
76 			freeRD.pop_back();
77 		}
78 	}
79 
Free(QuadRenderData * rd)80 	void RenderDataManager::Free(QuadRenderData* rd)
81 	{
82 		if (rd->index < qrd.size()-1) {
83 			qrd.back()->index = rd->index;
84 			std::swap(qrd.back(), qrd[rd->index]);
85 		}
86 		qrd.pop_back();
87 
88 		freeRD.push_back(rd);
89 		rd->GetQuad()->renderData = 0;
90 
91 		rd->GetQuad()->FreeCachedTexture();
92 	}
93 
UpdateRect(int sx,int sy,int w,int h)94 	void RenderDataManager::UpdateRect(int sx, int sy,int w,int h)
95 	{
96 		for (size_t a = 0; a < qrd.size(); a++)
97 		{
98 			QuadRenderData* rd = qrd[a];
99 			TQuad* q = rd->GetQuad();
100 
101 			// rect vs rect collision:
102 			if (q->sqPos.x + q->width >= sx && q->sqPos.y + q->width >= sy &&
103 				q->sqPos.x <= sx + w && q->sqPos.y <= sy + h)
104 			{
105 				assert(q->renderData==qrd[a]);
106 				Free(q->renderData);
107 			}
108 		}
109 	}
110 
111 	// delete all renderdata that is not used this frame and has maxlod < VBufMinDetail
FreeUnused()112 	void RenderDataManager::FreeUnused()
113 	{
114 		for (int a = 0; a < qrd.size(); a++)
115 		{
116 			QuadRenderData* rd = qrd[a];
117 
118 			if (rd->used) {
119 				rd->used = false;
120 				continue;
121 			}
122 
123 			if (rd->GetQuad()->maxLodValue < VBufMinDetail) {
124 				Free(rd);
125 				a--;
126 			}
127 		}
128 	}
129 
InitializeNode(TQuad * q)130 	void RenderDataManager::InitializeNode(TQuad* q)
131 	{
132 		assert(!q->renderData);
133 
134 		QuadRenderData* rd = q->renderData = Allocate();
135 
136 		// Allocate vertex data space
137 		const size_t vertexSize = q->GetVertexSize();
138 		if (static_cast<int>(vertexSize) != rd->vertexSize) {
139 			const size_t size = NUM_VERTICES * vertexSize;
140 
141 			if (rd->vertexBuffer.GetSize() != size) {
142 				rd->vertexBuffer.Init(size);
143 			}
144 			rd->vertexSize = vertexSize;
145 		}
146 
147 		// build the vertex buffer
148 		Vector3* v = (Vector3*)rd->vertexBuffer.LockData();
149 
150 		uint vda = q->textureSetup->vertexDataReq; 		// vertex data requirements
151 		const Heightmap* hm = roothm->GetLevel(q->depth); // get the right heightmap level
152 
153 		for (int y = q->hmPos.y; y <= (q->hmPos.y + QUAD_W); y++)
154 			for (int x = q->hmPos.x; x <= (q->hmPos.x + QUAD_W); x++)
155 			{
156 				*(v++) = Vector3(x * hm->squareSize, hm->atSynced(x, y), y * hm->squareSize);
157 
158 				Vector3 tangent, binormal;
159 				CalculateTangents(hm, x,y, tangent, binormal);
160 				Vector3 normal = binormal.cross(tangent);
161 				normal.ANormalize();
162 
163 				if (vda & VRT_Normal)
164 					*(v++) = normal;
165 
166 				if (vda & VRT_TangentSpaceMatrix)
167 				{
168 					tangent.ANormalize();
169 					binormal.ANormalize();
170 
171 					// orthonormal matrix, so inverse=transpose
172 					// Take the inverse of the tangent space -> world space transformation
173 					Vector3* tgs2ws = v;
174 					tgs2ws[0] = Vector3(tangent.x, binormal.x, normal.x);
175 					tgs2ws[1] = Vector3(tangent.y, binormal.y, normal.y);
176 					tgs2ws[2] = Vector3(tangent.z, binormal.z, normal.z);
177 					v += 3;
178 				}
179 			}
180 		rd->vertexBuffer.UnlockData();
181 		rd->SetQuad(q);
182 	}
183 
184 
InitializeNodeNormalMap(TQuad * q,int cfgNormalMapLevel)185 	void RenderDataManager::InitializeNodeNormalMap(TQuad* q, int cfgNormalMapLevel)
186 	{
187 		QuadRenderData* rd = q->renderData;
188 
189 		if (q->isLeaf()) {
190 			if (rd->normalMap) {
191 				glDeleteTextures(1, &rd->normalMap);
192 				rd->normalMap = 0;
193 				rd->normalMapW = 0;
194 			}
195 			return;
196 		}
197 
198 		// find the right level heightmap to generate the normal map from
199 		Heightmap* hm = roothm;
200 		int level = 0;
201 		for (; hm->highDetail; hm = hm->highDetail, level++)
202 			if (level == q->depth + cfgNormalMapLevel) break;
203 
204 		// calculate dimensions
205 		const int scale = 1 << (level - q->depth);
206 		size_t w = QUAD_W * scale + 1;
207 		const size_t h = w;
208 		const int startx = q->hmPos.x * scale;
209 
210 		// use power-of-two texture sizes if required
211 		size_t texw = 1;
212 		//if (GLEW_ARB_texture_non_power_of_two) texw = w;
213 		//else
214 		while (texw < w) texw *= 2;
215 
216 		// if not yet created, create a texture for it
217 		GLuint texture;
218 
219 		if (rd->normalMap && (rd->normalMapW == w) && (rd->normalMapTexWidth == texw)) {
220 			texture = rd->normalMap;
221 			glBindTexture(GL_TEXTURE_2D, texture);
222 		} else {
223 			if (rd->normalMap)
224 				glDeleteTextures(1,&rd->normalMap);
225 
226 			glGenTextures(1, &texture);
227 			glBindTexture(GL_TEXTURE_2D, texture);
228 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
229 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
230 
231 			rd->normalMap = texture;
232 			rd->normalMapW = w;
233 			rd->normalMapTexWidth = texw;
234 		}
235 
236 		// allocate temporary storage for normals
237 		uchar* normals = new uchar[texw*texw*3];
238 
239 		// calculate normals
240 		for (size_t y=0; y<h; y++) {
241 			const uchar* src = hm->GetNormal(startx, y + q->hmPos.y * scale);
242 			memcpy(&normals [3 * y * texw], src, 3 * w);
243 		}
244 
245 		// fill texture
246 		glTexImage2D(GL_TEXTURE_2D, 0, 3, texw,texw,0, GL_RGB, GL_UNSIGNED_BYTE, normals);
247 
248 		delete[] normals;
249 	}
250 
251 };
252