1 /*
2  Copyright (c) 2013 yvt
3 
4  This file is part of OpenSpades.
5 
6  OpenSpades is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OpenSpades is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OpenSpades.  If not, see <http://www.gnu.org/licenses/>.
18 
19  */
20 
21 #include "SWModelRenderer.h"
22 #include "SWModel.h"
23 #include "SWRenderer.h"
24 #include "SWUtils.h"
25 #include <Core/Bitmap.h>
26 #include <Core/Debug.h>
27 #include <Core/Math.h>
28 
29 namespace spades {
30 	namespace draw {
SWModelRenderer(SWRenderer * r,SWFeatureLevel level)31 		SWModelRenderer::SWModelRenderer(SWRenderer *r, SWFeatureLevel level)
32 		    : r(r), level(level) {}
33 
~SWModelRenderer()34 		SWModelRenderer::~SWModelRenderer() {}
35 
36 		struct ZVals {
37 			float zv[512];
ZValsspades::draw::ZVals38 			ZVals() {
39 				for (int i = 0; i < 512; i++)
40 					zv[i] = static_cast<float>(i);
41 			}
operator []spades::draw::ZVals42 			inline float operator[](size_t idx) const { return zv[idx]; }
43 		};
44 		static ZVals zvals;
45 
46 		template <SWFeatureLevel lvl>
RenderInner(spades::draw::SWModel * model,const client::ModelRenderParam & param)47 		void SWModelRenderer::RenderInner(spades::draw::SWModel *model,
48 		                                  const client::ModelRenderParam &param) {
49 			auto &mat = param.matrix;
50 			auto origin = mat.GetOrigin();
51 			auto axis1 = mat.GetAxis(0);
52 			auto axis2 = mat.GetAxis(1);
53 			auto axis3 = mat.GetAxis(2);
54 			auto *rawModel = model->GetRawModel();
55 			auto rawModelOrigin = rawModel->GetOrigin();
56 			rawModelOrigin += 0.25f;
57 			origin += axis1 * rawModelOrigin.x;
58 			origin += axis2 * rawModelOrigin.y;
59 			origin += axis3 * rawModelOrigin.z;
60 
61 			int w = rawModel->GetWidth();
62 			int h = rawModel->GetHeight();
63 			// int d = rawModel->GetDepth();
64 
65 			// evaluate brightness for each normals
66 			uint8_t brights[3 * 3 * 3];
67 			{
68 				auto lightVec = MakeVector3(0.f, -0.707f, -0.707f);
69 				float dot1 = Vector3::Dot(axis1, lightVec) * fastRSqrt(axis1.GetPoweredLength());
70 				float dot2 = Vector3::Dot(axis2, lightVec) * fastRSqrt(axis2.GetPoweredLength());
71 				float dot3 = Vector3::Dot(axis3, lightVec) * fastRSqrt(axis3.GetPoweredLength());
72 				for (int x = 0; x < 3; x++) {
73 					float d = 0.0;
74 					int cnt = 0;
75 					switch (x) {
76 						case 0:
77 							d = -dot1;
78 							cnt = 1;
79 							break;
80 						case 1:
81 							d = 0.f;
82 							cnt = 0;
83 							break;
84 						case 2:
85 							d = dot1;
86 							cnt = 1;
87 							break;
88 					}
89 					for (int y = 0; y < 3; y++) {
90 						auto d2 = d;
91 						auto cnt2 = cnt;
92 						switch (y) {
93 							case 0:
94 								d2 -= dot2;
95 								cnt2++;
96 								break;
97 							case 1: break;
98 							case 2:
99 								d2 += dot2;
100 								cnt2++;
101 								break;
102 						}
103 						for (int z = 0; z < 3; z++) {
104 							auto d3 = d;
105 							auto cnt3 = cnt2;
106 							switch (y) {
107 								case 0:
108 									d3 -= dot3;
109 									cnt3++;
110 									break;
111 								case 1: break;
112 								case 2:
113 									d3 += dot3;
114 									cnt3++;
115 									break;
116 							}
117 							switch (cnt3) {
118 								case 2: d3 *= 0.707f; break;
119 								case 3: d3 *= 0.57735f; break;
120 							}
121 							d3 = 192.f + d3 * 62.f;
122 							brights[x + y * 3 + z * 9] = static_cast<uint8_t>(d3);
123 						}
124 					}
125 				}
126 			}
127 
128 			// compute center coord. for culling
129 			{
130 				auto center = origin;
131 				auto localCenter = model->GetCenter();
132 				center += axis1 * localCenter.x;
133 				center += axis2 * localCenter.y;
134 				center += axis3 * localCenter.z;
135 
136 				float largestAxis = axis1.GetPoweredLength();
137 				largestAxis = std::max(largestAxis, axis2.GetPoweredLength());
138 				largestAxis = std::max(largestAxis, axis3.GetPoweredLength());
139 
140 				if (!r->SphereFrustrumCull(center, model->GetRadius() * sqrtf(largestAxis)))
141 					return;
142 			}
143 
144 			Bitmap *fbmp = r->fb;
145 			auto *fb = fbmp->GetPixels();
146 			int fw = fbmp->GetWidth();
147 			int fh = fbmp->GetHeight();
148 			auto *db = r->depthBuffer.data();
149 
150 			Matrix4 viewproj = r->GetProjectionViewMatrix();
151 			Vector4 ndc2scrscale = {fw * 0.5f, -fh * 0.5f, 1.f, 1.f};
152 			// Vector4 ndc2scroff = {fw * 0.5f, fh * 0.5f, 0.f, 0.f};
153 			int ndc2scroffX = fw >> 1;
154 			int ndc2scroffY = fh >> 1;
155 
156 			// render each points
157 			auto tOrigin = viewproj * MakeVector4(origin.x, origin.y, origin.z, 1.f);
158 			auto tAxis1 = viewproj * MakeVector4(axis1.x, axis1.y, axis1.z, 0.f);
159 			auto tAxis2 = viewproj * MakeVector4(axis2.x, axis2.y, axis2.z, 0.f);
160 			auto tAxis3 = viewproj * MakeVector4(axis3.x, axis3.y, axis3.z, 0.f);
161 			tOrigin *= ndc2scrscale;
162 			tAxis1 *= ndc2scrscale;
163 			tAxis2 *= ndc2scrscale;
164 			tAxis3 *= ndc2scrscale;
165 
166 			float pointDiameter; // = largestAxis * 0.55f * fh * 0.5f;
167 			{
168 				float largestAxis = tAxis1.GetPoweredLength();
169 				largestAxis = std::max(largestAxis, tAxis2.GetPoweredLength());
170 				largestAxis = std::max(largestAxis, tAxis3.GetPoweredLength());
171 				pointDiameter = sqrtf(largestAxis);
172 			}
173 
174 			uint32_t customColor;
175 			customColor = ToFixed8(param.customColor.z) | (ToFixed8(param.customColor.y) << 8) |
176 			              (ToFixed8(param.customColor.x) << 16);
177 
178 			auto v1 = tOrigin;
179 			float zNear = r->sceneDef.zNear;
180 			for (int x = 0; x < w; x++) {
181 				auto v2 = v1;
182 				for (int y = 0; y < h; y++) {
183 					auto *mp = &model->renderData[model->renderDataAddr[x + y * w]];
184 					while (*mp != -1) {
185 						uint32_t data = *(mp++);
186 						uint32_t normal = *(mp++);
187 						int z = static_cast<int>(data >> 24);
188 						// SPAssert(z < d);
189 						SPAssert(z >= 0);
190 
191 						auto vv = v2 + tAxis3 * zvals[z];
192 						if (vv.z < zNear)
193 							continue;
194 
195 						// save Z value (don't divide this by W!)
196 						float zval = vv.z;
197 
198 						// use vv.z for point radius to be divided by W
199 						vv.z = pointDiameter;
200 
201 						// perspective division
202 						float scl = fastRcp(vv.w);
203 						vv *= scl;
204 
205 						int ix = static_cast<int>(vv.x) + ndc2scroffX;
206 						int iy = static_cast<int>(vv.y) + ndc2scroffY;
207 						int idm = static_cast<int>(vv.z + .99f);
208 						idm = std::max(1, idm);
209 						int minX = ix - (idm >> 1);
210 						int minY = iy - (idm >> 1);
211 						if (minX >= fw || minY >= fh)
212 							continue;
213 						int maxX = ix + idm;
214 						int maxY = iy + idm;
215 						if (maxX <= 0 || maxY <= 0)
216 							continue;
217 
218 						minX = std::max(minX, 0);
219 						minY = std::max(minY, 0);
220 						maxX = std::min(maxX, fw);
221 						maxY = std::min(maxY, fh);
222 
223 						auto *fb2 = fb + (minX + minY * fw);
224 						auto *db2 = db + (minX + minY * fw);
225 						int w = maxX - minX;
226 
227 						uint32_t color = data & 0xffffff;
228 						if (color == 0)
229 							color = customColor;
230 
231 						SPAssert(normal < 27);
232 						int bright = brights[normal];
233 #if ENABLE_SSE2
234 						if (lvl == SWFeatureLevel::SSE2) {
235 							auto m = _mm_setr_epi32(color, 0, 0, 0);
236 							auto f = _mm_set1_epi16(bright << 8);
237 
238 							m = _mm_unpacklo_epi8(m, _mm_setzero_si128());
239 							m = _mm_mulhi_epu16(m, f);
240 							m = _mm_packus_epi16(m, m);
241 
242 							_mm_store_ss(reinterpret_cast<float *>(&color), _mm_castsi128_ps(m));
243 						} else
244 #endif
245 						{
246 							uint32_t c1 = color & 0xff00;
247 							uint32_t c2 = color & 0xff00ff;
248 							c1 *= bright;
249 							c2 *= bright;
250 							color = ((c1 & 0xff0000) | (c2 & 0xff00ff00)) >> 8;
251 						}
252 
253 						for (int yy = minY; yy < maxY; yy++) {
254 							auto *fb3 = fb2;
255 							auto *db3 = db2;
256 
257 							for (int xx = w; xx > 0; xx--) {
258 								if (zval < *db3) {
259 									*db3 = zval;
260 									*fb3 = color;
261 								}
262 								fb3++;
263 								db3++;
264 							}
265 
266 							fb2 += fw;
267 							db2 += fw;
268 						}
269 					}
270 					v2 += tAxis2;
271 				}
272 				v1 += tAxis1;
273 			}
274 		}
275 
Render(spades::draw::SWModel * model,const client::ModelRenderParam & param)276 		void SWModelRenderer::Render(spades::draw::SWModel *model,
277 		                             const client::ModelRenderParam &param) {
278 #if ENABLE_SSE2
279 			if (static_cast<int>(level) >= static_cast<int>(SWFeatureLevel::SSE2)) {
280 				RenderInner<SWFeatureLevel::SSE2>(model, param);
281 			} else
282 #endif
283 				RenderInner<SWFeatureLevel::None>(model, param);
284 		}
285 	}
286 }
287