I'm trying to load Wavefront OBJ files for a game I'm trying to make. I have it down... sort of. Some of the faces are either missing when rendered, or they have incorrect vertex positions. The way I'm loading them probably isn't very optimized, too. I'm using LWJGL and it's OpenGL binding (I think it's a binding) to render my model.
Here's what the model is supposed to look like:
http://i1291.photobucket.com/albums/b560/DandDMC/blender2014-07-0415-28-40-23_zps0fbfcebb.png
Here's what it looks like ingame:
http://i1291.photobucket.com/albums/b560/DandDMC/javaw2014-07-0415-52-54-99_zpsdf14fb6c.png
Here's the loading method:
public static Model loadModel(String fileLocation)
{
File file = new File(fileLocation);
if(!file.exists())
{
try
{
throw new FileNotFoundException("The file named: " + fileLocation + " doesn't exist!");
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
return null;
}
ArrayList<Vertex> vertices = new ArrayList<Vertex> ();
ArrayList<Vertex> texVertices = new ArrayList<Vertex> ();
ArrayList<Face> faces = new ArrayList<Face> ();
try
{
BufferedReader r = new BufferedReader(new FileReader(file));
String s = "";
int refIndex = 1;
int vertIndex = 1;
int texVertIndex = 1;
while((s = r.readLine()) != null)
{
String[] split = s.split(" ");
if(split[0].equals("v"))
{
vertices.add(new Vertex(Double.parseDouble(split[1]), Double.parseDouble(split[2]), Double.parseDouble(split[3]), vertIndex));
vertIndex ++;
}
else if(split[0].equals("vt"))
{
texVertices.add(new Vertex(Double.parseDouble(split[1]), Double.parseDouble(split[2]), 0.0, texVertIndex));
texVertIndex ++;
}
else if(split[0].equals("f"))
{
ArrayList<Integer> vert = new ArrayList<Integer> ();
ArrayList<Integer> texVert = new ArrayList<Integer> ();
for(int i = 1; i < split.length; i ++)
{
String[] fSplit = split[i].split("/");
vert.add(Integer.parseInt(fSplit[0]));
texVert.add(Integer.parseInt(fSplit[1]));
}
faces.add(new Face(vert, texVert));
}
else if(split[0].equals("#") || split[0].equals("o") || split[0].equals("mtllib") || split[0].equals("usemtl") || split[0].equals("s"))
{
// Don't have a use for as of now
}
else
{
throw new Exception("The syntax at line: " + refIndex + " is incorrect.");
}
refIndex ++;
}
r.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return new Model(vertices, texVertices, faces);
}
Here's the Face class (it's pretty simple):
package com.glh.model;
import java.util.*;
public class Face
{
public ArrayList<Integer> verticeIndexes;
public ArrayList<Integer> texVerticeIndexes;
public Face(ArrayList<Integer> verticeIndexes, ArrayList<Integer> texVerticeIndexes)
{
this.verticeIndexes = verticeIndexes;
this.texVerticeIndexes = texVerticeIndexes;
}
public ArrayList<Vertex> getVertexPositions(Model parentModel)
{
ArrayList<Vertex> l = new ArrayList<Vertex> ();
for(int i : verticeIndexes)
{
l.add(parentModel.vertices.get(i - 1));
}
return l;
}
public ArrayList<Vertex> getTextureVertexPositions(Model parentModel)
{
ArrayList<Vertex> l = new ArrayList<Vertex> ();
for(int i : texVerticeIndexes)
{
l.add(parentModel.texVertices.get(i - 1));
}
return l;
}
}
The Vertex class even simpler:
package com.glh.model;
public class Vertex
{
public double x;
public double y;
public double z;
public int index;
public Vertex(double x, double y, double z, int index)
{
this.x = x;
this.y = y;
this.z = z;
this.index = index;
}
public Vertex()
{
this(0.0, 0.0, 0.0, 0);
}
}
And the method to render it:
public void renderModel_tri(Model m)
{
// The model is much smaller in Blender
glScalef(0.3F, 0.3F, 0.3F);
glBegin(GL_QUADS);
for(Face f : m.faces)
{
ArrayList<Vertex> vertices = f.getVertexPositions(m);
ArrayList<Vertex> texVertices = f.getTextureVertexPositions(m);
for(int i = 0; i < vertices.size(); i ++)
{
Vertex vert = vertices.get(i);
Vertex texVert = texVertices.get(i);
glTexCoord2d(texVert.x, texVert.y);
glVertex3d(vert.x, vert.y, vert.z);
}
}
glEnd();
}
The Model class just holds a list of all the vertices and faces.
package com.glh.model;
import java.util.*;
public class Model
{
public ArrayList<Vertex> vertices;
public ArrayList<Vertex> texVertices;
public ArrayList<Face> faces;
public Model(ArrayList<Vertex> vertices, ArrayList<Vertex> texVertices, ArrayList<Face> faces)
{
this.vertices = vertices;
this.texVertices = texVertices;
this.faces = faces;
}
}
I found it... My model is composed of triangles, and I was drawing QUADS in the game. I changed the: glBegin(GL_QUADS); to glBegin(GL_TRIANGLES);
Related
I followed the popular tutorials on skeletal animation by Thin Matrix and another code sample on
GitHub
The mesh renders find without any animations. But as soon as animations are applied it gets skewed.
If I pass identity matrices as bonetransforms, it works. Works as in it still renders properly just without any animation.
Also I noticed that the collada file I use uses Z as up and I use Y as up. But I export all data without changing a thing to make sure all transforms and vertex data work as intended. I later on plan on adjusting this as I export so that the data uses Y as up as well.
Here's my code for Skeletal Animation:
Header:
class SkeletalAnimation {
typedef struct Bone{
int id;
std::string name;
glm::mat4 offset;
std::vector<Bone> children;
} Bone;
typedef struct {
std::vector<float> translationTimestamps;
std::vector<float> rotationTimetamps;
std::vector<float> scalingTimetamps;
std::vector<glm::vec3> translations;
std::vector<glm::quat> rotations;
std::vector<glm::vec3> scalings;
} BoneTransforms;
typedef struct Animation {
float duration;
float ticksPerSecond;
std::unordered_map<std::string, BoneTransforms> boneTransforms;
Animation(float pDuration, float ticksPerSecond) :
duration(pDuration),
ticksPerSecond(ticksPerSecond),
boneTransforms({})
{}
Animation() {}
} Animation;
typedef std::unordered_map<std::string, std::pair<int, glm::mat4>> BoneData;
typedef std::unordered_map<std::string, Animation> AnimationMap;
typedef std::vector<glm::mat4> Pose;
typedef struct{
unsigned int segment;
float fracture;
} Segment;
typedef struct {
Pose pose;
BoneData boneData;
unsigned int boneCount;
std::string name;
Bone skeleton;
} MeshEntry;
typedef std::unordered_map<std::string, MeshEntry> MeshBoneMap;
private:
const std::string mPath;
SDL_Renderer* mRenderer;
std::vector<MeshEntry> mMeshEntries;
std::vector<SkeletalMesh*> mMeshes;
std::vector<ImageTexture*> mTextures;
std::vector<unsigned int> mMeshToTexture;
std::string* mCurrentAnimation;
std::vector<std::string> mAnimations;
AnimationMap mAnimationMap;
Segment mCurrentSegment;
glm::mat4 mGlobalInverseTransform;
MeshBoneMap mMeshBoneMap;
static glm::mat4 sIdentityMatrix;
void LoadNode(aiNode* pNode, const aiScene* pScene);
void LoadSkeletalMesh(aiMesh* pMesh, const aiScene* pScene);
bool LoadBones(Bone& pBone, aiNode* pNode, BoneData& pBoneData);
void LoadAnimations(const aiScene* pScene);
void LoadMaterials(const aiScene* pScene);
void Animate(float pDeltaTime, Bone& pSkeleton, Pose& pPose, glm::mat4& pParentTransform);
static inline glm::mat4 aiToGlmMat4(const aiMatrix4x4& pAiMat);
static inline glm::vec3 aiToGlmVec3(const aiVector3D& pAiVec);
static inline glm::quat aiToGlmQuat(const aiQuaternion& pAiVec);
static inline void GetSegment(Segment* pSegment,const std::vector<float>& pTimestamps,const float pDeltaTime);
public:
SkeletalAnimation(const std::string pPath, SDL_Renderer* pRenderer);
~SkeletalAnimation();
void LoadAnimation();
void GetAllAnimations(std::vector<std::string>* pAnimations);
float GetAnimationDuration();
void SetAnimationTime(float pTime);
void SetAnimation(std::string pAnimation);
void RenderAnimation(float pDeltaTime, SkeletalAnimationShader* pSkeletalAnimationShader);
void RenderStill(SkeletalAnimationShader* pSkeletalAnimationShader);
void ClearModel();
};
Source:
glm::mat4 SkeletalAnimation::sIdentityMatrix = glm::mat4();
SkeletalAnimation::SkeletalAnimation(const std::string pPath, SDL_Renderer* pRenderer) :
mPath(pPath),
mRenderer(pRenderer),
mCurrentAnimation(new std::string()),
mAnimations({}),
mAnimationMap({}),
mMeshBoneMap({})
{}
SkeletalAnimation::~SkeletalAnimation() {
}
void SkeletalAnimation::LoadAnimation() {
Assimp::Importer _importer;
const aiScene* _scene = _importer.ReadFile(mPath, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices);
if (!_scene) {
SDL_Log("Assimp Error Loading Animation at path: %s \n Error: %s .", mPath.c_str(), _importer.GetErrorString());
return;
}
mGlobalInverseTransform = glm::inverse(aiToGlmMat4(_scene->mRootNode->mTransformation));
LoadNode(_scene->mRootNode, _scene);
LoadAnimations(_scene);
LoadMaterials(_scene);
}
void SkeletalAnimation::LoadNode(aiNode* pNode, const aiScene* pScene) {
for (size_t i = 0; i < pNode->mNumMeshes; i++) {
LoadSkeletalMesh(pScene->mMeshes[pNode->mMeshes[i]], pScene);
}
for (size_t i = 0; i < pNode->mNumChildren; i++) {
LoadNode(pNode->mChildren[i], pScene);
}
}
void SkeletalAnimation::LoadSkeletalMesh(aiMesh* pMesh, const aiScene* pScene) {
MeshEntry _meshEntry;
_meshEntry.boneCount = pMesh->mNumBones;
_meshEntry.name = std::string(pMesh->mName.C_Str());
_meshEntry.pose = {};
_meshEntry.pose.resize(pMesh->mNumBones, sIdentityMatrix);
_meshEntry.boneData = {};
SkeletalMeshData _meshData;
for (size_t i = 0; i < pMesh->mNumVertices; i++) {
_meshData.vertices.insert(_meshData.vertices.end(),
{
pMesh->mVertices[i].x,
pMesh->mVertices[i].y , //Swaped Y and Z since Blender uses Z as up and I use Y as up.
pMesh->mVertices[i].z });
if (pMesh->mTextureCoords[0]) {
_meshData.uvs.insert(_meshData.uvs.end(),
{
pMesh->mTextureCoords[0][i].x,
pMesh->mTextureCoords[0][i].y
});
}
else {
_meshData.uvs.insert(_meshData.uvs.end(),
{
0.0f,
0.0f
});
}
_meshData.normals.insert(_meshData.normals.end(),
{
pMesh->mNormals[i].x,
pMesh->mNormals[i].y ,
pMesh->mNormals[i].z });
_meshData.boneIDs.insert(_meshData.boneIDs.end(), {
0,
0,
0,
0});
_meshData.weights.insert(_meshData.weights.end(), {
0.0f,
0.0f,
0.0f,
0.0f});
}
for (size_t i = 0; i < pMesh->mNumFaces; i++) {
aiFace _face = pMesh->mFaces[i];
for (size_t j = 0; j < _face.mNumIndices; j++) {
_meshData.indices.push_back(_face.mIndices[j]);
}
}
for (size_t i = 0; i < pMesh->mNumBones; i++) {
aiBone* _bone = pMesh->mBones[i];
glm::mat4 _offset = aiToGlmMat4(_bone->mOffsetMatrix);
_meshEntry.boneData[_bone->mName.C_Str()] = std::make_pair(i , _offset);
for (size_t j = 0; j < _bone->mNumWeights; j++) {
aiVertexWeight _weight = _bone->mWeights[j];
unsigned int _vertexID = _weight.mVertexId * 4;
for (size_t k = 0; k < 4; k++) {
if (_meshData.weights[_vertexID + k] == 0.0f) {
_meshData.weights[_vertexID + k] = _weight.mWeight;
_meshData.boneIDs[_vertexID + k] = i;
break;
}
}
}
}
for (size_t i = 0; i < _meshData.weights.size(); i+=4) {
float _totalWeight =
_meshData.weights[i] +
_meshData.weights[i+1] +
_meshData.weights[i+2] +
_meshData.weights[i+3];
if (_totalWeight > 0.0f) {
_meshData.weights[i] /= _totalWeight;
_meshData.weights[i+1] /= _totalWeight;
_meshData.weights[i+2] /= _totalWeight;
_meshData.weights[i+3] /= _totalWeight;
}
}
SkeletalMesh* _newMesh = new SkeletalMesh();
_newMesh->BuildMesh(_meshData);
mMeshes.push_back(_newMesh);
mMeshToTexture.push_back(pMesh->mMaterialIndex);
LoadBones(_meshEntry.skeleton, pScene->mRootNode, _meshEntry.boneData);
mMeshEntries.push_back(_meshEntry);
}
bool SkeletalAnimation::LoadBones(Bone& pBone ,aiNode* pNode, BoneData& pBoneData) {
if (pBoneData.find(pNode->mName.C_Str()) != pBoneData.end()) {
pBone.name = pNode->mName.C_Str();
pBone.id = pBoneData[pBone.name].first;
pBone.offset = pBoneData[pBone.name].second;
for (size_t i = 0; i < pNode->mNumChildren; i++) {
Bone _child;
LoadBones(_child, pNode->mChildren[i], pBoneData);
pBone.children.push_back(_child);
}
return true;
}
else {
for (size_t i = 0; i < pNode->mNumChildren; i++) {
if (LoadBones(pBone, pNode->mChildren[i], pBoneData)) {
return true;
}
}
}
return false;
}
void SkeletalAnimation::LoadAnimations(const aiScene* pScene) {
for (size_t i = 0; i < pScene->mNumAnimations; i++) {
Animation _currentInternalAnimation(0.0f, 1.0f);
aiAnimation* _currentAiAnimation = pScene->mAnimations[i];
mAnimations.push_back(std::string(_currentAiAnimation->mName.C_Str()));
if (i == 0) {
*mCurrentAnimation = mAnimations[0];
}
if (_currentAiAnimation->mTicksPerSecond != 0.0f) {
_currentInternalAnimation.ticksPerSecond = _currentAiAnimation->mTicksPerSecond;
}
else {
_currentInternalAnimation.ticksPerSecond = 1;
}
_currentInternalAnimation.duration = _currentAiAnimation->mDuration * _currentAiAnimation->mTicksPerSecond;
_currentInternalAnimation.boneTransforms = {};
BoneTransforms _transforms;
for (size_t j = 0; j < _currentAiAnimation->mNumChannels; j++) {
aiNodeAnim* _channel = _currentAiAnimation->mChannels[j];
for (size_t k = 0; k < _channel->mNumPositionKeys; k++) {
_transforms.translations.push_back(aiToGlmVec3(_channel->mPositionKeys[k].mValue));
_transforms.translationTimestamps.push_back(_channel->mPositionKeys[k].mTime);
}
for (size_t k = 0; k < _channel->mNumRotationKeys; k++) {
_transforms.rotations.push_back(aiToGlmQuat(_channel->mRotationKeys[k].mValue));
_transforms.rotationTimetamps.push_back(_channel->mRotationKeys[k].mTime);
}
for (size_t k = 0; k < _channel->mNumScalingKeys; k++) {
_transforms.scalings.push_back(aiToGlmVec3(_channel->mScalingKeys[k].mValue));
_transforms.scalingTimetamps.push_back(_channel->mScalingKeys[k].mTime);
}
_currentInternalAnimation.boneTransforms[_channel->mNodeName.C_Str()] = _transforms;
}
mAnimationMap[_currentAiAnimation->mName.C_Str()] = _currentInternalAnimation;
}
}
void SkeletalAnimation::LoadMaterials(const aiScene* pScene) {
mTextures.resize(pScene->mNumMaterials);
for (size_t i = 0; i < pScene->mNumMaterials; i++) {
aiMaterial* _material = pScene->mMaterials[i];
mTextures[i] = nullptr;
if (_material->GetTextureCount(aiTextureType_DIFFUSE)) {
aiString _path;
if (_material->GetTexture(aiTextureType_DIFFUSE, 0, &_path) == AI_SUCCESS) {
int _idx = std::string(_path.data).rfind("\\");
std::string _fileName = std::string(_path.data).substr(_idx + 1);
std::string _texturePath = std::string("assets/") + _fileName;
SDL_Log("Model Loading Texture at path: %s .", _texturePath.c_str());
mTextures[i] = new ImageTexture(_texturePath, mRenderer);
mTextures[i]->Load();
if (!mTextures[i]->IsLoaded()) {
delete mTextures[i];
mTextures[i] = nullptr;
SDL_Log("Model Error Loading Texture at path: %s .", _texturePath.c_str());
}
}
}
}
}
float SkeletalAnimation::GetAnimationDuration() {
return mAnimationMap[*mCurrentAnimation].duration;
}
void SkeletalAnimation::SetAnimationTime(float pTime) {
for (size_t i = 0; i < mMeshes.size(); i++) {
MeshEntry& _entry = mMeshEntries[i];
Animate(pTime, _entry.skeleton, _entry.pose, sIdentityMatrix);
}
}
void SkeletalAnimation::Animate(float pDeltaTime, Bone& pSkeleton, Pose& pPose, glm::mat4& pParentTransform) {
Animation& _currentAnimation = mAnimationMap[*mCurrentAnimation];
BoneTransforms& _boneTransforms = _currentAnimation.boneTransforms[pSkeleton.name];
pDeltaTime = fmod(pDeltaTime, _currentAnimation.duration);
//Calculate translations
GetSegment(&mCurrentSegment, _boneTransforms.translationTimestamps, pDeltaTime);
glm::vec3 _translation = glm::mix(
_boneTransforms.translations[mCurrentSegment.segment - 1],
_boneTransforms.translations[mCurrentSegment.segment],
mCurrentSegment.fracture);
//Calculate rotations
GetSegment(&mCurrentSegment, _boneTransforms.rotationTimetamps, pDeltaTime);
glm::quat _rotation = glm::slerp(
_boneTransforms.rotations[mCurrentSegment.segment - 1],
_boneTransforms.rotations[mCurrentSegment.segment],
mCurrentSegment.fracture);
//Calculate scalings
GetSegment(&mCurrentSegment, _boneTransforms.scalingTimetamps, pDeltaTime);
glm::vec3 _scaling = glm::mix(
_boneTransforms.scalings[mCurrentSegment.segment - 1],
_boneTransforms.scalings[mCurrentSegment.segment],
mCurrentSegment.fracture);
glm::mat4 _translationMatrix = glm::translate(glm::mat4(1.0f), _translation);
glm::mat4 _rotationMatrix = glm::toMat4(_rotation);
glm::mat4 _scalingMatrix = glm::scale(glm::mat4(1.0f), _scaling); glm::mat4(1.0f);
glm::mat4 _localTransform = _translationMatrix * _rotationMatrix * _scalingMatrix;
glm::mat4 _globalTransform = pParentTransform * _localTransform;
pPose[pSkeleton.id] = mGlobalInverseTransform * _globalTransform * pSkeleton.offset;
for (Bone& _child : pSkeleton.children) {
Animate(pDeltaTime, _child, pPose, _globalTransform);
}
}
void SkeletalAnimation::GetAllAnimations(std::vector<std::string>* pAnimations) {
pAnimations->clear();
*pAnimations = mAnimations;
}
void SkeletalAnimation::SetAnimation(std::string pAnimation) {
assert(std::find(mAnimations.begin(), mAnimations.end(), pAnimation) != mAnimations.end(), "Animation does not exist.");
*mCurrentAnimation = pAnimation;
}
void SkeletalAnimation::RenderAnimation(float pDeltaTime, SkeletalAnimationShader* pSkeletalAnimationShader) {
for (size_t i = 0; i < mMeshes.size(); i++) {
unsigned int _materialIndex = mMeshToTexture[i];
if (_materialIndex < mTextures.size() && mTextures[_materialIndex]) {
mTextures[_materialIndex]->Enable();
}
MeshEntry& _entry = mMeshEntries[i];
Animate(pDeltaTime, _entry.skeleton, _entry.pose, sIdentityMatrix);
pSkeletalAnimationShader->SetBoneTransforms(_entry.boneCount, _entry.pose);
mMeshes[i]->Render();
}
}
void SkeletalAnimation::RenderStill(SkeletalAnimationShader* pSkeletalAnimationShader) {
for (size_t i = 0; i < mMeshes.size(); i++) {
unsigned int _materialIndex = mMeshToTexture[i];
if (_materialIndex < mTextures.size() && mTextures[_materialIndex]) {
mTextures[_materialIndex]->Enable();
}
MeshEntry& _entry = mMeshEntries[i];
pSkeletalAnimationShader->SetBoneTransforms(_entry.boneCount, _entry.pose);
mMeshes[i]->Render();
}
}
void SkeletalAnimation::ClearModel() {
for (size_t i = 0; i < mMeshes.size(); i++) {
if (mMeshes[i]) {
delete mMeshes[i];
mMeshes[i] = nullptr;
}
}
for (size_t i = 0; i < mTextures.size(); i++) {
if (mTextures[i]) {
delete mTextures[i];
mTextures[i] = nullptr;
}
}
}
void SkeletalAnimation::GetSegment(Segment* pSegment,const std::vector<float>& pTimestamps, const float pDeltaTime) {
unsigned int _segment = 1;
while (pDeltaTime > pTimestamps[_segment]) {
_segment++;
}
float _start = pTimestamps[_segment - 1];
float _end = pTimestamps[_segment];
float _fracture = (pDeltaTime - _start) / (_end - _start);
pSegment->segment = _segment;
pSegment->fracture = _fracture;
}
glm::mat4 SkeletalAnimation::aiToGlmMat4(const aiMatrix4x4& pAiMat) {
glm::mat4 _glmMat;
for (int y = 0; y < 4; y++)
{
for (int x = 0; x < 4; x++)
{
_glmMat[x][y] = pAiMat[y][x];
}
}
return _glmMat;
}
glm::vec3 SkeletalAnimation::aiToGlmVec3(const aiVector3D& pAiVec) {
return glm::vec3(pAiVec.x, pAiVec.y, pAiVec.z); //Swapped Y and Z to correct Blender ups.
}
glm::quat SkeletalAnimation::aiToGlmQuat(const aiQuaternion& pAiQuat) {
return glm::quat(pAiQuat.w, pAiQuat.x, pAiQuat.y, pAiQuat.z);
}
I read my code line by line multiple times to see what I'm doing wrong but I can't think of anything. I don't think my shader is the issue but here's the vertex shader:
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 uv;
layout (location = 2) in vec3 normal;
layout (location = 3) in ivec4 boneIds;
layout (location = 4) in vec4 boneWeights;
out vec2 textureUV;
out vec3 lightNormal;
out vec4 worldPosition;
uniform mat4 model;
uniform mat4 projectionView;
uniform mat4 boneTransforms[50];
void main()
{
mat4 boneTransform = mat4(0.0f);
for(int i = 0; i < 4; i++){
boneTransform += boneTransforms[boneIds[i]] * boneWeights[i];
}
worldPosition = boneTransform * vec4(position, 1.0f);
worldPosition = model * worldPosition;
gl_Position = projectionView * worldPosition;
textureUV = uv;
lightNormal = mat3(transpose(inverse(model * boneTransform))) * normal;
}
The result:
I figured it out. BoneTransforms needed to be moved within the for loop. The same instance was getting over-written each loop cycle.
I'm building an Octree for computer graphics. Trying to build the tree with following code:
void Accel::build() {
Octree *m_octree = new Octree(depth);
std::vector<uint32_t> indices(triangleCount);
for (uint32_t i = 0; i < triangleCount; ++i) {
indices[i] = i;
}
m_octree->root = m_octree->octreeBuild(m_bbox, indices, m_mesh);
}
The tree and tree node class are like the following:
Octree.h
class Octree {
public:
uint32_t totalNodes = 0;
uint32_t maxDepth = 0;
uint32_t totalDepth = 0;
std::vector<OctreeNode *> octreeVector;
OctreeNode *root;
Octree();
Octree(uint32_t d);
~Octree();
OctreeNode *octreeBuild(BoundingBox3f boundingBox, std::vector<uint32_t> indices, Mesh* mesh);
};
Octree.cpp
Octree::Octree(){}
Octree::Octree(uint32_t d): maxDepth(d) {}
Octree::~Octree(){
}
OctreeNode *Octree::octreeBuild(BoundingBox3f boundingBox, std::vector<uint32_t> indices, Mesh* mesh) {
if (indices.empty()) {
OctreeNode *node = nullptr;
octreeVector.push_back(node);
return node;
}
if (indices.size() <= 10 || totalDepth == maxDepth) {
OctreeNode *node = new OctreeNode(indices, boundingBox, true);
octreeVector.push_back(node);
totalNodes += 1;
return node;
}
if (totalDepth < maxDepth){
std::vector<uint32_t> nodeArray[8]; // Eight vector to store child node indices
BoundingBox3f boxArray[8] = {
boundingBox, boundingBox, boundingBox, boundingBox, boundingBox, boundingBox, boundingBox, boundingBox
}; // Eight bounding box, one for each child node
for (uint32_t i = 0; i < 8; ++i) { // Seperate bounding box
Point3f corner = boundingBox.getCorner(i);
Point3f center = boundingBox.getCenter();
Point3f minPoint = Point3f(std::min(corner.x(), center.x()), std::min(corner.y(), center.y()), std::min(corner.z(), center.z()));
Point3f maxPoint = Point3f(std::max(corner.x(), center.x()), std::max(corner.y(), center.y()), std::max(corner.z(), center.z()));
boxArray[i] = BoundingBox3f(minPoint, maxPoint);
}
for (uint32_t i = 0; i < static_cast<uint32_t>(indices.size()); i++) { // Check overlapping, store indices to cooresponding vector
BoundingBox3f triangleBox = mesh->getBoundingBox(i);
for (uint32_t j = 0; j < 8; ++j) {
if (boxArray[j].overlaps(triangleBox) && boxArray[j].contains(triangleBox.getCenter())) {
nodeArray[j].push_back(i);
break;
}
}
}
OctreeNode *node = new OctreeNode(indices, boundingBox, false); // Build the new node with children
octreeVector.push_back(node);
if (octreeVector.size() == 1) {
root = node;
}
totalDepth += 1;
for (uint32_t i = 0; i < 8; ++i) {
node->child[i] = octreeBuild(boxArray[i], nodeArray[i], mesh);
totalNodes += 1;
}
return node;
}
return nullptr;
};
I manage to build the program and the Octree::build went through without error messages. But after trying to search form the tree root, I got the EXC_BAD_ACCESS error. It seems like the root of my octree is lost. I'm new to C++, not sure if that's because the algorithms or the way I program with C++ was wrong. Thanks for any advice!
I'm reading about Dijkstra's algorithm in CLRS, Third Edition (p. 662). Here is a part from the book I don't understand:
If the graph is sufficiently sparse — in particular, E = o(V^2/lg V) — we can improve the algorithm by implementing the min-priority queue with a binary min-heap.
Why should the graph be sparse?
Here is another part:
Each DECREASE-KEY operation takes time O(log V), and there are still
at most E such operations.
Suppose my graph looks like this:
I'd like to calculate the shortest path from 1 to 6 and use the min-heap approach. So first off, I add all my nodes to a min priority queue. After building a min heap, the min node is the source node (since its distance to itself is 0). I extract it and update distances of all its neighbors.
Then I need to call decreaseKey on the node with the lowest distance to make a new minimum of the heap. But how do I know its index in constant time?
Node
private static class Node implements Comparable<Node> {
final int key;
int distance = Integer.MAX_VALUE;
Node prev = null;
public Node(int key) {
this.key = key;
}
#Override
public int compareTo(Node o) {
if (distance < o.distance) {
return -1;
} else if (distance > o.distance) {
return 1;
} else {
return 0;
}
}
#Override
public String toString() {
return "key=" + key + " distance=" + distance;
}
#Override
public int hashCode() {
return key;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Node)) {
return false;
}
Node other = (Node) obj;
return key == other.key;
}
}
MinPriorityQueue
public static class MinPriorityQueue {
private Node[] array;
private int heapSize;
public MinPriorityQueue(Node[] array) {
this.array = array;
this.heapSize = this.array.length;
}
public Node extractMin() {
Node temp = array[0];
swap(0, heapSize - 1, array);
heapSize--;
sink(0);
return temp;
}
public boolean isEmpty() {
return heapSize == 0;
}
public void buildMinHeap() {
for (int i = heapSize / 2 - 1; i >= 0; i--) {
sink(i);
}
}
public void decreaseKey(int index, Node key) {
if (key.compareTo(array[index]) >= 0) {
throw new IllegalArgumentException("the new key must be greater than the current key");
}
array[index] = key;
while (index > 0 && array[index].compareTo(array[parentIndex(index)]) < 0) {
swap(index, parentIndex(index), array);
index = parentIndex(index);
}
}
private int parentIndex(int index) {
return (index - 1) / 2;
}
private int left(int index) {
return 2 * index + 1;
}
private int right(int index) {
return 2 * index + 2;
}
private void sink(int index) {
int smallestIndex = index;
int left = left(index);
int right = right(index);
if (left < heapSize && array[left].compareTo(array[smallestIndex]) < 0) {
smallestIndex = left;
}
if (right < heapSize && array[right].compareTo(array[smallestIndex]) < 0) {
smallestIndex = right;
}
if (index != smallestIndex) {
swap(smallestIndex, index, array);
sink(smallestIndex);
}
}
public Node min() {
return array[0];
}
private void swap(int i, int j, Node[] array) {
Node temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
Why should the graph be sparse?
The running time of Dijkstra's algorithm depends on the combination of the underlying data structure and the graph shape (edges and vertices).
For example, using a linked list would require O(V²) time, i.e. it only depends on the number of vertices.
Using a heap would require O((V + E) log V), i.e. it depends on both the number of vertices and the number of edges.
If your E is sufficiently smaller compared to V (as in E << V² / logV), then using heap becomes more efficient.
Then I need to call decreaseKey on the node with the lowest distance to make a new minimum of the heap. But how do I know its index in constant time?
If you're using a binary heap, then extractMin always runs in O(log V) time and gives you the node with the lowest distance (a.k.a. key).
For example, if you're implementing the binary min-heap as an array H, then the first element of the array H[1] (by convention we count from 1) will always be the element with the lowest distance, so finding it only takes O(1).
However, after each extractMin, insert or decreaseKey you have to run swim or sink to restore the heap condition, consequently moving the lowest-distance node to the top. This takes O(log V).
What you also want to do is maintain a mapping between keys in the heap and vertices, as mentioned in the book: "make sure that
vertices and corresponding heap elements maintain handles to each other" (briefly discussed in section 6.5).
Let's suppose that your graph consists of vertices (Node) in your case you have 7 (0 ->6 ) and edges. These are represented by the following model :
Node model :
public class Vertex{
final private String id;
final private String name;
public Vertex(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Vertex other = (Vertex) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
#Override
public String toString() {
return name;
}
}
And the edges will be present by this model : Edge
public class Edge {
private final String id;
private final Vertex source;
private final Vertex destination;
private final int weight;
public Edge(String id, Vertex source, Vertex destination, int weight) {
this.id = id;
this.source = source;
this.destination = destination;
this.weight = weight;
}
public String getId() {
return id;
}
public Vertex getDestination() {
return destination;
}
public Vertex getSource() {
return source;
}
public int getWeight() {
return weight;
}
#Override
public String toString() {
return source + " " + destination;
}
}
The graph (nodes + edges) will be present by this class : Graph
public class Graph {
private final List<Vertex> vertexes;
private final List<Edge> edges;
public Graph(List<Vertex> vertexes, List<Edge> edges) {
this.vertexes = vertexes;
this.edges = edges;
}
public List<Vertex> getVertexes() {
return vertexes;
}
public List<Edge> getEdges() {
return edges;
}
}
This is a simple implementation of Dijkstra’s algorithm. It does not use any performance optimization :
public class DijkstraAlgorithm {
private final List<Vertex> nodes;
private final List<Edge> edges;
private Set<Vertex> settledNodes;
private Set<Vertex> unSettledNodes;
private Map<Vertex, Vertex> predecessors;
private Map<Vertex, Integer> distance;
public DijkstraAlgorithm(Graph graph) {
// create a copy of the array so that we can operate on this array
this.nodes = new ArrayList<Vertex>(graph.getVertexes());
this.edges = new ArrayList<Edge>(graph.getEdges());
}
public void execute(Vertex source) {
settledNodes = new HashSet<Vertex>();
unSettledNodes = new HashSet<Vertex>();
distance = new HashMap<Vertex, Integer>();
predecessors = new HashMap<Vertex, Vertex>();
distance.put(source, 0);
unSettledNodes.add(source);
while (unSettledNodes.size() > 0) {
Vertex node = getMinimum(unSettledNodes);
settledNodes.add(node);
unSettledNodes.remove(node);
findMinimalDistances(node);
}
}
private void findMinimalDistances(Vertex node) {
List<Vertex> adjacentNodes = getNeighbors(node);
for (Vertex target : adjacentNodes) {
if (getShortestDistance(target) > getShortestDistance(node)
+ getDistance(node, target)) {
distance.put(target, getShortestDistance(node)
+ getDistance(node, target));
predecessors.put(target, node);
unSettledNodes.add(target);
}
}
}
private int getDistance(Vertex node, Vertex target) {
for (Edge edge : edges) {
if (edge.getSource().equals(node)
&& edge.getDestination().equals(target)) {
return edge.getWeight();
}
}
throw new RuntimeException("Should not happen");
}
private List<Vertex> getNeighbors(Vertex node) {
List<Vertex> neighbors = new ArrayList<Vertex>();
for (Edge edge : edges) {
if (edge.getSource().equals(node)
&& !isSettled(edge.getDestination())) {
neighbors.add(edge.getDestination());
}
}
return neighbors;
}
private Vertex getMinimum(Set<Vertex> vertexes) {
Vertex minimum = null;
for (Vertex vertex : vertexes) {
if (minimum == null) {
minimum = vertex;
} else {
if (getShortestDistance(vertex) < getShortestDistance(minimum)) {
minimum = vertex;
}
}
}
return minimum;
}
private boolean isSettled(Vertex vertex) {
return settledNodes.contains(vertex);
}
private int getShortestDistance(Vertex destination) {
Integer d = distance.get(destination);
if (d == null) {
return Integer.MAX_VALUE;
} else {
return d;
}
}
/*
* This method returns the path from the source to the selected target and
* NULL if no path exists
*/
public LinkedList<Vertex> getPath(Vertex target) {
LinkedList<Vertex> path = new LinkedList<Vertex>();
Vertex step = target;
// check if a path exists
if (predecessors.get(step) == null) {
return null;
}
path.add(step);
while (predecessors.get(step) != null) {
step = predecessors.get(step);
path.add(step);
}
// Put it into the correct order
Collections.reverse(path);
return path;
}
}
Then create a test class and add your graph values :
public class TestDijkstraAlgorithm {
private List<Vertex> nodes;
private List<Edge> edges;
#Test
public void testExcute() {
nodes = new ArrayList<Vertex>();
edges = new ArrayList<Edge>();
for (int i = 0; i < 11; i++) {
Vertex location = new Vertex("Node_" + i, "Node_" + i);
nodes.add(location);
}
addLane("Edge_0", 0, 1, 5);
addLane("Edge_1", 0, 2, 40);
addLane("Edge_2", 0, 3, 21);
addLane("Edge_3", 2, 3, 13);
addLane("Edge_4", 2, 4, 19);
addLane("Edge_5", 4, 5, 32);
addLane("Edge_6", 3, 5, 41);
addLane("Edge_7", 4, 6, 14);
addLane("Edge_8", 5, 6, 8);
// Lets check from location Loc_1 to Loc_10
Graph graph = new Graph(nodes, edges);
DijkstraAlgorithm dijkstra = new DijkstraAlgorithm(graph);
dijkstra.execute(nodes.get(0));
LinkedList<Vertex> path = dijkstra.getPath(nodes.get(10));
assertNotNull(path);
assertTrue(path.size() > 0);
for (Vertex vertex : path) {
System.out.println(vertex);
}
}
private void addLane(String laneId, int sourceLocNo, int destLocNo,
int duration) {
Edge lane = new Edge(laneId,nodes.get(sourceLocNo), nodes.get(destLocNo), duration );
edges.add(lane);
}
}
when I try to set
cub.SetArray(cube);
I get an error
Console Application1.exe has triggered a breakpoint
What I'm doing wrong? When I try to debug cub -> cubesarray I get size -842150451. I don't understand why.Here's my all code
class Cube{
public:
static const int Change_ARRAY = 5;
private:
string color;
int size;
int *walls;
int n; // current size of array
int maximumsize; // maximum size of array
void Increase(int many);
public:
Cube(int maximumsize = 0);
~Cube();
void SetWalls(int wall);
void SetColor(string color);
void SetSize(int size);
string GetColor(){return color;}
int GetWalls(int i){return walls[i];}
int GetSize(){return size;}
int GetN(){return n;}
};
Cube::Cube(int maximumsize):n(0), maximumsize(maximumsize), size(size), walls(NULL){
if(maximumsize > 0){
walls = new int[maximumsize];
}
}
Cube::~Cube(){
if(walls){
delete [] walls;
}
}
void Cube::Increase(int many){
if(many > maximumsize){
int *newest = new int[many];
for(int i=0; i<n; i++)
newest[i] = walls[i];
delete [] walls;
walls = newest;
maximumsize = many;
}else if( many < maximumsize){
int *newest = new int[many];
for(int i=0; i<many; i++)
newest[i] = walls[i];
delete [] walls;
walls = newest;
n = maximumsize = many;
}
}
void Cube::SetWalls(int wall){
if(n == maximumsize) Increase(n + Change_ARRAY);
walls[n] = wall;
n++;
}
void Cube::SetColor(string color){
this->color = color;
}
void Cube::SetSize(int size){
this->size = size;
}
class CubesArray{
public:
static const int Change_Array = 5;
private:
Cube *cubesarray;
int currentsize; // current size of array
int maxsize; // maximumsize
void Change (int kk);
public:
CubesArray(int maxsize = 1);
~CubesArray();
void SetArray(Cube c);
Cube GetArray(int ind){return cubesarray[ind];}
int GetCsize(){return currentsize;}
};
CubesArray::CubesArray(int maxsize):cubesarray(NULL), currentsize(0), maxsize(maxsize){
if(maxsize > 0){
cubesarray = new Cube[maxsize];
}
}
CubesArray::~CubesArray(){
if(cubesarray){
delete [] cubesarray;
}
}
void CubesArray::Change(int kk){
if(kk > maxsize){
Cube *newarr = new Cube[kk];
for(int i=0; i<currentsize; i++)
newarr[i] = cubesarray[i];
delete [] cubesarray;
cubesarray = newarr;
maxsize = kk;
}if(kk < maxsize){
Cube *newarr = new Cube[kk];
for(int i=0; i<kk; i++)
newarr[i] = cubesarray[i];
delete [] cubesarray;
cubesarray = newarr;
currentsize = maxsize = kk;
}
}
void CubesArray::SetArray(Cube cub){
if(currentsize = maxsize) Change(currentsize + Change_Array);
cubesarray[currentsize] = cub;
currentsize++;
}
void Read(CubesArray & cub);
int main(){
CubesArray cub;
Read(cub);
system("pause");
return 0;
}
void Read(CubesArray & cub){
string color;
int size;
int i=0;
Cube cube;
ifstream fd(Data);
while(!fd.eof()){
fd >> color >> size;
cube.SetSize(size);
cube.SetColor(color);
cout << cube.GetColor() << " " << cube.GetSize() << " ";
while(fd.peek() != '\n' && !fd.eof()){
int w;
fd >> w;
cube.SetWalls(w);
cout << cube.GetWalls(i) << " ";
cub.SetArray(cube); // when I set cube to cub I get this error!!!
i++;
}
cout << endl;
fd.ignore();
}
}
Change:
if(currentsize = maxsize)
To:
if(currentsize == maxsize)
In addition, here is your real problem:
You have no copy-constructor in class Cube, so the walls array is not properly copied whenever you send a Cube instance by value, e.g., cub.SetArray(cube).
You must define it as follows:
Cube::Cube(const Cube& cube):n(cube.n),maximumsize(cube.maximumsize),size(cube.size),wall(NULL)
{
if (maximumsize > 0)
{
walls = new int[maximumsize];
for (int i=0; i<maximumsize; i++)
wall[i] = cube.wall[i];
}
}
And you have no assignment-operator in class Cube, so the walls array is not properly copied whenever you assign one Cube instance into another, e.g., cubesarray[currentsize] = cub.
You must define it as follows:
Cube& Cube::operator=(const Cube& cube)
{
n = cube.n;
maximumsize = cube.maximumsize;
size = cube.size;
wall = NULL;
if (maximumsize > 0)
{
walls = new int[maximumsize];
for (int i=0; i<maximumsize; i++)
wall[i] = cube.wall[i];
}
return *this;
}
BTW, in the copy-constructor, you can simply call the assignment-operator (remove coding redundancy):
Cube::Cube(const Cube& cube)
{
if (this != &cube)
*this = cube;
}
Your Cube class violates the rule of three. Look here:
void CubesArray::SetArray(Cube cub){ // calls copy constructor
That call creates a copy of your Cube class. Your Cube class is not safely copyable. Please see this and scroll down to the Managing Resources section: What is The Rule of Three?
You should pass Cube by reference or const reference, not by value. Doing so may correct the error you're having now, but still, your class is faulty.
So what im trying to do is use an array of pointers to keep track of spawning bots and let them do battle by colliding. This is done by using methods in an array of pointers but i keep getting a LNK2020 error when I build it
This is the VBot.h File
class VBot
{
public:
VBot( int startX, int startY, Panel ^ drawingPanel ) :
x(startX), y(startY), panel(drawingPanel), energy(100), image(NULL) { };
virtual ~VBot() { };
virtual void Move() = 0;
virtual int EnergyToFightWith() = 0;
bool IsDead() const { return energy <= 0; }
virtual void Show();
bool CollidedWith ( VBot * b ) const;
void DoBattleWith ( VBot * b );
protected:
int x, y; // Current position of the VBot
gcroot<Drawing::Bitmap ^> image; // Image displayed for the VBot
gcroot<Panel ^> panel; // Panel on which to show the VBot.
int energy;
VBot();
};
class CNNBot : public VBot
{
public:
CNNBot( int startX, int startY, Panel ^ drawingPanel ):
VBot(startX, startY, drawingPanel)
{
image = gcnew Drawing::Bitmap("HappyBot.bmp");
}
~CNNBot(){};
void Move();
int EnergyToFightWith();
bool IsDead() { return (VBot::IsDead()); }
virtual void Show() { VBot::Show();}
bool CollidedWith ( VBot * b ) { return VBot::CollidedWith(b);}
void DoBattleWith ( VBot * b ){ VBot::DoBattleWith(b);}
private:
static const int MOVE_VAL = 55;
static const int RIGHT_BOUND = 490;
static const int DOWN = 40;
static const int MAXY = 379;
bool switcher;
};
This is the VBot.cpp File
#include "stdafx.h"
#include "Vbot.h"
void VBot::Show()
{
Graphics ^ g = panel->CreateGraphics();
g->DrawImageUnscaled( image, x, y );
g->~Graphics();
}
bool VBot::CollidedWith ( VBot * b ) const
{
if ( b == NULL )
return false;
return ( x + image->Width ) >= b->x
&& ( b->x + b->image->Width ) >= x
&& ( y + image->Height ) >= b->y
&& ( b->y + b->image->Height ) >= y;
}
void VBot::DoBattleWith ( VBot * b )
{
int mine = EnergyToFightWith();
int yours = b->EnergyToFightWith();
if( mine == yours )
{
energy = energy - mine / 2;
b->energy = b->energy - yours / 2;
}
void CNNBot::Move()
{
if (this->switcher)
{
this->x += MOVE_VAL;
if( this->x >= RIGHT_BOUND)
{
this->x = 0;
this->y += DOWN;
if(this->y > MAXY)
{
this->switcher = false;
this->y = MAXY;
}
}
}
else
{
this->x += MOVE_VAL;
if( this->x >= RIGHT_BOUND)
{
this->x = 0;
this->y -= DOWN;
if(this->y < 0)
{
this->switcher = true;
this->y = 0;
}
}
}
panel->Invalidate();
}
This is the BotContainer.h
#include <vcclr.h>
#include "VBot.h"
#include "stdafx.h"
#ifndef BOTCONTAINER_H
#define BOTCONTAINER_H
using namespace std;
//------------------------------------------------------------------------------
// This class is an array class that will contain pointers to bots that are spawned
//
class BotContainer
{
private:
static const int MAXARRAY = 1000; //this is the max size of the array
VBot * list[MAXARRAY]; //This is the array of pointers
int count; // the variable that keeps track of the array
public:
//-------------------------------------------------------------------------------
//this the constructor that sets count to zero
//-------------------------------------------------------------------------------
BotContainer(){ count = 0;}
//--------------------------------------------------------------------------------
//This method will check for a collision between the all possible combinations of
//two bots it is const
//---------------------------------------------------------------------------------
void checkCollisions() const;
//---------------------------------------------------------------------------------
//This method will destroy all dead bots in the array
//---------------------------------------------------------------------------------
void destroy();
//----------------------------------------------------------------------------------
//This method will take given bot pointer and add it to the list.
//The Parameters are a pointer to a VBot called inBot.
//This inBot will be added to the end of the array which will then increment count
//Before adding it checks to make sure the array isn't full.
//----------------------------------------------------------------------------------
void add(VBot * inBot);
void Add(CNNBot * inBot);
//----------------------------------------------------------------------------------
//This method will move all the elements in the array.
//----------------------------------------------------------------------------------
void moveAll();
};
#endif
This the BotContainer.cpp file
#include "stdafx.h"
#include <vcclr.h>
#include "BotContainer.h"
//---------------------------------------------------------------------------------
//This method will delete all the deat bots in the array from the highest indexed
//dead bot. If a bot is deleted then the method will shift all higher indexed elements
// to one index less than its previous index
//-------------------------------------------------------------------------------------
void BotContainer::destroy()
{
for( int i = count - 1; i <= 0; i--)
{
if ( list[i]->IsDead() == true )
{
int current = i; //index of the dead array
delete list[i];
for( int j = i + 1; j < count; j++)
{
list[current] = list[j];
current = j; // index of the next element above the one just shifted
}
count--;
}
}
}
//------------------------------------------------------------------------------------
//This method will go throught the array calling move() for each bot in the array
//-----------------------------------------------------------------------------------
void BotContainer::moveAll()
{
for( int i = 0; i < count; i++)
{
list[i]->Move();
}
}
//-----------------------------------------------------------------------------------
//This method checks for collisions between any 2 bots in the array making sure to not
//test (ivsi) or (ivsj) and also testing (jvsi).
// This method is const therfore it doesn't change the array just tests it.
//-----------------------------------------------------------------------------------
void BotContainer::checkCollisions() const
{
for( int i = 0; i < count; i++)
{
if (list[i]->IsDead() != true)
{
for( int j = i + 1; j < count; j++)
{
if(list[j]->IsDead() != true)
{
if ( list[i]->CollidedWith(list[j]))
{
list[i]->DoBattleWith(list[j]);
}
}
}
}
}
}
//--------------------------------------------------------------------------------
//This method will add a pointer of a bot to the array checking first to make sure
//the array isn't full
//--------------------------------------------------------------------------------
void BotContainer::add(VBot * inBot)
{
if(this->count <= MAXARRAY)
{
list[count] = inBot;
count++;
}
}
void BotContainer::Add(CNNBot * inBot)
{
if(this->count <= MAXARRAY)
{
list[count] = inBot;
count++;
}
}
And this is the Form.h file
#pragma once
#include "stdafx.h"
#include <vcclr.h>
#include "BotContainer.h"
namespace Prog3
{
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
/// <summary>
/// Summary for Form1
/// </summary>
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
x = 0;
y = 0;
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Form1()
{
if (components)
{
delete components;
}
}
private: System::Windows::Forms::Panel^ pnlGameZone;
private: System::Windows::Forms::ComboBox^ cmbSelect;
protected:
private: System::Windows::Forms::Button^ btnBotSpawn;
private: System::Windows::Forms::TrackBar^ trackBar1;
private: System::Windows::Forms::Label^ label1;
private: System::Windows::Forms::Label^ label2;
private: System::Windows::Forms::Label^ label3;
private: System::Windows::Forms::Label^ label4;
private: System::ComponentModel::IContainer^ components;
private:
/// <summary>
/// Required designer variable.
/// </summary>
int x;
int y;
BotContainer VList();
private: System::Windows::Forms::Timer^ tmrspeed;
private: System::Windows::Forms::NumericUpDown^ updownX;
private: System::Windows::Forms::NumericUpDown^ updownY;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
this->components = (gcnew System::ComponentModel::Container());
this->pnlGameZone = (gcnew System::Windows::Forms::Panel());
this->cmbSelect = (gcnew System::Windows::Forms::ComboBox());
this->btnBotSpawn = (gcnew System::Windows::Forms::Button());
this->trackBar1 = (gcnew System::Windows::Forms::TrackBar());
this->label1 = (gcnew System::Windows::Forms::Label());
this->label2 = (gcnew System::Windows::Forms::Label());
this->label3 = (gcnew System::Windows::Forms::Label());
this->label4 = (gcnew System::Windows::Forms::Label());
this->updownX = (gcnew System::Windows::Forms::NumericUpDown());
this->updownY = (gcnew System::Windows::Forms::NumericUpDown());
this->tmrspeed = (gcnew System::Windows::Forms::Timer(this-
>components));
(cli::safe_cast<System::ComponentModel::ISupportInitialize^ >
(this->trackBar1))->BeginInit();
(cli::safe_cast<System::ComponentModel::ISupportInitialize^ >
(this->updownY))->BeginInit();
(cli::safe_cast<System::ComponentModel::ISupportInitialize^ >
(this->updownY))->BeginInit();
this->SuspendLayout();
//
// pnlGameZone
//
this->pnlGameZone->BackColor =
System::Drawing::SystemColors::Window;
this->pnlGameZone->Location = System::Drawing::Point(53, 102);
this->pnlGameZone->Name = L"pnlGameZone";
this->pnlGameZone->Size = System::Drawing::Size(495, 379);
this->pnlGameZone->TabIndex = 0;
//
// cmbSelect
//
this->cmbSelect->DropDownStyle =
System::Windows::Forms::ComboBoxStyle::DropDownList;
this->cmbSelect->FormattingEnabled = true;
this->cmbSelect->Items->AddRange(gcnew cli::array< System::Object^
>(3) {L"CNN Bot\t", L"MSNBC Bot", L"FOX NEWS Bot"});
this->cmbSelect->Location = System::Drawing::Point(53, 52);
this->cmbSelect->Name = L"cmbSelect";
this->cmbSelect->Size = System::Drawing::Size(121, 21);
this->cmbSelect->TabIndex = 1;
//
// btnBotSpawn
//
this->btnBotSpawn->Location = System::Drawing::Point(427, 48);
this->btnBotSpawn->Name = L"btnBotSpawn";
this->btnBotSpawn->Size = System::Drawing::Size(87, 23);
this->btnBotSpawn->TabIndex = 4;
this->btnBotSpawn->Text = L"Spawn a Bot";
this->btnBotSpawn->UseVisualStyleBackColor = true;
this->btnBotSpawn->Click += gcnew System::EventHandler(this,
&Form1::tmrspeed_Tick);
//
// trackBar1
//
this->trackBar1->Location = System::Drawing::Point(552, 48);
this->trackBar1->Maximum = 800;
this->trackBar1->Minimum = 50;
this->trackBar1->Name = L"trackBar1";
this->trackBar1->Size = System::Drawing::Size(134, 45);
this->trackBar1->TabIndex = 5;
this->trackBar1->TickFrequency = 100;
this->trackBar1->Value = 50;
//
// label1
//
this->label1->AutoSize = true;
this->label1->Location = System::Drawing::Point(50, 34);
this->label1->Name = L"label1";
this->label1->Size = System::Drawing::Size(65, 13);
this->label1->TabIndex = 6;
this->label1->Text = L"Select a Bot";
//
// label2
//
this->label2->AutoSize = true;
this->label2->Location = System::Drawing::Point(200, 35);
this->label2->Name = L"label2";
this->label2->Size = System::Drawing::Size(53, 13);
this->label2->TabIndex = 7;
this->label2->Text = L"Starting X";
//
// label3
//
this->label3->AutoSize = true;
this->label3->Location = System::Drawing::Point(306, 35);
this->label3->Name = L"label3";
this->label3->Size = System::Drawing::Size(53, 13);
this->label3->TabIndex = 8;
this->label3->Text = L"Starting Y";
//
// label4
//
this->label4->AutoSize = true;
this->label4->Location = System::Drawing::Point(549, 32);
this->label4->Name = L"label4";
this->label4->Size = System::Drawing::Size(103, 13);
this->label4->TabIndex = 9;
this->label4->Text = L"Speed of Movement";
//
// updownX
//
this->updownX->Location = System::Drawing::Point(203, 53);
this->updownX->Name = L"updownX";
this->updownX->Size = System::Drawing::Size(85, 20);
this->updownX->TabIndex = 12;
//
// updownY
//
this->updownY->Location = System::Drawing::Point(309, 52);
this->updownY->Name = L"updownY";
this->updownY->Size = System::Drawing::Size(80, 20);
this->updownY->TabIndex = 13;
//
// tmrspeed
//
this->tmrspeed->Tick += gcnew System::EventHandler(this,
&Form1::tmrspeed_Tick);
//
// Form1
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(740, 493);
this->Controls->Add(this->updownY);
this->Controls->Add(this->updownX);
this->Controls->Add(this->label4);
this->Controls->Add(this->label3);
this->Controls->Add(this->label2);
this->Controls->Add(this->label1);
this->Controls->Add(this->trackBar1);
this->Controls->Add(this->btnBotSpawn);
this->Controls->Add(this->cmbSelect);
this->Controls->Add(this->pnlGameZone);
this->Name = L"Form1";
this->Text = L"Form1";
this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load);
(cli::safe_cast<System::ComponentModel::ISupportInitialize^ >
(this->trackBar1))->EndInit();
(cli::safe_cast<System::ComponentModel::ISupportInitialize^ >
(this->updownX))->EndInit();
(cli::safe_cast<System::ComponentModel::ISupportInitialize^ >e) e)
(this->updownY))->EndInit();
this->ResumeLayout(false);
this->PerformLayout();
}
#pragma endregion
private: System::Void btnBotSpawn_Click(System::Object^ sender, System::EventArgs^
e) {
if ( cmbSelect->SelectedIndex == 0)
{
x = Decimal::ToInt32(updownX->Value);
y = Decimal::ToInt32(updownY->Value);
CNNBot newBot(x,y,pnlGameZone);
CNNBot * temp = &newBot;
VList().Add(temp);
}
}
private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e)
{
}
private: System::Void tmrspeed_Tick(System::Object^ sender, System::EventArgs^ e)
{
VList().moveAll();
VList().checkCollisions();
VList().destroy();
}
};
};
This is the error message i get.
Error 1 error LNK2020: unresolved token (06000003) Prog3.Form1::VList C:\Users\Duerst\Documents\Visual Studio 2010\Projects\Prog 3\Prog 3\Prog 3.obj Prog 3
I am a novice programmer so it is probably something pretty basic, but it would be great if you guys could find what I've done wrong.
The problem is that when you declare VList like this:
BotContainer VList();
The compiler thinks it is a function instead of an object because of the () operator.
To fix it, remove the parenthesis ()
BotContainer VList;
And also remove the parenthesis on very ocurrence of VList:
VList.Add(temp);
instead of:
VList().Add(temp);
And:
VList.moveAll();
VList.checkCollisions();
VList.destroy();
instead of:
VList().moveAll();
VList().checkCollisions();
VList().destroy();