/* Copyright (c) 2013 yvt This file is part of OpenSpades. OpenSpades is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenSpades is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenSpades. If not, see . */ #include "SWModelRenderer.h" #include "SWModel.h" #include "SWRenderer.h" #include "SWUtils.h" #include #include #include namespace spades { namespace draw { SWModelRenderer::SWModelRenderer(SWRenderer *r, SWFeatureLevel level) : r(r), level(level) {} SWModelRenderer::~SWModelRenderer() {} struct ZVals { float zv[512]; ZVals() { for (int i = 0; i < 512; i++) zv[i] = static_cast(i); } inline float operator[](size_t idx) const { return zv[idx]; } }; static ZVals zvals; template void SWModelRenderer::RenderInner(spades::draw::SWModel *model, const client::ModelRenderParam ¶m) { auto &mat = param.matrix; auto origin = mat.GetOrigin(); auto axis1 = mat.GetAxis(0); auto axis2 = mat.GetAxis(1); auto axis3 = mat.GetAxis(2); auto *rawModel = model->GetRawModel(); auto rawModelOrigin = rawModel->GetOrigin(); rawModelOrigin += 0.25f; origin += axis1 * rawModelOrigin.x; origin += axis2 * rawModelOrigin.y; origin += axis3 * rawModelOrigin.z; int w = rawModel->GetWidth(); int h = rawModel->GetHeight(); // int d = rawModel->GetDepth(); // evaluate brightness for each normals uint8_t brights[3 * 3 * 3]; { auto lightVec = MakeVector3(0.f, -0.707f, -0.707f); float dot1 = Vector3::Dot(axis1, lightVec) * fastRSqrt(axis1.GetPoweredLength()); float dot2 = Vector3::Dot(axis2, lightVec) * fastRSqrt(axis2.GetPoweredLength()); float dot3 = Vector3::Dot(axis3, lightVec) * fastRSqrt(axis3.GetPoweredLength()); for (int x = 0; x < 3; x++) { float d = 0.0; int cnt = 0; switch (x) { case 0: d = -dot1; cnt = 1; break; case 1: d = 0.f; cnt = 0; break; case 2: d = dot1; cnt = 1; break; } for (int y = 0; y < 3; y++) { auto d2 = d; auto cnt2 = cnt; switch (y) { case 0: d2 -= dot2; cnt2++; break; case 1: break; case 2: d2 += dot2; cnt2++; break; } for (int z = 0; z < 3; z++) { auto d3 = d; auto cnt3 = cnt2; switch (y) { case 0: d3 -= dot3; cnt3++; break; case 1: break; case 2: d3 += dot3; cnt3++; break; } switch (cnt3) { case 2: d3 *= 0.707f; break; case 3: d3 *= 0.57735f; break; } d3 = 192.f + d3 * 62.f; brights[x + y * 3 + z * 9] = static_cast(d3); } } } } // compute center coord. for culling { auto center = origin; auto localCenter = model->GetCenter(); center += axis1 * localCenter.x; center += axis2 * localCenter.y; center += axis3 * localCenter.z; float largestAxis = axis1.GetPoweredLength(); largestAxis = std::max(largestAxis, axis2.GetPoweredLength()); largestAxis = std::max(largestAxis, axis3.GetPoweredLength()); if (!r->SphereFrustrumCull(center, model->GetRadius() * sqrtf(largestAxis))) return; } Bitmap *fbmp = r->fb; auto *fb = fbmp->GetPixels(); int fw = fbmp->GetWidth(); int fh = fbmp->GetHeight(); auto *db = r->depthBuffer.data(); Matrix4 viewproj = r->GetProjectionViewMatrix(); Vector4 ndc2scrscale = {fw * 0.5f, -fh * 0.5f, 1.f, 1.f}; // Vector4 ndc2scroff = {fw * 0.5f, fh * 0.5f, 0.f, 0.f}; int ndc2scroffX = fw >> 1; int ndc2scroffY = fh >> 1; // render each points auto tOrigin = viewproj * MakeVector4(origin.x, origin.y, origin.z, 1.f); auto tAxis1 = viewproj * MakeVector4(axis1.x, axis1.y, axis1.z, 0.f); auto tAxis2 = viewproj * MakeVector4(axis2.x, axis2.y, axis2.z, 0.f); auto tAxis3 = viewproj * MakeVector4(axis3.x, axis3.y, axis3.z, 0.f); tOrigin *= ndc2scrscale; tAxis1 *= ndc2scrscale; tAxis2 *= ndc2scrscale; tAxis3 *= ndc2scrscale; float pointDiameter; // = largestAxis * 0.55f * fh * 0.5f; { float largestAxis = tAxis1.GetPoweredLength(); largestAxis = std::max(largestAxis, tAxis2.GetPoweredLength()); largestAxis = std::max(largestAxis, tAxis3.GetPoweredLength()); pointDiameter = sqrtf(largestAxis); } uint32_t customColor; customColor = ToFixed8(param.customColor.z) | (ToFixed8(param.customColor.y) << 8) | (ToFixed8(param.customColor.x) << 16); auto v1 = tOrigin; float zNear = r->sceneDef.zNear; for (int x = 0; x < w; x++) { auto v2 = v1; for (int y = 0; y < h; y++) { auto *mp = &model->renderData[model->renderDataAddr[x + y * w]]; while (*mp != -1) { uint32_t data = *(mp++); uint32_t normal = *(mp++); int z = static_cast(data >> 24); // SPAssert(z < d); SPAssert(z >= 0); auto vv = v2 + tAxis3 * zvals[z]; if (vv.z < zNear) continue; // save Z value (don't divide this by W!) float zval = vv.z; // use vv.z for point radius to be divided by W vv.z = pointDiameter; // perspective division float scl = fastRcp(vv.w); vv *= scl; int ix = static_cast(vv.x) + ndc2scroffX; int iy = static_cast(vv.y) + ndc2scroffY; int idm = static_cast(vv.z + .99f); idm = std::max(1, idm); int minX = ix - (idm >> 1); int minY = iy - (idm >> 1); if (minX >= fw || minY >= fh) continue; int maxX = ix + idm; int maxY = iy + idm; if (maxX <= 0 || maxY <= 0) continue; minX = std::max(minX, 0); minY = std::max(minY, 0); maxX = std::min(maxX, fw); maxY = std::min(maxY, fh); auto *fb2 = fb + (minX + minY * fw); auto *db2 = db + (minX + minY * fw); int w = maxX - minX; uint32_t color = data & 0xffffff; if (color == 0) color = customColor; SPAssert(normal < 27); int bright = brights[normal]; #if ENABLE_SSE2 if (lvl == SWFeatureLevel::SSE2) { auto m = _mm_setr_epi32(color, 0, 0, 0); auto f = _mm_set1_epi16(bright << 8); m = _mm_unpacklo_epi8(m, _mm_setzero_si128()); m = _mm_mulhi_epu16(m, f); m = _mm_packus_epi16(m, m); _mm_store_ss(reinterpret_cast(&color), _mm_castsi128_ps(m)); } else #endif { uint32_t c1 = color & 0xff00; uint32_t c2 = color & 0xff00ff; c1 *= bright; c2 *= bright; color = ((c1 & 0xff0000) | (c2 & 0xff00ff00)) >> 8; } for (int yy = minY; yy < maxY; yy++) { auto *fb3 = fb2; auto *db3 = db2; for (int xx = w; xx > 0; xx--) { if (zval < *db3) { *db3 = zval; *fb3 = color; } fb3++; db3++; } fb2 += fw; db2 += fw; } } v2 += tAxis2; } v1 += tAxis1; } } void SWModelRenderer::Render(spades::draw::SWModel *model, const client::ModelRenderParam ¶m) { #if ENABLE_SSE2 if (static_cast(level) >= static_cast(SWFeatureLevel::SSE2)) { RenderInner(model, param); } else #endif RenderInner(model, param); } } }