Changeset View
Changeset View
Standalone View
Standalone View
source/graphics/ModelDef.cpp
/* Copyright (C) 2015 Wildfire Games. | /* Copyright (C) 2020 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
Show All 11 Lines | |||||
#include "precompiled.h" | #include "precompiled.h" | ||||
#include "ModelDef.h" | #include "ModelDef.h" | ||||
#include "graphics/SkeletonAnimDef.h" | #include "graphics/SkeletonAnimDef.h" | ||||
#include "ps/FileIo.h" | #include "ps/FileIo.h" | ||||
#include "maths/Vector4D.h" | #include "maths/Vector4D.h" | ||||
#if HAVE_SSE | |||||
# include <xmmintrin.h> | |||||
#endif | |||||
CVector3D CModelDef::SkinPoint(const SModelVertex& vtx, | CVector3D CModelDef::SkinPoint(const SModelVertex& vtx, | ||||
const CMatrix3D newPoseMatrices[]) | const CMatrix3D newPoseMatrices[]) | ||||
{ | { | ||||
CVector3D result (0, 0, 0); | CVector3D result (0, 0, 0); | ||||
for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i) | for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i) | ||||
{ | { | ||||
result += newPoseMatrices[vtx.m_Blend.m_Bone[i]].Transform(vtx.m_Coords) * vtx.m_Blend.m_Weight[i]; | result += newPoseMatrices[vtx.m_Blend.m_Bone[i]].Transform(vtx.m_Coords) * vtx.m_Blend.m_Weight[i]; | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | for (size_t j = 0; j < numVertices; ++j) | ||||
// optimise that case a bit.) | // optimise that case a bit.) | ||||
if (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence | if (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence | ||||
norm.Normalize(); | norm.Normalize(); | ||||
memcpy(PositionData + PositionStride*j, &pos.X, 3*sizeof(float)); | memcpy(PositionData + PositionStride*j, &pos.X, 3*sizeof(float)); | ||||
memcpy(NormalData + NormalStride*j, &norm.X, 3*sizeof(float)); | memcpy(NormalData + NormalStride*j, &norm.X, 3*sizeof(float)); | ||||
} | } | ||||
} | } | ||||
#if HAVE_SSE | |||||
void CModelDef::SkinPointsAndNormals_SSE( | |||||
size_t numVertices, | |||||
const VertexArrayIterator<CVector3D>& Position, | |||||
const VertexArrayIterator<CVector3D>& Normal, | |||||
const SModelVertex* vertices, | |||||
const size_t* blendIndices, | |||||
const CMatrix3D newPoseMatrices[]) | |||||
{ | |||||
// To avoid some performance overhead, get the raw vertex array pointers | |||||
char* PositionData = Position.GetData(); | |||||
size_t PositionStride = Position.GetStride(); | |||||
char* NormalData = Normal.GetData(); | |||||
size_t NormalStride = Normal.GetStride(); | |||||
// Must be aligned correctly for SSE | |||||
ASSERT((intptr_t)newPoseMatrices % 16 == 0); | |||||
ASSERT((intptr_t)PositionData % 16 == 0); | |||||
ASSERT((intptr_t)PositionStride % 16 == 0); | |||||
ASSERT((intptr_t)NormalData % 16 == 0); | |||||
ASSERT((intptr_t)NormalStride % 16 == 0); | |||||
__m128 col0, col1, col2, col3, vec0, vec1, vec2; | |||||
for (size_t j = 0; j < numVertices; ++j) | |||||
{ | |||||
const SModelVertex& vtx = vertices[j]; | |||||
const CMatrix3D& mtx = newPoseMatrices[blendIndices[j]]; | |||||
// Loads matrix to xmm registers. | |||||
col0 = _mm_load_ps(mtx._data); | |||||
col1 = _mm_load_ps(mtx._data + 4); | |||||
col2 = _mm_load_ps(mtx._data + 8); | |||||
col3 = _mm_load_ps(mtx._data + 12); | |||||
// Loads and computes vertex coordinates. | |||||
vec0 = _mm_load1_ps(&vtx.m_Coords.X); // v0 = [x, x, x, x] | |||||
vec0 = _mm_mul_ps(col0, vec0); // v0 = [_11*x, _21*x, _31*x, _41*x] | |||||
vec1 = _mm_load1_ps(&vtx.m_Coords.Y); // v1 = [y, y, y, y] | |||||
vec1 = _mm_mul_ps(col1, vec1); // v1 = [_12*y, _22*y, _32*y, _42*y] | |||||
vec0 = _mm_add_ps(vec0, vec1); // v0 = [_11*x + _12*y, ...] | |||||
vec1 = _mm_load1_ps(&vtx.m_Coords.Z); // v1 = [z, z, z, z] | |||||
vec1 = _mm_mul_ps(col2, vec1); // v1 = [_13*z, _23*z, _33*z, _43*z] | |||||
vec1 = _mm_add_ps(vec1, col3); // v1 = [_13*z + _14, ...] | |||||
vec0 = _mm_add_ps(vec0, vec1); // v0 = [_11*x + _12*y + _13*z + _14, ...] | |||||
_mm_store_ps((float*)(PositionData + PositionStride*j), vec0); | |||||
// Loads and computes normal vectors. | |||||
vec0 = _mm_load1_ps(&vtx.m_Norm.X); // v0 = [x, x, x, x] | |||||
vec0 = _mm_mul_ps(col0, vec0); // v0 = [_11*x, _21*x, _31*x, _41*x] | |||||
vec1 = _mm_load1_ps(&vtx.m_Norm.Y); // v1 = [y, y, y, y] | |||||
vec1 = _mm_mul_ps(col1, vec1); // v1 = [_12*y, _22*y, _32*y, _42*y] | |||||
vec0 = _mm_add_ps(vec0, vec1); // v0 = [_11*x + _12*y, ...] | |||||
vec1 = _mm_load1_ps(&vtx.m_Norm.Z); // v1 = [z, z, z, z] | |||||
vec1 = _mm_mul_ps(col2, vec1); // v1 = [_13*z, _23*z, _33*z, _43*z] | |||||
vec0 = _mm_add_ps(vec0, vec1); // v0 = [_11*x + _12*y + _13*z, ...] | |||||
// If there was more than one influence, the result is probably not going | |||||
// to be of unit length (since it's a weighted sum of several independent | |||||
// unit vectors), so we need to normalise it. | |||||
// (It's fairly common to only have one influence, so it seems sensible to | |||||
// optimise that case a bit.) | |||||
if (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence | |||||
{ | |||||
// Normalization. | |||||
// vec1 = [x*x, y*y, z*z, ?*?] | |||||
vec1 = _mm_mul_ps(vec0, vec0); | |||||
// vec2 = [y*y, z*z, x*x, ?*?] | |||||
vec2 = _mm_shuffle_ps(vec1, vec1, _MM_SHUFFLE(3, 0, 2, 1)); | |||||
vec1 = _mm_add_ps(vec1, vec2); | |||||
// vec2 = [z*z, x*x, y*y, ?*?] | |||||
vec2 = _mm_shuffle_ps(vec2, vec2, _MM_SHUFFLE(3, 0, 2, 1)); | |||||
vec1 = _mm_add_ps(vec1, vec2); | |||||
// rsqrt(a) = 1 / sqrt(a) | |||||
vec1 = _mm_rsqrt_ps(vec1); | |||||
vec0 = _mm_mul_ps(vec0, vec1); | |||||
} | |||||
_mm_store_ps((float*)(NormalData + NormalStride*j), vec0); | |||||
} | |||||
} | |||||
#endif | |||||
void CModelDef::BlendBoneMatrices( | void CModelDef::BlendBoneMatrices( | ||||
vladislavbelov: `sse_*_SSE`? I don't think it's good. | |||||
Done Inline ActionsRight will unify the names. Do you prefer prefix or suffix? Stan: Right will unify the names. Do you prefer prefix or suffix? | |||||
Done Inline ActionsSuffix without underscore. vladislavbelov: Suffix without underscore. | |||||
CMatrix3D boneMatrices[]) | CMatrix3D boneMatrices[]) | ||||
{ | { | ||||
for (size_t i = 0; i < m_NumBlends; ++i) | for (size_t i = 0; i < m_NumBlends; ++i) | ||||
{ | { | ||||
const SVertexBlend& blend = m_pBlends[i]; | const SVertexBlend& blend = m_pBlends[i]; | ||||
CMatrix3D& boneMatrix = boneMatrices[m_NumBones + 1 + i]; | CMatrix3D& boneMatrix = boneMatrices[m_NumBones + 1 + i]; | ||||
// Note: there is a special case of joint influence, in which the vertex | // Note: there is a special case of joint influence, in which the vertex | ||||
▲ Show 20 Lines • Show All 250 Lines • ▼ Show 20 Lines | |||||
CModelDefRPrivate* CModelDef::GetRenderData(const void* key) const | CModelDefRPrivate* CModelDef::GetRenderData(const void* key) const | ||||
{ | { | ||||
RenderDataMap::const_iterator it = m_RenderData.find(key); | RenderDataMap::const_iterator it = m_RenderData.find(key); | ||||
if (it != m_RenderData.end()) | if (it != m_RenderData.end()) | ||||
return it->second; | return it->second; | ||||
return 0; | return 0; | ||||
} | } | ||||
Done Inline ActionsWhy do you use extern for the implementation? vladislavbelov: Why do you use `extern` for the implementation? | |||||
Done Inline Actions
Was done like this for Color iirc Stan: > Why do you use `extern` for the implementation?
Was done like this for Color iirc | |||||
Done Inline ActionsFollowing mistakes isn't a good practice. vladislavbelov: Following mistakes isn't a good practice. | |||||
Done Inline ActionsUseless empty line. vladislavbelov: Useless empty line. |
Wildfire Games · Phabricator
sse_*_SSE? I don't think it's good.