What I have is some code that I have got from trying to reverse engineer model loading code, because all the instructions that I have seen show how to transform the vertex positions in the shader and adapt the code to that. At first, I didn't know how I would store the information that needs to be loaded in because there are a few ways to do it. I finally just store bone IDs with their weights and pushed a vector for each mesh. The reason why I am writing code like this is that I want vertex positions from my model while I am animating it. This is something that I need for, some physics that I want to apply. I am not sure I got everything right because I am looking at OpenGL and using DirectX and some of the functions, I need I do not know what they are called. Here is my code.
//SkinMesh.h
#pragma once
#include "Vertex.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "ConstantBuffer.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include "Texture.h"
//#include <vector>
struct BoneMatrix
{
aiMatrix4x4 offset_matrix;
aiMatrix4x4 final_world_transform;
//glm::mat4 offset_matrix;
//glm::mat4 final_world_transform;
};
struct pasvars {
std::vector<Vertex> vertices;
std::vector<DWORD> indices;
};
class SkinMesh
{
public:
SkinMesh(ID3D11Device* device, ID3D11DeviceContext* deviceContext, std::vector<Vertex>& vertices, std::vector<DWORD>& indices, std::vector<Texture>& textures, const DirectX::XMMATRIX& transformMatrix);
SkinMesh(const SkinMesh& mesh);
void Draw();
const DirectX::XMMATRIX& GetTransformMatrix();
pasvars getVars();
DirectX::XMFLOAT4 vertPos(UINT vert);
void setVert(DirectX::XMFLOAT4 pos, UINT vert);
private:
std::vector<Vertex> vertices;
VertexBuffer<Vertex> vertexbuffer;
std::vector<DWORD> inducies;
IndexBuffer indexbuffer;
ID3D11DeviceContext* deviceContext;
std::vector<Texture> textures;
DirectX::XMMATRIX transformMatrix;
};
//SkinMesh.cpp
#include "SkinMesh.h"
SkinMesh::SkinMesh(ID3D11Device* device, ID3D11DeviceContext* deviceContext, std::vector<Vertex>& vertices, std::vector<DWORD>& indices, std::vector<Texture>& textures, const DirectX::XMMATRIX& transformMatrix)
{
this->deviceContext = deviceContext;
this->textures = textures;
this->transformMatrix = transformMatrix;
this->vertices = vertices;
this->inducies = indices;
//HRESULT hr = this->vertexbuffer.Initialize(device, vertices.data(), vertices.size());
//COM_ERROR_IF_FAILED(hr, "Failed to initialize vertex buffer for SkinMesh.");
//
//hr = this->indexbuffer.Initialize(device, indices.data(), indices.size());
//COM_ERROR_IF_FAILED(hr, "Failed to initialize index buffer for SkinMesh.");
}
SkinMesh::SkinMesh(const SkinMesh& SkinMesh)
{
this->deviceContext = SkinMesh.deviceContext;
this->inducies = SkinMesh.inducies;
this->vertices = SkinMesh.vertices;
this->textures = SkinMesh.textures;
this->transformMatrix = SkinMesh.transformMatrix;
}
void SkinMesh::Draw()
{
UINT offset = 0;
for (int i = 0; i < textures.size(); i++)
{
if (textures[i].GetType() == aiTextureType::aiTextureType_DIFFUSE)
{
this->deviceContext->PSSetShaderResources(0, 1, textures[i].GetTextureResourceViewAddress());
break;
}
}
this->deviceContext->IASetVertexBuffers(0, 1, this->vertexbuffer.GetAddressOf(), this->vertexbuffer.StridePtr(), &offset);
this->deviceContext->IASetIndexBuffer(this->indexbuffer.Get(), DXGI_FORMAT::DXGI_FORMAT_R32_UINT, 0);
this->deviceContext->DrawIndexed(this->indexbuffer.IndexCount(), 0, 0);
}
const DirectX::XMMATRIX& SkinMesh::GetTransformMatrix()
{
return this->transformMatrix;
}
pasvars SkinMesh::getVars() {
pasvars ret;
ret.indices = this->inducies;
ret.vertices = this->vertices;
return ret;
}
DirectX::XMFLOAT4 SkinMesh::vertPos(UINT vert) {
return DirectX::XMFLOAT4(vertices.at(vert).pos.x, vertices.at(vert).pos.y, vertices.at(vert).pos.z, 1.0f);
}
void SkinMesh::setVert(DirectX::XMFLOAT4 pos, UINT vert) {
vertices.at(vert).pos.x = pos.x;
vertices.at(vert).pos.y = pos.y;
vertices.at(vert).pos.z = pos.z;
}
//SkinModel.h
#pragma once
#include "VertexBoneData.h"
#include <DirectXMath.h>
#include "SkinMesh.h"
#include <string>
#include <map>
using namespace std;
using namespace DirectX;
struct BoneAndWeight {
vector<UINT> boneIdx;
vector<float> weights;
};
struct BoneToMesh {
UINT meshNum;
vector<UINT> vertIdx;
vector<float> weights;
XMMATRIX boneMatrix;
};
struct BoneData {
vector<BoneAndWeight> access;
string name;
};
struct BoneMat {
XMMATRIX final;
XMMATRIX offset;
};
class SkinModel
{
public:
bool Initialize(const std::string& filePath, ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader);
void Draw(const XMMATRIX& worldMatrix, const XMMATRIX& viewProjectionMatrix);
void Transform();
private:
void getzero();
XMMATRIX zeroed;
void readNodeHierarchy(const aiNode* p_node, const XMMATRIX parent_transform);
XMMATRIX global_inverse;
map<string, UINT> m_bone_mapping;
std::vector<SkinMesh> meshes;
std::vector<SkinMesh> transformed;
bool LoadModel(const std::string& filePath);
void ProcessNode(aiNode* node, const aiScene* scene, const XMMATRIX& parentTransformMatrix);
SkinMesh ProcessMesh(aiMesh* mesh, const aiScene* scene, const XMMATRIX& transformMatrix);
TextureStorageType DetermineTextureStorageType(const aiScene* pScene, aiMaterial* pMat, unsigned int index, aiTextureType textureType);
std::vector<Texture> LoadMaterialTextures(aiMaterial* pMaterial, aiTextureType textureType, const aiScene* pScene);
int GetTextureIndex(aiString* pStr);
vector<UINT> meshinfo;
ID3D11Device* device = nullptr;
ID3D11DeviceContext* deviceContext = nullptr;
ConstantBuffer<CB_VS_vertexshader>* cb_vs_vertexshader = nullptr;
std::string directory = "";
UINT m_num_bones = 0;
int meshIndex = 0;
const aiNode* rootNode;
vector<BoneData> theBones;
vector<VertexBoneData> bones_id_weights;
vector<XMFLOAT3> boneRotations;
vector<BoneMat> m_bone_matrices;
//vector<MeshInfo> m_mesh_infos;
};
//SkinModel.cpp
#include "SkinModel.h"
bool SkinModel::Initialize(const std::string& filePath, ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader)
{
getzero();
this->device = device;
this->deviceContext = deviceContext;
this->cb_vs_vertexshader = &cb_vs_vertexshader;
try
{
if (!this->LoadModel(filePath))
return false;
}
catch (COMException& exception)
{
ErrorLogger::Log(exception);
return false;
}
return true;
}
void SkinModel::Draw(const XMMATRIX& worldMatrix, const XMMATRIX& viewProjectionMatrix)
{
this->deviceContext->VSSetConstantBuffers(0, 1, this->cb_vs_vertexshader->GetAddressOf());
for (int i = 0; i < transformed.size(); i++)
{
//Update Constant buffer with WVP Matrix
this->cb_vs_vertexshader->data.mat = meshes[i].GetTransformMatrix() * worldMatrix * viewProjectionMatrix; //Calculate World-View-Projection Matrix
this->cb_vs_vertexshader->data.mat = XMMatrixTranspose(this->cb_vs_vertexshader->data.mat);
this->cb_vs_vertexshader->ApplyChanges();
transformed[i].Draw();
}
}
bool SkinModel::LoadModel(const std::string& filePath)
{
this->directory = StringHelper::GetDirectoryFromPath(filePath);
Assimp::Importer importer;
const aiScene* pScene = importer.ReadFile(filePath,
aiProcess_Triangulate |
aiProcess_ConvertToLeftHanded);
if (pScene == nullptr)
return false;
rootNode = pScene->mRootNode;
global_inverse = XMMatrixTranspose(XMMATRIX(&pScene->mRootNode->mTransformation.a1));
//global_inverse = XMMatrixInverse(); // I dont know how to get an inverse in DirectX.
this->ProcessNode(pScene->mRootNode, pScene, DirectX::XMMatrixIdentity());
transformed.resize(meshes.size());
return true;
}
void SkinModel::ProcessNode(aiNode* node, const aiScene* scene, const XMMATRIX& parentTransformMatrix)
{
vector<VertexBoneData> bones_id_weights;
XMMATRIX nodeTransformMatrix = XMMatrixTranspose(XMMATRIX(&node->mTransformation.a1)) * parentTransformMatrix;
for (UINT i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(this->ProcessMesh(mesh, scene, nodeTransformMatrix));
}
for (UINT i = 0; i < node->mNumChildren; i++)
{
this->ProcessNode(node->mChildren[i], scene, nodeTransformMatrix);
}
}
SkinMesh SkinModel::ProcessMesh(aiMesh* mesh, const aiScene* scene, const XMMATRIX& transformMatrix)
{
vector<BoneAndWeight> bwstor;
bwstor.resize(mesh->mNumVertices);
// Data to fill
std::vector<Vertex> vertices;
std::vector<DWORD> indices;
//Get vertices
for (UINT i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
vertex.pos.x = mesh->mVertices[i].x;
vertex.pos.y = mesh->mVertices[i].y;
vertex.pos.z = mesh->mVertices[i].z;
if (mesh->mTextureCoords[0])
{
vertex.texCoord.x = (float)mesh->mTextureCoords[0][i].x;
vertex.texCoord.y = (float)mesh->mTextureCoords[0][i].y;
}
vertices.push_back(vertex);
}
//Get indices
for (UINT i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
for (UINT j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
bones_id_weights.resize(mesh->mNumVertices);
for (UINT i = 0; i < mesh->mNumBones; i++)
{
UINT bone_index = 0;
string bone_name(mesh->mBones[i]->mName.data);
if (m_bone_mapping.find(bone_name) == m_bone_mapping.end())
{
// Allocate an index for a new bone
bone_index = m_num_bones;
m_num_bones++;
XMMATRIX bi;
BoneMat pushMat;
pushMat.offset = bi;
m_bone_matrices.push_back(pushMat);
m_bone_matrices[bone_index].offset = XMMatrixTranspose(XMMATRIX(&mesh->mBones[i]->mOffsetMatrix.a1));
m_bone_mapping[bone_name] = bone_index;
BoneData abone;
theBones.push_back(abone);
}
else
{
bone_index = m_bone_mapping[bone_name];
}
BoneToMesh stor;
stor.meshNum = meshIndex;
for (UINT j = 0; j < mesh->mBones[i]->mNumWeights; j++)
{
UINT vertex_id = mesh->mBones[i]->mWeights[j].mVertexId;
float weight = mesh->mBones[i]->mWeights[j].mWeight;
bwstor.at(vertex_id).boneIdx.push_back(bone_index);
bwstor.at(vertex_id).weights.push_back(weight);
}
}
BoneData dat;
dat.access = bwstor;
theBones.push_back(dat);
std::vector<Texture> textures;
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
std::vector<Texture> diffuseTextures = LoadMaterialTextures(material, aiTextureType::aiTextureType_DIFFUSE, scene);
textures.insert(textures.end(), diffuseTextures.begin(), diffuseTextures.end());
meshIndex++;
return SkinMesh(this->device, this->deviceContext, vertices, indices, textures, transformMatrix);
}
TextureStorageType SkinModel::DetermineTextureStorageType(const aiScene* pScene, aiMaterial* pMat, unsigned int index, aiTextureType textureType)
{
if (pMat->GetTextureCount(textureType) == 0)
return TextureStorageType::None;
aiString path;
pMat->GetTexture(textureType, index, &path);
std::string texturePath = path.C_Str();
//Check if texture is an embedded indexed texture by seeing if the file path is an index #
if (texturePath[0] == '*')
{
if (pScene->mTextures[0]->mHeight == 0)
{
return TextureStorageType::EmbeddedIndexCompressed;
}
else
{
assert("SUPPORT DOES NOT EXIST YET FOR INDEXED NON COMPRESSED TEXTURES!" && 0);
return TextureStorageType::EmbeddedIndexNonCompressed;
}
}
//Check if texture is an embedded texture but not indexed (path will be the texture's name instead of #)
if (auto pTex = pScene->GetEmbeddedTexture(texturePath.c_str()))
{
if (pTex->mHeight == 0)
{
return TextureStorageType::EmbeddedCompressed;
}
else
{
assert("SUPPORT DOES NOT EXIST YET FOR EMBEDDED NON COMPRESSED TEXTURES!" && 0);
return TextureStorageType::EmbeddedNonCompressed;
}
}
//Lastly check if texture is a filepath by checking for period before extension name
if (texturePath.find('.') != std::string::npos)
{
return TextureStorageType::Disk;
}
return TextureStorageType::None; // No texture exists
}
std::vector<Texture> SkinModel::LoadMaterialTextures(aiMaterial* pMaterial, aiTextureType textureType, const aiScene* pScene)
{
std::vector<Texture> materialTextures;
TextureStorageType storetype = TextureStorageType::Invalid;
unsigned int textureCount = pMaterial->GetTextureCount(textureType);
if (textureCount == 0) //If there are no textures
{
storetype = TextureStorageType::None;
aiColor3D aiColor(0.0f, 0.0f, 0.0f);
switch (textureType)
{
case aiTextureType_DIFFUSE:
pMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, aiColor);
if (aiColor.IsBlack()) //If color = black, just use grey
{
materialTextures.push_back(Texture(this->device, Colors::UnloadedTextureColor, textureType));
return materialTextures;
}
materialTextures.push_back(Texture(this->device, Color(aiColor.r * 255, aiColor.g * 255, aiColor.b * 255), textureType));
return materialTextures;
}
}
else
{
for (UINT i = 0; i < textureCount; i++)
{
aiString path;
pMaterial->GetTexture(textureType, i, &path);
TextureStorageType storetype = DetermineTextureStorageType(pScene, pMaterial, i, textureType);
switch (storetype)
{
case TextureStorageType::EmbeddedIndexCompressed:
{
int index = GetTextureIndex(&path);
Texture embeddedIndexedTexture(this->device,
reinterpret_cast<uint8_t*>(pScene->mTextures[index]->pcData),
pScene->mTextures[index]->mWidth,
textureType);
materialTextures.push_back(embeddedIndexedTexture);
break;
}
case TextureStorageType::EmbeddedCompressed:
{
const aiTexture* pTexture = pScene->GetEmbeddedTexture(path.C_Str());
Texture embeddedTexture(this->device,
reinterpret_cast<uint8_t*>(pTexture->pcData),
pTexture->mWidth,
textureType);
materialTextures.push_back(embeddedTexture);
break;
}
case TextureStorageType::Disk:
{
std::string filename = this->directory + '\\' + path.C_Str();
Texture diskTexture(this->device, filename, textureType);
materialTextures.push_back(diskTexture);
break;
}
}
}
}
if (materialTextures.size() == 0)
{
materialTextures.push_back(Texture(this->device, Colors::UnhandledTextureColor, aiTextureType::aiTextureType_DIFFUSE));
}
return materialTextures;
}
int SkinModel::GetTextureIndex(aiString* pStr)
{
assert(pStr->length >= 2);
return atoi(&pStr->C_Str()[1]);
}
void SkinModel::Transform() {
readNodeHierarchy(rootNode, XMMatrixIdentity());
for (UINT i = 0; i < meshes.size(); i++) {
for (UINT j = 0; j < theBones.at(i).access.size();j++) {
XMMATRIX trans = zeroed;
if (theBones.at(i).access.at(j).boneIdx.size() > 0) {
for (UINT k = 0; k < theBones.at(i).access.at(j).boneIdx.size(); k++) {
UINT curidx = theBones.at(i).access.at(j).boneIdx.at(k);
trans += m_bone_matrices.at(curidx).final * theBones.at(i).access.at(j).weights.at(k);
}
}else{
trans = XMMatrixIdentity();
}
XMFLOAT4 tomul = meshes.at(i).vertPos(j);
XMVECTOR amul = XMLoadFloat4(&tomul);
XMVector4Transform(amul, trans);
XMFLOAT4 tr;
//How do I get a vector position from this
transformed.at(i).setVert(tr, j);
}
}
}
void SkinModel::readNodeHierarchy(const aiNode* p_node, const XMMATRIX parent_transform) {
string node_name(p_node->mName.data);
if (m_bone_mapping.find(node_name) != m_bone_mapping.end()) // true if node_name exist in bone_mapping
{
UINT bonenum = m_bone_mapping[node_name];
XMMATRIX node_transform = XMMatrixRotationRollPitchYaw(boneRotations.at(bonenum).x, boneRotations.at(bonenum).y, boneRotations.at(bonenum).z);
const XMMATRIX global_transform = parent_transform * node_transform;
UINT bone_index = m_bone_mapping[node_name];
m_bone_matrices[bone_index].final = global_inverse * global_transform * m_bone_matrices[bone_index].offset;
for (UINT i = 0; i < p_node->mNumChildren; i++)
{
readNodeHierarchy(p_node->mChildren[i], global_transform);
}
}
}
void SkinModel::getzero() {
zeroed = XMMatrixSet(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
Related
One thing is that the number of vertices in the mesh does not align with the number of vertices the bones are mapped to. The other is that I tried to hold on to the mRootnode in the header. When I call functions in the model class the node returned is not the same. I don't know how much of my code is right. I am trying to deform the mesh in the model class and not the shader.
Here is my code:
//Model.h
#pragma once
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include "Mesh.h"
#include <map>
#include <unordered_map>
#include <string>
using namespace std;
using namespace DirectX;
struct Bone {
int id = 0; // position of the bone in final upload array
std::string name = "";
XMMATRIX offset = XMMatrixIdentity();
std::vector<Bone> children = {};
};
class Model
{
public:
bool Initialize(const std::string & filePath, ID3D11Device * device, ID3D11DeviceContext * deviceContext, ConstantBuffer<CB_VS_vertexshader> & cb_vs_vertexshader, bool skel);
void Draw(const XMMATRIX & worldMatrix, const XMMATRIX & viewProjectionMatrix);
void Draw(const XMMATRIX& worldMatrix, const XMMATRIX& viewProjectionMatrix, float locx, float locz);
void boneTransform();
bool makeGrass(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader);
bool makeMount(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader);
bool makeWindow(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader);
std::vector<Vertex> getVert(int num);
std::vector<DWORD> getIndu(int num);
float boneRots[500][4];
int numbones = 0;
string bonenames[500];
Bone skeleton;
private:
std::vector<Mesh> meshes;
XMMATRIX convertMat(aiMatrix4x4 m);
bool LoadModel(const std::string & filePath, bool hasSkel);
void ProcessNode(aiNode * node, const aiScene * scene, const XMMATRIX & parentTransformMatrix);
Mesh ProcessMesh(aiNode* node, aiMesh * mesh, const aiScene * scene, const XMMATRIX & transformMatrix);
void readNodeHierarchy(const aiNode* p_node, const aiMatrix4x4 parent_transform);
void updateMeshes();
TextureStorageType DetermineTextureStorageType(const aiScene* pScene, aiMaterial* pMat, unsigned int index, aiTextureType textureType);
std::vector<Texture> LoadMaterialTextures(aiMaterial* pMaterial, aiTextureType textureType, const aiScene* pScene);
int GetTextureIndex(aiString* pStr);
bool first;
ID3D11Device * device = nullptr;
ID3D11DeviceContext * deviceContext = nullptr;
ConstantBuffer<CB_VS_vertexshader> * cb_vs_vertexshader = nullptr;
std::string directory = "";
bool hasSkeleton;
aiMatrix4x4 m_global_inverse_transform;
std::vector<Mesh> transformed;
std::map<std::string, unsigned int> m_bone_mapping;
std::vector<BoneMatrix> m_bone_matrices;
std::vector<XMMATRIX> glmTransforms;
unsigned int m_num_bones = 0;
std::vector<MeshInfo> m_mesh_infos;
int mesh_index = 0;
std::vector<MeshBones> forMesh;
const aiScene* rootscene;
const aiNode * rootnode;
std::vector<unsigned int> texnum;
//std::unordered_map<std::string, std::pair<int, XMMATRIX>> boneInfo = {};
};
//Model.cpp
#include "Model.h"
#include <random>
unsigned int seed(165981);
mt19937 gen(seed);
uniform_real_distribution<double> dist(0, 1);
bool Model::Initialize(const std::string & filePath, ID3D11Device * device, ID3D11DeviceContext * deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader, bool skel)
{
this->device = device;
this->deviceContext = deviceContext;
this->cb_vs_vertexshader = &cb_vs_vertexshader;
try
{
if (!this->LoadModel(filePath, skel))
return false;
}
catch (COMException & exception)
{
ErrorLogger::Log(exception);
return false;
}
return true;
}
void Model::Draw(const XMMATRIX & worldMatrix, const XMMATRIX & viewProjectionMatrix)
{
this->deviceContext->VSSetConstantBuffers(0, 1, this->cb_vs_vertexshader->GetAddressOf());
if (hasSkeleton) {
this->cb_vs_vertexshader->data.mat = viewProjectionMatrix; //Calculate World-View-Projection Matrix
this->cb_vs_vertexshader->data.mat = XMMatrixTranspose(this->cb_vs_vertexshader->data.mat);
this->cb_vs_vertexshader->ApplyChanges();
for (int i = 0; i < transformed.size(); i++) transformed[i].Draw();
}else{
for (int i = 0; i < meshes.size(); i++)
{
//Update Constant buffer with WVP Matrix
this->cb_vs_vertexshader->data.mat = meshes[i].GetTransformMatrix() * worldMatrix * viewProjectionMatrix; //Calculate World-View-Projection Matrix
this->cb_vs_vertexshader->data.mat = XMMatrixTranspose(this->cb_vs_vertexshader->data.mat);
this->cb_vs_vertexshader->ApplyChanges();
meshes[i].Draw();
}
}
}
void Model::Draw(const XMMATRIX& worldMatrix, const XMMATRIX& viewProjectionMatrix, float locx, float locz) {
if (hasSkeleton) {
int non = 0;
}
this->deviceContext->VSSetConstantBuffers(0, 1, this->cb_vs_vertexshader->GetAddressOf());
char ord[] = {0, 0, 0};
float val[] = { 0, 0, 0 };
char last = 0;
for (char i = 0; i < 3; i++) {
float relx = meshes.at(i).locx - locx;
float relz = meshes.at(i).locz - locz;
float dist = sqrt(relx * relx + relz * relz);
for (char j = 0; j < 3; j++) {
if (dist > val[j]) {
last = j;
break;
}
}
if (last < 2) {
for (char j = 1; j >= last; j--) {
ord[j + 1] = ord[j];
val[j + 1] = val[j];
}
}
ord[last] = i;
val[last] = dist;
}
for (int i = 0; i < 3; i++)
{
int cho = (int)ord[i];
//Update Constant buffer with WVP Matrix
this->cb_vs_vertexshader->data.mat = meshes[cho].GetTransformMatrix() * worldMatrix * viewProjectionMatrix; //Calculate World-View-Projection Matrix
this->cb_vs_vertexshader->data.mat = XMMatrixTranspose(this->cb_vs_vertexshader->data.mat);
this->cb_vs_vertexshader->ApplyChanges();
meshes[cho].Draw();
}
}
std::vector<Vertex> Model::getVert(int num) {
std::vector<Vertex> ret;
if (num < meshes.size()) ret = meshes.at(num).getVerts();
return ret;
}
std::vector<DWORD> Model::getIndu(int num) {
std::vector<DWORD> ret;
if (num < meshes.size()) ret = meshes.at(num).getIndus();
return ret;
}
bool Model::LoadModel(const std::string & filePath, bool hasSkel)
{
first = true;
hasSkeleton = hasSkel;
this->directory = StringHelper::GetDirectoryFromPath(filePath);
Assimp::Importer importer;
const aiScene* pScene = importer.ReadFile(filePath,
aiProcess_Triangulate |
aiProcess_ConvertToLeftHanded | aiProcess_OptimizeMeshes);
if (pScene == nullptr)
return false;
rootscene = pScene;
rootnode = pScene->mRootNode;
m_global_inverse_transform = pScene->mRootNode->mTransformation;
m_global_inverse_transform.Inverse();
this->ProcessNode(pScene->mRootNode, pScene, DirectX::XMMatrixIdentity());
return true;
}
void Model::ProcessNode(aiNode * node, const aiScene * scene, const XMMATRIX & parentTransformMatrix)
{
XMMATRIX nodeTransformMatrix = XMMatrixTranspose(XMMATRIX(&node->mTransformation.a1)) * parentTransformMatrix;
for (UINT i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
Mesh mdup = this->ProcessMesh(node, mesh, scene, nodeTransformMatrix);
meshes.push_back(mdup);
if (hasSkeleton) transformed.push_back(mdup);
}
//int num = node->mNumChildren;
for (UINT i = 0; i < node->mNumChildren; i++)
{
this->ProcessNode(node->mChildren[i], scene, nodeTransformMatrix);
}
}
Mesh Model::ProcessMesh(aiNode* node, aiMesh* mesh, const aiScene* scene, const XMMATRIX& transformMatrix)
{
// Data to fill
std::vector<Vertex> vertices;
std::vector<DWORD> indices;
std::vector<VertexBoneData> bones_id_weights;
VertexBoneData emp;
//Get vertices
for (UINT i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
vertex.pos.x = mesh->mVertices[i].x;
vertex.pos.y = mesh->mVertices[i].y;
vertex.pos.z = mesh->mVertices[i].z;
if (mesh->mTextureCoords[0])
{
vertex.texCoord.x = (float)mesh->mTextureCoords[0][i].x;
vertex.texCoord.y = (float)mesh->mTextureCoords[0][i].y;
}
vertices.push_back(vertex);
bones_id_weights.push_back(emp);
}
//Get indices
for (UINT i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
for (UINT j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
std::vector<Texture> textures;
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
std::vector<Texture> diffuseTextures = LoadMaterialTextures(material, aiTextureType::aiTextureType_DIFFUSE, scene);
textures.insert(textures.end(), diffuseTextures.begin(), diffuseTextures.end());
if (hasSkeleton) {
//std::unordered_map<std::string, std::pair<int, XMMATRIX>> boneInfo = {};
std::vector<unsigned int> boneCounts;
boneCounts.resize(vertices.size(), 0);
int nBoneCount = mesh->mNumBones;
for (unsigned int i = 0; i < mesh->mNumBones; i++)
{
unsigned int bone_index = 0;
std::string bone_name(mesh->mBones[i]->mName.data);
if (m_bone_mapping.find(bone_name) == m_bone_mapping.end())
{
bonenames[numbones] = bone_name;
bone_index = m_num_bones;
m_num_bones++;
BoneMatrix bi;
m_bone_matrices.push_back(bi);
m_bone_matrices[bone_index].offset_matrix = mesh->mBones[i]->mOffsetMatrix;
m_bone_mapping[bone_name] = bone_index;
for (int j = 0; j < 4; j++) {
boneRots[numbones][j] = 0;
}
XMMATRIX pusher1;
glmTransforms.push_back(pusher1);
numbones++;
}
else
{
bone_index = m_bone_mapping[bone_name];
}
for (unsigned int j = 0; j < mesh->mBones[i]->mNumWeights; j++)
{
unsigned int vertex_id = mesh->mBones[i]->mWeights[j].mVertexId;
float weight = mesh->mBones[i]->mWeights[j].mWeight;
if (vertex_id >= bones_id_weights.size()) {
int non = 0;
}
bones_id_weights[vertex_id].ids.push_back(bone_index);
bones_id_weights[vertex_id].weights.push_back(weight);
}
}
MeshBones tmp;
tmp.boneInfl = bones_id_weights;
tmp.mesh = mesh_index;
forMesh.push_back(tmp);
mesh_index++;
}
return Mesh(this->device, this->deviceContext, vertices, indices, textures, transformMatrix);
}
TextureStorageType Model::DetermineTextureStorageType(const aiScene * pScene, aiMaterial * pMat, unsigned int index, aiTextureType textureType)
{
if (pMat->GetTextureCount(textureType) == 0)
return TextureStorageType::None;
aiString path;
pMat->GetTexture(textureType, index, &path);
std::string texturePath = path.C_Str();
//Check if texture is an embedded indexed texture by seeing if the file path is an index #
if (texturePath[0] == '*')
{
if (pScene->mTextures[0]->mHeight == 0)
{
return TextureStorageType::EmbeddedIndexCompressed;
}
else
{
assert("SUPPORT DOES NOT EXIST YET FOR INDEXED NON COMPRESSED TEXTURES!" && 0);
return TextureStorageType::EmbeddedIndexNonCompressed;
}
}
//Check if texture is an embedded texture but not indexed (path will be the texture's name instead of #)
if (auto pTex = pScene->GetEmbeddedTexture(texturePath.c_str()))
{
if (pTex->mHeight == 0)
{
return TextureStorageType::EmbeddedCompressed;
}
else
{
assert("SUPPORT DOES NOT EXIST YET FOR EMBEDDED NON COMPRESSED TEXTURES!" && 0);
return TextureStorageType::EmbeddedNonCompressed;
}
}
//Lastly check if texture is a filepath by checking for period before extension name
if (texturePath.find('.') != std::string::npos)
{
return TextureStorageType::Disk;
}
return TextureStorageType::None; // No texture exists
}
std::vector<Texture> Model::LoadMaterialTextures(aiMaterial * pMaterial, aiTextureType textureType, const aiScene * pScene)
{
std::vector<Texture> materialTextures;
TextureStorageType storetype = TextureStorageType::Invalid;
unsigned int textureCount = pMaterial->GetTextureCount(textureType);
if (textureCount == 0) //If there are no textures
{
storetype = TextureStorageType::None;
aiColor3D aiColor(0.0f, 0.0f, 0.0f);
switch (textureType)
{
case aiTextureType_DIFFUSE:
pMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, aiColor);
if (aiColor.IsBlack()) //If color = black, just use grey
{
materialTextures.push_back(Texture(this->device, Colors::UnloadedTextureColor, textureType));
return materialTextures;
}
materialTextures.push_back(Texture(this->device, Color(aiColor.r * 255, aiColor.g * 255, aiColor.b * 255), textureType));
return materialTextures;
}
}
else
{
for (UINT i = 0; i < textureCount; i++)
{
aiString path;
pMaterial->GetTexture(textureType, i, &path);
TextureStorageType storetype = DetermineTextureStorageType(pScene, pMaterial, i, textureType);
switch (storetype)
{
case TextureStorageType::EmbeddedIndexCompressed:
{
int index = GetTextureIndex(&path);
Texture embeddedIndexedTexture( this->device,
reinterpret_cast<uint8_t*>(pScene->mTextures[index]->pcData),
pScene->mTextures[index]->mWidth,
textureType);
materialTextures.push_back(embeddedIndexedTexture);
break;
}
case TextureStorageType::EmbeddedCompressed:
{
const aiTexture * pTexture = pScene->GetEmbeddedTexture(path.C_Str());
Texture embeddedTexture(this->device,
reinterpret_cast<uint8_t*>(pTexture->pcData),
pTexture->mWidth,
textureType);
materialTextures.push_back(embeddedTexture);
break;
}
case TextureStorageType::Disk:
{
std::string filename = this->directory + '\\' + path.C_Str();
Texture diskTexture(this->device, filename, textureType);
materialTextures.push_back(diskTexture);
break;
}
}
}
}
if (materialTextures.size() == 0)
{
materialTextures.push_back(Texture(this->device, Colors::UnhandledTextureColor, aiTextureType::aiTextureType_DIFFUSE));
}
return materialTextures;
}
int Model::GetTextureIndex(aiString * pStr)
{
assert(pStr->length >= 2);
return atoi(&pStr->C_Str()[1]);
}
void Model::readNodeHierarchy(const aiNode* p_node, const aiMatrix4x4 parent_transform)
{
std::string aname = p_node->mName.data;
aiMatrix4x4 global_transform;
std::string node_name(p_node->mName.data);
aiMatrix4x4 node_transform = p_node->mTransformation;
int sizes = 0;
for (int i = 0; i < meshes.size(); i++) sizes += meshes.at(i).getSize();
if (m_bone_mapping.find(node_name) != m_bone_mapping.end())
{
int bi = m_bone_mapping[node_name];
//aiVector3D scaling_vector = node_anim->mScalingKeys[2].mValue;
aiVector3D scaling_vector = aiVector3D(1, 1, 1);
aiMatrix4x4 scaling_matr;
aiMatrix4x4::Scaling(scaling_vector, scaling_matr);
//rotation
//aiQuaternion rotate_quat = node_anim->mRotationKeys[2].mValue;
aiQuaternion rotate_quat = aiQuaternion(boneRots[bi][0], boneRots[bi][1], boneRots[bi][2], boneRots[bi][3]);;
aiMatrix4x4 rotate_matr = aiMatrix4x4(rotate_quat.GetMatrix());
//translation
//aiVector3D translate_vector = node_anim->mPositionKeys[2].mValue;
aiVector3D translate_vector = aiVector3D(0, 0, 0);
aiMatrix4x4 translate_matr;
aiMatrix4x4::Translation(translate_vector, translate_matr);
node_transform = translate_matr * rotate_matr * scaling_matr;
aiMatrix4x4 global_transform = parent_transform * node_transform;
unsigned int bone_ind1 = m_bone_mapping[node_name];
m_bone_matrices[bone_ind1].final_world_transform = m_global_inverse_transform * global_transform * m_bone_matrices[bone_ind1].offset_matrix;
glmTransforms[bone_ind1] = XMMATRIX(&m_bone_matrices[bone_ind1].final_world_transform.a1);
//XMMATRIX(&pScene->mRootNode->mTransformation.a1)
}
int num = p_node->mNumChildren;
if (num > -1) {
for (unsigned int i = 0; i < p_node->mNumChildren; i++)
{
readNodeHierarchy(p_node->mChildren[i], global_transform);
}
}
}
void Model::boneTransform()
{
std::vector<aiMatrix4x4> transforms;
aiMatrix4x4 identity_matrix;
readNodeHierarchy(rootnode, identity_matrix);
glmTransforms.resize(m_num_bones);
for (unsigned int i = 0; i < m_num_bones; i++)
{
glmTransforms[i] = convertMat(m_bone_matrices[i].final_world_transform);
}
updateMeshes();
}
void Model::updateMeshes() {
for (int i = 0; i < forMesh.size(); i++) {
int curm = forMesh.at(i).mesh;
if (forMesh.at(i).boneInfl.size() != meshes.at(1).getSize()) {
int var1 = forMesh.at(i).boneInfl.size();
int var2 = meshes.at(1).getSize();
int non = 0;
}
for (int j = 0; j < forMesh.at(i).boneInfl.size();j++) {
XMFLOAT3 opos = meshes.at(curm).getVertex(j);
XMMATRIX bend = XMMatrixSet(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (forMesh.at(curm).boneInfl.at(j).ids.size() > 0) {
for (int k = 0; k < forMesh.at(curm).boneInfl.at(j).ids.size(); k++) {
int matnum = forMesh.at(curm).boneInfl.at(j).ids.at(k);
float matwei = forMesh.at(curm).boneInfl.at(j).weights.at(k);
bend += glmTransforms.at(matnum) * matwei;
}
XMVECTOR invec = XMLoadFloat3(&opos);
XMVECTOR vec = XMVector3Transform(invec, bend);
XMFLOAT3 otpos;
XMStoreFloat3(&otpos, vec);
transformed.at(curm).setVertex(otpos, j);
}else{
}
}
}
}
bool Model::makeGrass(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader) {
if (!Initialize("Data\\Objects\\MyStuff\\grassblade.obj", device, deviceContext, cb_vs_vertexshader, false)) {
return false;
}
std::vector<Vertex> grassVerts;
std::vector<DWORD> grassIndu;
grassVerts = getVert(0);
grassIndu = getIndu(0);
std::vector<Vertex> outVerts;
std::vector<DWORD> outIndu;
int blanum = 0;
for (float x = -120; x < 120; x += .6) {
for (float z = -120; z < 120; z += .6) {
if (!(x > -25 && x < 25 && z > -25 && z < 25)) {
XMFLOAT3 grasPos(x, 0, z);
float col = dist(gen);
XMFLOAT3 grasScale(.5, .5, .5);
float myAngle = dist(gen) * 360;
float revAngle = dist(gen) * 90 - 45;
XMMATRIX WVP;
XMMATRIX translation, rotationX, rotationY, rotationZ, scale;
translation = XMMatrixTranslation(grasPos.x, 0, grasPos.z);
rotationX = XMMatrixRotationX(revAngle * 0.01745329251994329576923690768489);
rotationY = XMMatrixRotationY(myAngle * 0.01745329251994329576923690768489);
scale = XMMatrixScaling(50, 50, 50);
WVP = rotationX * rotationY * translation * scale;
int vertsiz = grassVerts.size();
for (int i = 0; i < vertsiz; i++) {
XMFLOAT3 inpos = grassVerts.at(i).pos;
XMVECTOR invec = XMLoadFloat3(&inpos);
XMVECTOR vec = XMVector3Transform(invec, WVP);
XMFLOAT3 otpos;
Vertex tmp;
XMStoreFloat3(&otpos, vec);
tmp.pos = otpos;
tmp.texCoord.x = col;
tmp.texCoord.y = 0;
outVerts.push_back(tmp);
}
int indsiz = grassIndu.size();
for (int i = 0; i < indsiz; i++) {
int strt = blanum * indsiz;
DWORD theind = strt + grassIndu.at(i);
outIndu.push_back(theind);
}
blanum++;
}
}
}
hasSkeleton = true;
vector<Texture> materialTextures;
materialTextures.push_back(Texture(this->device, Colors::UnhandledTextureColor, aiTextureType::aiTextureType_DIFFUSE));
transformed.push_back(Mesh(this->device, this->deviceContext, outVerts, outIndu, materialTextures, XMMatrixIdentity()));
return true;
}
bool Model::makeMount(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader) {
if (!Initialize("Data\\Objects\\MyStuff\\mountains1.fbx", device, deviceContext, cb_vs_vertexshader, false)) {
return false;
}
meshes.at(0).clearTextures();
meshes.at(0).ismore = true;
meshes.at(0).loadTexture(device, "Data/Textures2/variation.png");
meshes.at(0).loadTexture(device, "Data/Textures2/cliff-rockface1_albedo.png");
meshes.at(0).loadTexture(device, "Data/Textures/seamless_grass.jpg");
return true;
}
bool Model::makeWindow(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader) {
if (!Initialize("Data\\Objects\\MyStuff\\window.fbx", device, deviceContext, cb_vs_vertexshader, false)) {
return false;
}
for (int i = 0; i < 3; i++) {
meshes.at(i).clearTextures();
meshes.at(i).ismore = true;
meshes.at(i).loadTexture(device, "Data/Textures2/liquid_pattern.jpg");
meshes.at(i).loadTexture(device, "Data/Textures2/liquid_alpha.png");
}
return true;
}
XMMATRIX Model::convertMat(aiMatrix4x4 m) {
return XMMATRIX
(m.a1, m.a2, m.a3, m.a4,
m.b1, m.b2, m.b3, m.b4,
m.c1, m.c2, m.c3, m.c4,
m.d1, m.d2, m.d3, m.d4);
}
//Mesh.h
#pragma once
#include "Vertex.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "ConstantBuffer.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include "Texture.h"
struct BoneMatrix
{
aiMatrix4x4 offset_matrix;
aiMatrix4x4 final_world_transform;
};
struct VertexBoneData
{
std::vector<unsigned int> ids;
std::vector<float> weights;
};
struct MeshBones {
unsigned int mesh;
std::vector<VertexBoneData> boneInfl;
};
struct MeshInfo {
MeshInfo()
{ // accumulate all vertices from many scene mesh in one vector
base_index = 0; // offset in our one mesh after adding vertices from next scene->mesh[i]
}
unsigned int base_index; // base_index == base_vertex
};
class Mesh
{
public:
Mesh(ID3D11Device * device, ID3D11DeviceContext * deviceContext, std::vector<Vertex> & vertices, std::vector<DWORD> & indices, std::vector<Texture> & textures, const DirectX::XMMATRIX & transformMatrix);
Mesh(const Mesh & mesh);
void Draw();
void loadTexture(ID3D11Device* device, std::string path);
void clearTextures();
const DirectX::XMMATRIX & GetTransformMatrix();
std::vector<Vertex> getVerts();
std::vector<DWORD> getIndus();
DirectX::XMFLOAT3 getVertex(unsigned int num);
void setVertex(DirectX::XMFLOAT3 vert, unsigned int num);
int getSize();
void reinit(ID3D11Device* device);
bool getpos = false;
bool ismore = false;
float locx = 0;
float locz = 0;
bool xdif = false;
private:
std::vector<Vertex> meshVerts;
std::vector<DWORD> meshIndu;
VertexBuffer<Vertex> vertexbuffer;
IndexBuffer indexbuffer;
ID3D11DeviceContext * deviceContext;
std::vector<Texture> textures;
DirectX::XMMATRIX transformMatrix;
};
//Mesh.cpp
#include "Mesh.h"
Mesh::Mesh(ID3D11Device * device, ID3D11DeviceContext * deviceContext, std::vector<Vertex>& vertices, std::vector<DWORD>& indices, std::vector<Texture> & textures, const DirectX::XMMATRIX & transformMatrix)
{
meshVerts = vertices;
meshIndu = indices;
this->deviceContext = deviceContext;
this->textures = textures;
this->transformMatrix = transformMatrix;
HRESULT hr = this->vertexbuffer.Initialize(device, vertices.data(), vertices.size());
COM_ERROR_IF_FAILED(hr, "Failed to initialize vertex buffer for mesh.");
hr = this->indexbuffer.Initialize(device, indices.data(), indices.size());
COM_ERROR_IF_FAILED(hr, "Failed to initialize index buffer for mesh.");
int vcnt = vertices.size();
float kep = vertices.at(1).pos.x;
for (int i = 0; i < vcnt; i++) {
locx += vertices.at(i).pos.x;
locz += vertices.at(i).pos.z;
if (vertices.at(i).pos.x != kep) xdif = true;
}
locx /= (float)vcnt;
locz /= (float)vcnt;
}
Mesh::Mesh(const Mesh & mesh)
{
this->deviceContext = mesh.deviceContext;
this->indexbuffer = mesh.indexbuffer;
this->vertexbuffer = mesh.vertexbuffer;
this->textures = mesh.textures;
this->transformMatrix = mesh.transformMatrix;
this->meshVerts = mesh.meshVerts;
this->meshIndu = mesh.meshIndu;
this->getpos = mesh.getpos;
this->locx = mesh.locx;
this->locz = mesh.locz;
this->xdif = mesh.xdif;
}
void Mesh::Draw()
{
UINT offset = 0;
if (ismore) {
UINT tcnt = 0;
for (int i = 0; i < textures.size(); i++)
{
if (textures[i].GetType() == aiTextureType::aiTextureType_DIFFUSE)
{
this->deviceContext->PSSetShaderResources(tcnt, 1, textures[i].GetTextureResourceViewAddress());
tcnt++;
}
}
}else {
for (int i = 0; i < textures.size(); i++)
{
if (textures[i].GetType() == aiTextureType::aiTextureType_DIFFUSE)
{
this->deviceContext->PSSetShaderResources(0, 1, textures[i].GetTextureResourceViewAddress());
break;
}
}
}
this->deviceContext->IASetVertexBuffers(0, 1, this->vertexbuffer.GetAddressOf(), this->vertexbuffer.StridePtr(), &offset);
this->deviceContext->IASetIndexBuffer(this->indexbuffer.Get(), DXGI_FORMAT::DXGI_FORMAT_R32_UINT, 0);
this->deviceContext->DrawIndexed(this->indexbuffer.IndexCount(), 0, 0);
}
const DirectX::XMMATRIX & Mesh::GetTransformMatrix()
{
return this->transformMatrix;
}
std::vector<Vertex> Mesh::getVerts() {
return meshVerts;
}
std::vector<DWORD> Mesh::getIndus() {
return meshIndu;
}
DirectX::XMFLOAT3 Mesh::getVertex(unsigned int num) {
return meshVerts.at(num).pos;
}
void Mesh::setVertex(DirectX::XMFLOAT3 vert, unsigned int num) {
meshVerts.at(num).pos = vert;
}
void Mesh::reinit(ID3D11Device* device) {
vertexbuffer.Initialize(device, &meshVerts[0], meshVerts.size());
}
void Mesh::loadTexture(ID3D11Device* device, std::string path) {
textures.push_back(Texture(device, path, aiTextureType_DIFFUSE));
}
void Mesh::clearTextures() {
textures.clear();
}
int Mesh::getSize() {
return meshVerts.size();
}
I am trying to make my renderer able to use either default or custom shader.
class Renderer
{
public:
void Create()
{
//Setup vao, vbo, ebo, quad indices, add data to ebo,
//create default Shader
//set camera projection
}
void Map()
{
vertexData = (VertexData*)glMapBufferRange(...);
}
void Draw(Rectangle destRect, Rectangle texCoordRect, Texture* texture)
{
//4 times for 4 different corners
float textureSlot = FindTexture(texture);
AddDataToMappedBuffer(position, texCoords, textureSlot, color);
indexCount += 6;
}
void Unmap()
{
glUnmapBuffer(...);
}
void Render()
{
Unmap();
shader.UniformMatrix4fv("projection", camera.getProjection());
for(uint i = 0; i < texture.size(); ++i)
{
texture[i]->Bind(i);
}
BindVAO();
DrawElements(TRIANGELS, indexCount, UNSIGNED_INT, nullptr);
UnbindVAO();
For(uint i = 0; i < texture.size(); ++i)
{
texture[i]->Unbind(i);
}
indexCount = 0;
texture.clear();
}
float Renderer2D::FindTexture(Texture* t)
{
float result = 0.0f;
bool found = false;
for (uint i = 0; i < texture.size(); ++i) {
if (texture[i] == t) {
result = static_cast<float>(i + 1);
found = true;
break;
}
}
if (!found) {
if (texture.size() >= MAX_TEXTURES) {
Render();
Map();
}
texture.push_back(t);
result = static_cast<float>(texture.size());
}
return result;
}
private:
std::vector<Texture*> texture;
uint vao, vbo, ebo;
VertexData* vertexData;
float FindTexture(Texture*);
Shader shader;
Camera2D camera;
};
int main()
{
Renderer renderer;
renderer.Create();
while(!quit)
{
//Clear color etc.
renderer.Map();
renderer.Draw(Rectangle(0.0f, 0.0f, 500.0f, 500.0f), Rectangle(0.0f, 0.0f, 1.0f, 1.0f), texture("sometexture.png"));
renderer.Render();
}
}
that is how it works right now.
I would like my renderer to be like this:
in Draw method set shader
check if this shader is in an array
if it is, do not do another draw call - use shader which is already in an array
if it is not, add this shader to an array, use it and do another draw call
I tried to do method which find shader - similar to texture - and go through all shaders and use them. It do not do exactly what I want.
std::vector<Shader*> shader;
void Renderer2D::FindShader(Shader* sh)
{
bool found = false;
for (uint i = 0; i < shader.size(); ++i) {
if (shader[i] == sh) {
found = true;
return;
}
}
if (!found) {
shader.push_back(sh);
Render();
Map();
}
}
void Render()
{
Unmap();
for(const auto& shader : shader)
{
shader->UseProgram();
shader.UniformMatrix4fv("projection", camera.getProjection());
}
for(uint i = 0; i < texture.size(); ++i)
{
texture[i]->Bind(i);
}
BindVAO();
DrawElements(TRIANGELS, indexCount, UNSIGNED_INT, nullptr);
UnbindVAO();
For(uint i = 0; i < texture.size(); ++i)
{
texture[i]->Unbind(i);
}
indexCount = 0;
texture.clear();
}
void Draw(Rectangle destRect, Rectangle texCoordRect, Texture* texture, Shader* shader)
{
FindShader(shader);
float textureSlot = FindTexture(texture);
AddDataToMappedBuffer(position, texCoords, textureSlot, color);
indexCount += 6;
}
I have also tried to find shader without doing another draw call when shader is not found - only add it to an array - and in render method loop through all shaders and do draw call. It also did not work well.
How should I do it?
I'm trying to load a mesh into my directx app. I can successfully load any mesh but texture coordinates are wrong on a side of mesh. (Only a side. The other side's texture is mapped correctly)
ScreenShot
Here's my code.
Model.h
#ifndef MODEL_H
#define MODEL_H
#include <vector>
#include <d3d11_1.h>
#include <DirectXMath.h>
#include <D3DX10.h>
#include <Importer.hpp>
#include <scene.h>
#include <postprocess.h>
#include "Mesh.h"
using namespace DirectX;
class CModel
{
public:
CModel();
~CModel();
bool Load(HWND hwnd, ID3D11Device* dev, ID3D11DeviceContext* devcon, std::string filename);
void Draw(ID3D11DeviceContext* devcon);
void Close();
private:
ID3D11Device *dev;
ID3D11DeviceContext *devcon;
std::vector<Mesh> meshes;
string directory;
vector<Texture> textures_loaded;
HWND hwnd;
void processNode(aiNode* node, const aiScene* scene);
Mesh processMesh(aiMesh* mesh, const aiScene* scene);
vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName, const aiScene* scene);
string determineTextureType(const aiScene* scene, aiMaterial* mat);
int getTextureIndex(aiString* str);
ID3D11ShaderResourceView* getTextureFromModel(const aiScene* scene, int textureindex);
};
#endif
Model.cpp
#include "Model.h"
CModel::CModel()
{
}
CModel::~CModel()
{
}
bool CModel::Load(HWND hwnd, ID3D11Device* dev, ID3D11DeviceContext* devcon,
std::string filename)
{
Assimp::Importer importer;
const aiScene* pScene = importer.ReadFile(filename,
aiProcess_Triangulate |
aiProcess_ConvertToLeftHanded |
aiProcess_FlipUVs);
if (pScene == NULL)
return false;
this->directory = filename.substr(0, filename.find_last_of('/'));
this->dev = dev;
this->hwnd = hwnd;
processNode(pScene->mRootNode, pScene);
return true;
}
void CModel::processNode(aiNode* node, const aiScene* scene)
{
for (UINT i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(this->processMesh(mesh, scene));
}
for (UINT i = 0; i < node->mNumChildren; i++)
{
this->processNode(node->mChildren[i], scene);
}
}
string textype;
Mesh CModel::processMesh(aiMesh* mesh, const aiScene* scene)
{
// Data to fill
vector<VERTEX> vertices;
vector<DWORD> indices;
vector<Texture> textures;
if (mesh->mMaterialIndex >= 0)
{
aiMaterial* mat = scene->mMaterials[mesh->mMaterialIndex];
if(textype.empty()) textype = determineTextureType(scene, mat);
}
// Walk through each of the mesh's vertices
for (UINT i = 0; i < mesh->mNumVertices; i++)
{
VERTEX vertex;
vertex.X = mesh->mVertices[i].x;
vertex.Y = mesh->mVertices[i].y;
vertex.Z = mesh->mVertices[i].z;
if (mesh->mTextureCoords[0])
{
vertex.TEXX = mesh->mTextureCoords[0][i].x;
vertex.TEXY = mesh->mTextureCoords[0][i].y;
}
else
{
vertex.TEXX = 0.0f;
vertex.TEXY = 0.0f;
}
vertices.push_back(vertex);
}
for (UINT i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
for (UINT j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
if (mesh->mMaterialIndex >= 0)
{
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
vector<Texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse", scene);
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
//vector<Texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
//textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
}
return Mesh(dev, vertices, indices, textures);
}
vector<Texture> CModel::loadMaterialTextures(aiMaterial* mat, aiTextureType
type, string typeName, const aiScene* scene)
{
vector<Texture> textures;
for (UINT i = 0; i < mat->GetTextureCount(type); i++)
{
aiString str;
mat->GetTexture(type, i, &str);
// Check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
bool skip = false;
for (UINT j = 0; j < textures_loaded.size(); j++)
{
if (std::strcmp(textures_loaded[j].path.C_Str(), str.C_Str()) == 0)
{
textures.push_back(textures_loaded[j]);
skip = true; // A texture with the same filepath has already been loaded, continue to next one. (optimization)
break;
}
}
if (!skip)
{ // If texture hasn't been loaded already, load it
HRESULT hr;
Texture texture;
if (textype == "embedded compressed texture")
{
int textureindex = getTextureIndex(&str);
texture.texture = getTextureFromModel(scene, textureindex);
}
else
{
string filename = string(str.C_Str());
filename = directory + '/' + filename;
hr = D3DX11CreateShaderResourceViewFromFile(dev, filename.c_str(), nullptr, nullptr, &texture.texture, nullptr);
if (FAILED(hr))
MessageBox(hwnd, "Texture couldn't be loaded", "Error!", MB_ICONERROR | MB_OK);
}
texture.type = typeName;
texture.path = str;
textures.push_back(texture);
this->textures_loaded.push_back(texture); // Store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.
}
}
return textures;
}
void CModel::Draw(ID3D11DeviceContext* devcon)
{
for (int i = 0; i < meshes.size(); i++)
{
meshes[i].Draw(devcon);
}
}
void CModel::Close()
{
for (int i = 0; i < meshes.size(); i++)
{
meshes[i].Close();
}
dev->Release();
}
string CModel::determineTextureType(const aiScene* scene, aiMaterial* mat)
{
aiString textypeStr;
mat->GetTexture(aiTextureType_DIFFUSE, 0, &textypeStr);
string textypeteststr = textypeStr.C_Str();
if (textypeteststr == "*0" || textypeteststr == "*1" || textypeteststr == "*2" || textypeteststr == "*3" || textypeteststr == "*4" || textypeteststr == "*5")
{
if (scene->mTextures[0]->mHeight == 0)
{
return "embedded compressed texture";
}
else
{
return "embedded non-compressed texture";
}
}
if (textypeteststr.find('.') != string::npos)
{
return "textures are on disk";
}
}
int CModel::getTextureIndex(aiString* str)
{
string tistr;
tistr = str->C_Str();
tistr = tistr.substr(1);
return stoi(tistr);
}
ID3D11ShaderResourceView * CModel::getTextureFromModel(const aiScene * scene, int textureindex)
{
HRESULT hr;
ID3D11ShaderResourceView *texture;
int* size = reinterpret_cast<int*>(&scene->mTextures[textureindex]->mWidth);
hr = D3DX11CreateShaderResourceViewFromMemory(dev, reinterpret_cast<unsigned char*>(scene->mTextures[textureindex]->pcData), *size, nullptr, nullptr, &texture, nullptr);
if (FAILED(hr))
MessageBox(hwnd, "Texture couldn't be created from memory!", "Error!", MB_ICONERROR | MB_OK);
return texture;
}
Mesh.h (This is a header only class)
#ifndef MESH_H
#define MESH_H
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
using namespace std;
#include <vector>
#include <d3d11_1.h>
#include <DirectXMath.h>
#include <D3DX11.h>
#include <D3DX10.h>
using namespace DirectX;
struct VERTEX {
FLOAT X, Y, Z;
D3DXCOLOR color;
FLOAT TEXX, TEXY;
};
struct Texture {
string type;
aiString path;
ID3D11ShaderResourceView *texture;
};
class Mesh {
public:
vector<VERTEX> vertices;
vector<DWORD> indices;
vector<Texture> textures;
ID3D11Device *dev;
Mesh(ID3D11Device *dev,vector<VERTEX> vertices, vector<DWORD> indices, vector<Texture> textures)
{
this->vertices = vertices;
this->indices = indices;
this->textures = textures;
this->dev = dev;
this->setupMesh(dev);
}
void Draw(ID3D11DeviceContext *devcon)
{
UINT stride = sizeof(VERTEX);
UINT offset = 0;
devcon->IASetVertexBuffers(0, 1, &VertexBuffer, &stride, &offset);
devcon->IASetIndexBuffer(IndexBuffer, DXGI_FORMAT_R32_UINT, 0);
devcon->PSSetShaderResources(0, 1, &textures[0].texture);
devcon->DrawIndexed(indices.size(), 0, 0);
}
void Close()
{
VertexBuffer->Release();
IndexBuffer->Release();
}
private:
/* Render data */
ID3D11Buffer *VertexBuffer, *IndexBuffer;
/* Functions */
// Initializes all the buffer objects/arrays
bool setupMesh(ID3D11Device *dev)
{
HRESULT hr;
D3D11_BUFFER_DESC vbd;
vbd.Usage = D3D11_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(VERTEX) * vertices.size();
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA initData;
initData.pSysMem = &vertices[0];
hr = dev->CreateBuffer(&vbd, &initData, &VertexBuffer);
if (FAILED(hr))
return false;
D3D11_BUFFER_DESC ibd;
ibd.Usage = D3D11_USAGE_IMMUTABLE;
ibd.ByteWidth = sizeof(DWORD) * indices.size();
ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
ibd.CPUAccessFlags = 0;
ibd.MiscFlags = 0;
initData.pSysMem = &indices[0];
hr = dev->CreateBuffer(&ibd, &initData, &IndexBuffer);
if (FAILED(hr))
return false;
}
};
#endif
Thanks.
I solved it. If anyone else facing this issue be sure you have set a SamplerState.
Here is the Triangle.cpp code:
#include "Triangle.h"
Triangle::Triangle()
{
_vertexBuffer = NULL;
_indexBuffer = NULL;
_vertexShader = NULL;
_pixelShader = NULL;
_inputLayout = NULL;
_stride = sizeof(Vertex);
_offset = 0;
}
Triangle::~Triangle()
{
if (_vertexBuffer)
{
_vertexBuffer->Release();
_vertexBuffer = NULL;
}
if (_indexBuffer)
{
_indexBuffer->Release();
_indexBuffer = NULL;
}
if (_vertexShader)
{
_vertexShader->Release();
_vertexShader = NULL;
}
if (_pixelShader)
{
_pixelShader->Release();
_pixelShader = NULL;
}
if (_inputLayout)
{
_inputLayout->Release();
_inputLayout = NULL;
}
}
const bool Triangle::Initialize(DirectX * graphics)
{
_graphics = graphics;
InitTriangleData();
if (!InitializeVertexAndIndexBuffers(graphics->GetDevice()))
return false;
if (!InitializeShadersAndinputLayout(graphics->GetDevice()))
return false;
return true;
}
void Triangle::Render()
{
_graphics->GetDeviceContext()->IASetVertexBuffers(0, 1, &_vertexBuffer, &_stride, &_offset);
_graphics->GetDeviceContext()->IASetIndexBuffer(_indexBuffer, DXGI_FORMAT_D32_FLOAT, 0);
_graphics->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
_graphics->GetDeviceContext()->PSSetShader(_pixelShader, 0, 1);
_graphics->GetDeviceContext()->VSSetShader(_vertexShader, 0, 1);
_graphics->GetDeviceContext()->DrawIndexed(3, 0, 0);
_graphics->GetDeviceContext()->PSSetShader(0, 0, 0);
_graphics->GetDeviceContext()->VSSetShader(0, 0, 0);
}
void Triangle::InitTriangleData()
{
_vertices = new Vertex[3];
_indices = new unsigned long[3];
float halfX = 50;
float halfY = 50;
_vertices[0].position = D3DXVECTOR3(0.0f, halfY, 0.0f);
_vertices[0].uv = D3DXVECTOR2(0.0f, 0.0f);
_vertices[1].position = D3DXVECTOR3(halfX, -halfY, 0.0f);
_vertices[1].uv = D3DXVECTOR2(0.0f, 0.0f);
_vertices[2].position = D3DXVECTOR3(-halfX, -halfY, 0.0f);
_vertices[2].uv = D3DXVECTOR2(0.0f, 0.0f);
}
const bool Triangle::InitializeVertexAndIndexBuffers(ID3D11Device * device)
{
HRESULT result;
D3D11_BUFFER_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof bufferDesc);
D3D11_SUBRESOURCE_DATA bufferData;
ZeroMemory(&bufferData, sizeof bufferData);
bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufferDesc.ByteWidth = _stride * 3;
bufferDesc.StructureByteStride = 0;
bufferDesc.MiscFlags = 0;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
bufferData.pSysMem = _vertices;
result = device->CreateBuffer(&bufferDesc, &bufferData, &_vertexBuffer);
if (FAILED(result))
return false;
ZeroMemory(&bufferData, sizeof bufferData);
ZeroMemory(&bufferDesc, sizeof bufferDesc);
bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
bufferDesc.ByteWidth = sizeof(unsigned long) *3;
bufferDesc.StructureByteStride = 0;
bufferDesc.MiscFlags = 0;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
bufferData.pSysMem =_indices;
result = device->CreateBuffer(&bufferDesc, &bufferData, &_indexBuffer);
if (FAILED(result))
return false;
delete[] _vertices;
_vertices = 0;
delete[] _indices;
_indices = 0;
return true;
}
const bool Triangle::InitializeShadersAndinputLayout(ID3D11Device * device)
{
HRESULT result;
ID3D10Blob *data;
ID3D10Blob* error;
std::string name = ".//Resources//Shaders//BasicTextureShader//color.ps";
result = D3DX11CompileFromFileA(name.c_str(), NULL, NULL, "ColorPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &data, &error, NULL);
if (FAILED(result)) {
if (error)
{
PrintOutMessage(error);
error->Release();
error = NULL;
}
return false;
}
result = device->CreatePixelShader(data->GetBufferPointer(), data->GetBufferSize(), NULL, &_pixelShader);
if (FAILED(result)) {
return false;
}
data->Release();
data = NULL;
name.clear();
name = ".//Resources//Shaders//BasicTextureShader//color.vs";
result = D3DX11CompileFromFileA(name.c_str(), NULL, NULL, "ColorVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &data, &error, NULL);
if (FAILED(result)) {
if (error)
{
PrintOutMessage(error);
error->Release();
error = NULL;
}
return false;
}
result = device->CreateVertexShader(data->GetBufferPointer(), data->GetBufferSize(), NULL, &_vertexShader);
if (FAILED(result))
return false;
D3D11_INPUT_ELEMENT_DESC elements[2];
unsigned int elementsCount = 2;
elements[0].AlignedByteOffset = 0;
elements[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
elements[0].InputSlot = 0;
elements[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
elements[0].InstanceDataStepRate = 0;
elements[0].SemanticIndex = 0;
elements[0].SemanticName = "POSITION";
elements[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
elements[1].Format = DXGI_FORMAT_R32G32_FLOAT;
elements[1].InputSlot = 0;
elements[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
elements[1].InstanceDataStepRate = 0;
elements[1].SemanticIndex = 0;
elements[1].SemanticName = "TEXCOORD";
result = device->CreateInputLayout(elements, elementsCount, data->GetBufferPointer(), data->GetBufferSize(), &_inputLayout);
if (FAILED(result))
return false;
data->Release();
data = NULL;
return true;
}
void Triangle::PrintOutMessage(ID3D10Blob * error)
{
if (!error)
return;
char * message = new char[error->GetBufferSize()];
message = (char*)error->GetBufferPointer();
std::fstream file;
file.open("ShaderErrorReport.txt", std::fstream::in | std::fstream::out | std::fstream::trunc);
if (!file.is_open())
return;
for (size_t i = 0; i < error->GetBufferSize(); i++)
{
file << message[i];
}
file.close();
}
And the Triangle.h code:
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include "Engine\DirectX.h"
#include <D3DX10math.h>
#include <string>
#include <fstream>
class Triangle
{
private:
struct ConstantBuffer {
D3DXMATRIX world;
D3DXMATRIX view;
D3DXMATRIX proj;
};
struct Vertex
{
D3DXVECTOR3 position;
D3DXVECTOR2 uv;
};
public:
Triangle();
~Triangle();
const bool Initialize(DirectX* graphics);
void Render();
private:
void InitTriangleData();
const bool InitializeVertexAndIndexBuffers(ID3D11Device* device);
const bool InitializeShadersAndinputLayout(ID3D11Device* device);
void PrintOutMessage(ID3D10Blob * error);
private:
Vertex* _vertices;
unsigned long * _indices;
unsigned int _vertexCount = 3;
unsigned int indexCount = 3;
unsigned int _stride;
unsigned int _offset;
ID3D11Buffer* _vertexBuffer, *_indexBuffer, *_constantBuffer;
ID3D11InputLayout *_inputLayout;
ID3D11PixelShader* _pixelShader;
ID3D11VertexShader* _vertexShader;
DirectX* _graphics;
};
#endif
I have a Nvidia GTX 760 gpu and when i run this it the Graphics driver crushes and recovers...and my window goes white.[the 'clear' color is black]
The API ID3D11DeviceContext::PSSetShader and ID3D11DeviceContext::VSSetShader second and third arguments are use for dynamic linkage, in your case they should be nullptr and 0. By telling the device 0,1, you claim one class instance but give a nil pointer for it. The device reject your invalid shader binding, and when you attempt to render, the gpu hang because it does not have a proper shader to run.
In your code, replace
_graphics->GetDeviceContext()->PSSetShader(_pixelShader, 0, 1);
_graphics->GetDeviceContext()->VSSetShader(_vertexShader, 0, 1);
by
_graphics->GetDeviceContext()->PSSetShader(_pixelShader, nullptr, 0);
_graphics->GetDeviceContext()->VSSetShader(_vertexShader, nullptr, 0);
I have a very peculiar problem that I can not figure out. For my scene I have created a spline class which I use to generate points for which my camera traverses. This works fine and dandy when I create an instance of the spline class in my scene and use it. I have some functionality in my spline class for showing the points on the spline that use uniform data to draw their position and give them a color. This all works fine when I create an instance directly in my scene.
HOWEVER, I want to create multiple splines to have my camera traverse so I created a fairly simple management class to handle multiple splines. This works however for some reason I can not puzzle out why when I create an instance of a spline with the manager the uniform variables aren't working. Everything else works fine. When I say they don't work what I mean is that the getLocation functions work (they get the locationIDs as expected), the data to be sent is the same, there are no errors or warnings concerning the functions that are supposed to actually send the data down to their locations. The spline draws properly however they are always black and haven't been translated.
Here is my scene class. The constructors for both my manager and the spline class its self are exactly the same so in my ClothScene.hpp I can switch the type of the mSplineManager object from the manager class to the spline class and the uniform variables work fine.
#include "ClothScene.hpp"
#include <atlas/core/GLFW.hpp>
#include <atlas/core/Log.hpp>
#include <atlas/core/Macros.hpp>
#include <atlas/core/Float.hpp>
#include <iostream>
ClothScene::ClothScene() :
mIsPlaying(false),
mLastTime(0.0f),
mFPS(60.0f),
mTick(1.0f / mFPS),
mAnimTime(0.0f),
mAnimLength(10.0f),
mSplineManager(int(mAnimLength * mFPS)),
ballPosition{ -10.0f, 0.0f, 14.0f }
{
glEnable(GL_DEPTH_TEST);
auto mat = glm::translate(atlas::math::Matrix4(1.0f), ballPosition);
mBall.transformGeometry(mat);
}
ClothScene::~ClothScene() {
}
void ClothScene::mousePressEvent(int button, int action, int modifiers, double xPos, double yPos) {
USING_ATLAS_MATH_NS;
if (button == GLFW_MOUSE_BUTTON_LEFT && modifiers == GLFW_MOD_ALT)
{
if (action == GLFW_PRESS)
{
mIsDragging = true;
//Camera tilt up and down or turn left, right
mCamera.mouseDown(Point2(xPos, yPos), ClothCamera::CameraMovements::TUMBLE);
}
else
{
mIsDragging = false;
mCamera.mouseUp();
}
}
else if (button == GLFW_MOUSE_BUTTON_MIDDLE && modifiers == GLFW_MOD_ALT)
{
if (action == GLFW_PRESS)
{
mIsDragging = true;
//Camera move left, right, up, down
mCamera.mouseDown(Point2(xPos, yPos), ClothCamera::CameraMovements::TRACK);
}
else
{
mIsDragging = false;
mCamera.mouseUp();
}
}
else if (button == GLFW_MOUSE_BUTTON_RIGHT && modifiers == GLFW_MOD_ALT)
{
if (action == GLFW_PRESS)
{
// first click.
mIsDragging = true;
//Camera move back and forth
mCamera.mouseDown(Point2(xPos, yPos), ClothCamera::CameraMovements::DOLLY);
}
else
{
mIsDragging = false;
mCamera.mouseUp();
}
}
else if (action != GLFW_PRESS)
{
mIsDragging = false;
mCamera.mouseUp();
}
}
void ClothScene::mouseMoveEvent(double xPos, double yPos) {
mCamera.mouseUpdate(glm::vec2(xPos, yPos));
}
void ClothScene::keyPressEvent(int key, int scancode, int action, int mods) {
UNUSED(scancode);
UNUSED(mods);
if (action == GLFW_PRESS)
{
switch (key)
{
case GLFW_KEY_T:
mCamera.resetCamera();
break;
case GLFW_KEY_W:
mCamera.strafeCamera(0);
break;
case GLFW_KEY_S:
mCamera.strafeCamera(1);
break;
case GLFW_KEY_A:
mCamera.strafeCamera(2);
break;
case GLFW_KEY_D:
mCamera.strafeCamera(3);
break;
case GLFW_KEY_R:
mCamera.strafeCamera(4);
break;
case GLFW_KEY_F:
mCamera.strafeCamera(5);
break;
case GLFW_KEY_Q:
mCamera.strafeCamera(6);
break;
case GLFW_KEY_E:
mCamera.strafeCamera(7);
break;
case GLFW_KEY_C:
mCamera.newPosition(glm::vec3(0.0f, 3.0f, 0.0f));
break;
case GLFW_KEY_U:
mSplineManager.showSpline();
break;
case GLFW_KEY_I:
mSplineManager.showControlPoints();
break;
case GLFW_KEY_O:
mSplineManager.showCage();
break;
case GLFW_KEY_P:
mSplineManager.showSplinePoints();
break;
case GLFW_KEY_SPACE:
mIsPlaying = !mIsPlaying;
default:
break;
}
}
}
void ClothScene::screenResizeEvent(int width, int height) {
glViewport(0, 0, width, height);
mProjection = glm::perspective(glm::radians(45.0),(double)width / height, 1.0, 1000.0);
}
void ClothScene::renderScene() {
float grey = 161.0f / 255.0f;
glClearColor(grey, grey, grey, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
mView = mCamera.getCameraMatrix();
mGrid.renderGeometry(mProjection, mView);
mSplineManager.renderGeometry(mProjection, mView);
mBall.renderGeometry(mProjection, mView);
}
void ClothScene::updateScene(double time)
{
mTime.currentTime = (float)time;
mTime.totalTime += (float)time;
if (atlas::core::geq(mTime.currentTime - mLastTime, mTick))
{
mLastTime += mTick;
mTime.deltaTime = mTick;
if (mIsPlaying)
{
mAnimTime += mTick;
mSplineManager.updateGeometry(mTime);
if (mSplineManager.doneInterpolation())
{
mIsPlaying = false;
return;
}
auto point = mSplineManager.getSplinePosition();
mCamera.newPosition(point);
mCamera.lookAt(ballPosition);
auto mat = glm::translate(atlas::math::Matrix4(1.0f), ballPosition);
mBall.transformGeometry(mat);
}
}
}
Here is my SplineManger class. It super simple, it just creates a vector of splines and then applies the exact same functionality iterated through all of the splines in the vector. For some reason this prevents the shader from receiving the uniform data.
#include "SplineManager.hpp"
SplineManager::SplineManager(int totalFrames) :
finishedAllSplines(false),
currentSpline(0)
{
mTotalFrames = totalFrames;
addSplines();
}
SplineManager::~SplineManager() {
}
void SplineManager::addSplines() {
mControlPoints = std::vector<Point>
{
{ -20, -5, 0 },
{ -19, 5, -15 },
{ 12.7f, -5, -1.4f },
{ 20, 8.2f, 4.4f }
};
mSplines.push_back(Spline(mTotalFrames, mControlPoints));
}
void SplineManager::renderGeometry(atlas::math::Matrix4 projection, atlas::math::Matrix4 view) {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].renderGeometry(projection, view);
}
}
void SplineManager::updateGeometry(atlas::utils::Time const& t) {
mSplines[currentSpline].updateGeometry(t);
if (mSplines[currentSpline].doneInterpolation()) {
++currentSpline;
if (currentSpline == mSplines.size()) {
finishedAllSplines = true;
}
}
}
atlas::math::Point SplineManager::getSplinePosition() {
return mSplines[currentSpline].getSplinePosition();
}
void SplineManager::showSpline() {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].showSpline();
}
}
void SplineManager::showControlPoints() {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].showControlPoints();
}
}
void SplineManager::showCage() {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].showCage();
}
}
void SplineManager::showSplinePoints() {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].showSplinePoints();
}
}
bool SplineManager::doneInterpolation() {
return finishedAllSplines;
}
Here is my spline class. Again, this all works totally fine when I directly create an instance of it in the scene class. When instances are created in the SplineManager the uniform variables don't work.
#include "Spline.h"
#include "ShaderPaths.hpp"
#include <atlas/core/Macros.hpp>
Spline::Spline(int totalFrames) :
mResolution(500),
mTotalFrames(totalFrames),
mCurrentFrame(0),
mShowControlPoints(false),
mShowCage(false),
mShowSplinePoints(false),
mShowSpline(false),
mIsInterpolationDone(false)
{
USING_ATLAS_MATH_NS;
USING_ATLAS_GL_NS;
//Bezier
mBasisMatrix = Matrix4(
1.0f, 0.0f, 0.0f, 0.0f,
-3.0f, 3.0f, 0.0f, 0.0f,
3.0f, -6.0f, 3.0f, 0.0f,
-1.0f, 3.0f, -3.0f, 1.0f);
mControlPoints = std::vector<Point>
{
{ -20, -5, 0 },
{ -19, 5, -15 },
{ 12.7f, -5, -1.4f },
{ 20, 8.2f, 4.4f }
};
std::vector<Point> splinePoints;
float scale = 1.0f / mResolution;
for (int res = 0; res < mResolution + 1; ++res)
{
splinePoints.push_back(evaluateSpline(scale * res));
}
generateArcLengthTable();
glGenVertexArrays(1, &mVao);
glBindVertexArray(mVao);
glGenBuffers(1, &mControlBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mControlBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * mControlPoints.size(),
mControlPoints.data(), GL_STATIC_DRAW);
glGenBuffers(1, &mSplineBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mSplineBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * splinePoints.size(), splinePoints.data(), GL_STATIC_DRAW);
std::string shaderDir = generated::ShaderPaths::getShaderDirectory();
std::vector<ShaderInfo> shaders
{
{ GL_VERTEX_SHADER, shaderDir + "spline.vs.glsl" },
{ GL_FRAGMENT_SHADER, shaderDir + "spline.fs.glsl" }
};
mShaders.push_back(ShaderPointer(new Shader));
mShaders[0]->compileShaders(shaders);
mShaders[0]->linkShaders();
GLuint var;
var = mShaders[0]->getUniformVariable("uMVP");
mUniforms.insert(UniformKey("uMVP", var));
var = mShaders[0]->getUniformVariable("fColour");
mUniforms.insert(UniformKey("fColour", var));
mShaders[0]->disableShaders();
glBindVertexArray(0);
}
Spline::~Spline()
{
glDeleteVertexArrays(1, &mVao);
glDeleteVertexArrays(1, &mControlBuffer);
glDeleteVertexArrays(1, &mSplineBuffer);
}
void Spline::renderGeometry(atlas::math::Matrix4 projection,
atlas::math::Matrix4 view)
{
USING_ATLAS_MATH_NS;
mShaders[0]->enableShaders();
glBindVertexArray(mVao);
Matrix4 mvp = projection * view * mModel;
glUniformMatrix4fv(mUniforms["uMVP"], 1, GL_FALSE, &mvp[0][0]);
// Draw the control points first.
glUniform3f(mUniforms["fColour"], 1, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mControlBuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
if (mShowControlPoints)
{
glPointSize(5.0f);
glDrawArrays(GL_POINTS, 0, GLsizei(mControlPoints.size()));
glPointSize(1.0f);
}
if (mShowCage)
{
glDrawArrays(GL_LINE_STRIP, 0, GLsizei(mControlPoints.size()));
}
// Now draw the spline.
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mSplineBuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glUniform3f(mUniforms["fColour"], 0, 1, 0);
if (mShowSpline)
{
glLineWidth(5.0f);
glDrawArrays(GL_LINE_STRIP, 0, mResolution + 1);
glLineWidth(1.0f);
}
if (mShowSplinePoints)
{
glPointSize(8.0f);
glDrawArrays(GL_POINTS, 1, mResolution);
glPointSize(1.0f);
}
glDisableVertexAttribArray(0);
mShaders[0]->disableShaders();
}
void Spline::updateGeometry(atlas::utils::Time const& t)
{
UNUSED(t);
mSplinePosition = interpolateOnSpline();
mCurrentFrame++;
if (mCurrentFrame == mTotalFrames)
{
mIsInterpolationDone = true;
return;
}
}
void Spline::showControlPoints()
{
mShowControlPoints = !mShowControlPoints;
}
void Spline::showCage()
{
mShowCage = !mShowCage;
}
void Spline::showSplinePoints()
{
mShowSplinePoints = !mShowSplinePoints;
}
void Spline::showSpline()
{
mShowSpline = !mShowSpline;
}
bool Spline::doneInterpolation()
{
return mIsInterpolationDone;
}
atlas::math::Point Spline::getSplinePosition()
{
return mSplinePosition;
}
atlas::math::Point Spline::interpolateOnSpline()
{
int n = int(mTable.size());
float totalDistance = mTable[n - 1];
float step = totalDistance / mTotalFrames;
float currDistance = step * mCurrentFrame;
int index = tableLookUp(currDistance);
float t = (1.0f / mResolution) * (index % mResolution);
return evaluateSpline(t);
}
atlas::math::Point Spline::evaluateSpline(float t)
{
USING_ATLAS_MATH_NS;
Vector4 xControls =
{
mControlPoints[0].x, mControlPoints[1].x,
mControlPoints[2].x, mControlPoints[3].x
};
Vector4 yControls =
{
mControlPoints[0].y, mControlPoints[1].y,
mControlPoints[2].y, mControlPoints[3].y
};
Vector4 zControls =
{
mControlPoints[0].z, mControlPoints[1].z,
mControlPoints[2].z, mControlPoints[3].z
};
Vector4 xCoeff = xControls * mBasisMatrix;
Vector4 yCoeff = yControls * mBasisMatrix;
Vector4 zCoeff = zControls * mBasisMatrix;
float xcr, ycr, zcr;
xcr = xCoeff[0] + t * xCoeff[1] + t * t * xCoeff[2] + t * t * t * xCoeff[3];
ycr = yCoeff[0] + t * yCoeff[1] + t * t * yCoeff[2] + t * t * t * yCoeff[3];
zcr = zCoeff[0] + t * zCoeff[1] + t * t * zCoeff[2] + t * t * t * zCoeff[3];
return Point(xcr, ycr, zcr);
}
void Spline::generateArcLengthTable()
{
USING_ATLAS_MATH_NS;
if (!mTable.empty())
{
mTable.clear();
}
float scale = 1.0f / mResolution;
mTable.push_back(0.0f);
for (int i = 1; i < mResolution + 1; ++i)
{
Point p0 = evaluateSpline((i - 1) * scale);
Point p1 = evaluateSpline(i * scale);
Point dist = p0 - p1;
mTable.push_back(mTable[i - 1] + glm::length(dist));
}
}
int Spline::tableLookUp(float distance)
{
// Find the entry in our table that corresponds to the given distance.
float epsilon = chooseEpsilon();
for (int i = 0; i < int(mTable.size()); ++i)
{
if (glm::abs(mTable[i] - distance) < epsilon)
{
return i;
}
}
return -1;
}
float Spline::chooseEpsilon()
{
// Find the largest difference and use that to look up distances
// in our table.
float epsilon = 0.0f;
float diff;
for (int i = 0; i < mTable.size() - 1; ++i)
{
diff = glm::abs(mTable[i] - mTable[i + 1]);
if (diff > epsilon)
{
epsilon = diff;
}
}
return epsilon;
}
void Spline::setSplineCoordinates(std::vector<atlas::math::Point> mControlPoints_) {
mControlPoints = mControlPoints_;
}
The header files for anyone interested.
SplineManager.hpp
#ifndef SPLINEMANAGER_HPP
#define SPLINEMANAGER_HPP
#pragma once
#include <atlas/utils/Geometry.hpp>
#include "Spline.h"
USING_ATLAS_MATH_NS;
USING_ATLAS_GL_NS;
class SplineManager : public atlas::utils::Geometry {
public:
SplineManager(int totalFrames);
~SplineManager();
atlas::math::Point getSplinePosition();
void addSplines();
void showSpline();
void showControlPoints();
void showCage();
void showSplinePoints();
bool doneInterpolation();
void updateGeometry(atlas::utils::Time const& t) override;
void renderGeometry(atlas::math::Matrix4 projection, atlas::math::Matrix4 view) override;
private:
std::vector<Spline> mSplines;
std::vector<atlas::math::Point> mControlPoints;
int mTotalFrames;
int currentSpline;
bool finishedAllSplines;
};
#endif
Spline.h
#ifndef LAB04_INCLUDE_SPLINE_HPP
#define LAB04_INCLUDE_SPLINE_HPP
#pragma once
#include <atlas/utils/Geometry.hpp>
#include <fstream>
class Spline : public atlas::utils::Geometry
{
public:
Spline(int totalFrames, std::vector<atlas::math::Point> mControlPoints_);
Spline(int totalFrames);
~Spline();
void renderGeometry(atlas::math::Matrix4 projection,
atlas::math::Matrix4 view) override;
void updateGeometry(atlas::utils::Time const& t) override;
void showControlPoints();
void showCage();
void showSplinePoints();
void showSpline();
bool doneInterpolation();
atlas::math::Point getSplinePosition();
void setSplineCoordinates(std::vector<atlas::math::Point> mControlPoints_);
private:
atlas::math::Point interpolateOnSpline();
atlas::math::Point evaluateSpline(float t);
void generateArcLengthTable();
int tableLookUp(float distance);
float chooseEpsilon();
atlas::math::Matrix4 mBasisMatrix;
std::vector<atlas::math::Point> mControlPoints;
std::vector<float> mTable;
atlas::math::Point mSplinePosition;
GLuint mVao;
GLuint mControlBuffer;
GLuint mSplineBuffer;
int mResolution;
int mTotalFrames;
int mCurrentFrame;
bool mShowControlPoints;
bool mShowCage;
bool mShowSplinePoints;
bool mShowSpline;
bool mIsInterpolationDone;
};
#endif
ClothScene.hpp
#ifndef SCENE_HPP
#define SCENE_HPP
#pragma once
#include <atlas\utils\Scene.hpp>
#include "ClothCamera.hpp"
#include "SplineManager.hpp"
#include "Grid.hpp"
#include "Spline.h"
#include "Ball.hpp"
//#include "Cloth.hpp"
class ClothScene : public atlas::utils::Scene {
public:
ClothScene();
~ClothScene();
void mousePressEvent(int button, int action, int modifiers, double xPos, double yPos) override;
void mouseMoveEvent(double xPos, double yPos) override;
void keyPressEvent(int key, int scancode, int action, int mods) override;
void screenResizeEvent(int width, int height) override;
void renderScene() override;
void updateScene(double time) override;
private:
bool mIsDragging;
bool mIsPlaying;
float mLastTime;
float mFPS;
float mTick;
float mAnimTime;
float mAnimLength;
glm::vec3 ballPosition;
ClothCamera mCamera;
Grid mGrid;
SplineManager mSplineManager;//, mSpline2;
Ball mBall;
std::vector<atlas::math::Point> mControlPoints;
};
#endif