I'm following this tutorial on voxel terrain generation in Godot. However, I'm using C++ instead of GDScript. I got to about 7:08. The code is pretty much exactly translated and it compiles fine. However, when I try to open the project in Godot I get the Godot Engine has stopped working. A problem caused the program to stop working correctly... error. I don't know what's happening. When I delete the dll from the project it opens fine. Before, I got the official example to compile perfectly.
When I run the shell script Godot_v3.5.1-stable_win64_console.cmd instead of the executable the following error gets printed:
ERROR: Another resource is loaded from path 'res://Chunk.gdns' (possible cyclic resource inclusion).
at: (core/resource.cpp:79)
chunk.h:
#ifndef CHUNK_H
#define CHUNK_H
#include <vector>
#include <Godot.hpp>
#include <StaticBody.hpp>
#include <SurfaceTool.hpp>
#include <ArrayMesh.hpp>
#include <Mesh.hpp>
#include <MeshInstance.hpp>
namespace godot {
const Vector3 VERTICES[] = {
Vector3(0, 0, 0),
Vector3(1, 0, 0),
Vector3(0, 1, 0),
Vector3(1, 1, 0),
Vector3(0, 0, 1),
Vector3(1, 0, 1),
Vector3(0, 1, 1),
Vector3(1, 1, 1),
};
const Vector3 DIMENSION = Vector3(16, 64, 16);
const unsigned char TOP[] = {2, 3, 7, 6};
const unsigned char BOTTOM[] = {0, 4, 5, 1};
const unsigned char LEFT[] = {6, 4, 0, 2};
const unsigned char RIGHT[] = {3, 1, 5, 7};
const unsigned char FRONT[] = {7, 5, 4, 6};
const unsigned char BACK[] = {2, 0, 1, 3};
struct Block {};
class Chunk : public StaticBody {
GODOT_CLASS(Chunk, StaticBody)
private:
std::vector<Block> blocks;
SurfaceTool* st;
ArrayMesh* mesh;
MeshInstance* meshInstance;
public:
void Update();
void CreateBlock(const unsigned char x, const unsigned char y, const unsigned char z);
void CreateFace(const unsigned char* indices, const unsigned char x, const unsigned char y, const unsigned char z);
Chunk();
~Chunk();
static void _register_methods();
void _init();
void _process(float delta);
};
}
#endif
chunk.cpp:
#include "chunk.h"
using namespace godot;
void Chunk::_register_methods() {
register_method("_process", &Chunk::_process);
}
Chunk::Chunk() {}
Chunk::~Chunk() {
delete st;
delete mesh;
delete meshInstance;
}
void Chunk::_init() {
st = new SurfaceTool();
mesh = nullptr;
meshInstance = nullptr;
Update();
}
void Chunk::_process(float delta) {}
void Chunk::Update() {
// Unload chunk
if (meshInstance != nullptr) {
meshInstance->call_deferred("queue_free");
meshInstance = nullptr;
}
mesh = new ArrayMesh();
meshInstance = new MeshInstance();
st->begin(Mesh::PRIMITIVE_TRIANGLES);
for (unsigned char x = 0; x < DIMENSION.x; x++) {
for (unsigned char y = 0; y < DIMENSION.y; y++) {
for (unsigned char z = 0; z < DIMENSION.z; z++) {
CreateBlock(x, y, z);
}
}
}
st->generate_normals(false);
st->commit(mesh);
meshInstance->set_mesh(mesh);
add_child(meshInstance);
meshInstance->create_trimesh_collision();
}
void Chunk::CreateBlock(const unsigned char x, const unsigned char y, const unsigned char z) {
CreateFace(TOP, x, y, z);
CreateFace(BOTTOM, x, y, z);
CreateFace(LEFT, x, y, z);
CreateFace(RIGHT, x, y, z);
CreateFace(BACK, x, y, z);
CreateFace(FRONT, x, y, z);
}
void Chunk::CreateFace(const unsigned char* indices, const unsigned char x, const unsigned char y, const unsigned char z) {
auto offset = Vector3(x, y, z);
// Get corners
auto a = VERTICES[indices[0]] + offset;
auto b = VERTICES[indices[1]] + offset;
auto c = VERTICES[indices[2]] + offset;
auto d = VERTICES[indices[3]] + offset;
PoolVector3Array triangleOne;
triangleOne.push_back(a);
triangleOne.push_back(b);
triangleOne.push_back(c);
PoolVector3Array triangleTwo;
triangleTwo.push_back(a);
triangleTwo.push_back(c);
triangleTwo.push_back(d);
st->add_triangle_fan(triangleOne);
st->add_triangle_fan(triangleTwo);
}
Related
I'm using Visual Studio Express 2017 to work with OpenGL. My teacher provided the class with this code that worked on the lab computers which had the 2012 version installed, but on my PC it is not working. It keeps giving me 2 errors:
no instance of constructor canvas::canvas matches the argument list. E0289
This error is on the line Canvas cvs(640, 480, "my prog");
Canvas::canvas(canvas&&)
cannot convert argument 3 from 'const char[7] to char*'
C2664
This error is on the line if (n < 3)
Sorry, it's my first time using this site, so I pasted the entire code. It would be great if anyone could help me with this. I'm not really good with programming.
#include "stdafx.h"
#include<Windows.h>
#include<glut.h>
#include<gl/GLU.h>
#include <gl/GL.H>
#include<math.h>
#include<fstream>
void myInit(void)
{
glClearColor(1.0, 1.0, 1.0, 0.0);
glColor3ub(1.0, 1.0, 1.0);
glPointSize(100.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 640.0, 0.0, 480.0);
}
class Point2 //single point w/ floating point coordinates
{
public:
Point2() { x = y = 0.0f; }
Point2(float xx, float yy) { x = xx; y = yy; }
void set(float xx, float yy) { x = xx; y = yy; }
float getX() { return x; }
float getY() { return y; }
void draw(void)
{
glBegin(GL_POINTS); //draw this point
glVertex2f((GLfloat)x, (GLfloat)y);
glEnd();
}
private:
float x, y;
};
class IntRect //aligned rectangle with integer coordinates, used for viewport
{
public:
IntRect() { l = 0; r = 100; b = 0; t = 100; }
IntRect(int left, int right, int bottom, int top)
{
l = left; r = right; b = bottom; t = top;
}
void set(int left, int right, int bottom, int top)
{
l = left; r = right; b = bottom; t = top;
}
void draw(void); //draw this rectangle using OpenGL
int getL(void)
{
return l;
}
int getR(void)
{
return r;
}
int getT(void)
{
return t;
}
int getB(void)
{
return b;
}
private:
int l, r, b, t;
};
class RealRect //simlar to IntRect but w/ floating points & used for world window
{
public:
RealRect() { l = 0; r = 100; b = 0; t = 100; }
RealRect(float left, float right, float bottom, float top)
{
l = left; r = right; b = bottom; t = top;
}
void set(float left, float right, float bottom, float top)
{
l = left; r = right; b = bottom; t = top;
}
float getL(void)
{
return l;
}
float getR(void)
{
return r;
}
float getT(void)
{
return t;
}
float getB(void)
{
return b;
}
void draw(void); //draw this rectangle using OpenGL
private:
float l, r, b, t;
};
//<<End Support Classes>>>
class Canvas
{
public:
Canvas(int width, int height, char* windowTitle); //constructor
void setWindow(float l, float r, float b, float t);
void setViewport(int l, int r, int b, int t);
IntRect getViewport(void); //divulge the viewport data
RealRect getWindow(void); // divulge the window data
float getWindowAspectRatio(void);
void clearScreen();
void setBackgroundColor(float r, float g, float b);
void setColor(float r, float g, float b);
void lineTo(float x, float y);
void lineTo(Point2 p);
void moveTo(float x, float y);
void moveTo(Point2 p);
void moveRel(float dx, float dy);
void turnTo(float angle);
void turn(float angle);
void forward(float dist, int isVisible);
Point2 Tween(Point2 A, Point2 B, float t);
void drawTween(Point2 A[], Point2 B[], int N, float t);
private:
Point2 CP; //current position in the world
float CD; //current direction in the world
IntRect viewport; //the current window
RealRect window; //the current viewport
};
//<<moveTo>>
//changes current point
void Canvas::moveTo(float x, float y)
{
CP.set(x, y);
}
//<<lineTo>>
//draws a line from current point to new point
void Canvas::lineTo(float x, float y)
{
glBegin(GL_LINES);
glVertex2f((GLfloat)CP.getX(), (GLfloat)CP.getY());
glVertex2f((GLfloat)x, (GLfloat)y); //draw the line
glEnd();
CP.set(x, y); //update current point to new point
glFlush();
}
//<<setWindow>>
void Canvas::setWindow(float l, float r, float b, float t)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D((GLdouble)l, (GLdouble)r, (GLdouble)b, (GLdouble)t);
window.set(l, r, b, t);
}
//<<setViewport>>
void Canvas::setViewport(int l, int r, int b, int t)
{
glViewport(l, b, r - l, t - b);
viewport.set(l, r, b, t);
}
IntRect Canvas::getViewport(void)
{
return viewport;
}
RealRect Canvas::getWindow(void)
{
return window;
}
void Canvas::clearScreen(void)
{
glClear(GL_COLOR_BUFFER_BIT);
}
void Canvas::setBackgroundColor(float r, float g, float b)
{
glClearColor(r, g, b, 0.0); //4th variable level of transparency, may need to change
}
void Canvas::setColor(float r, float g, float b)
{
glColor3f(r, g, b);
}
void Canvas::lineTo(Point2 p)
{
glBegin(GL_LINES);
glVertex2f((GLfloat)CP.getX(), (GLfloat)CP.getY());
glVertex2f((GLfloat)p.getX(), (GLfloat)p.getY());
glEnd();
CP.set(p.getX(), p.getY());
glFlush();
}
Canvas::Canvas(int width, int height, char* windowTitle)
{
char* argv[1]; //dummy argument list for glutinit()
char dummyString[8];
argv[0] = dummyString; //hook up the pointer
int argc = 1;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(width, height);
glutInitWindowPosition(20, 20);
glutCreateWindow("FourCircle");
setWindow(0, (float)width, 0, (float)height); // default world window
setViewport(0, width, 0, height); //default viewport
CP.set(0.0f, 0.0f); //initialize the cp to (0,0)
}
void Canvas::moveTo(Point2 p) //moves current point CP to point p object
{
float x1, y1;
x1 = p.getX();
y1 = p.getY();
CP.set(x1, y1);
}
float Canvas::getWindowAspectRatio(void) //calculates aspect ratio of world window
{
float width, height, aspectRatio;
width = window.getR() - window.getL();
height = window.getT() - window.getB();
aspectRatio = width / height;
return aspectRatio;
}
void Canvas::moveRel(float dx, float dy)
{
CP.set(CP.getX() + dx, CP.getY() + dy);
}
void Canvas::turnTo(float angle)
{
CD = angle;
}
void Canvas::turn(float angle)
{
CD += angle;
}
void Canvas::forward(float dist, int isVisible)
{
const float RadPerDeg = 0.017453393; //radians per degree
float x = CP.getX() + dist * cos(RadPerDeg * CD);
float y = CP.getY() + dist * sin(RadPerDeg * CD);
if (isVisible)
lineTo(x, y);
else
moveTo(x, y);
}
Point2 Canvas::Tween(Point2 A, Point2 B, float t)
{
Point2 P;
P.set(A.getX() + (B.getX() - A.getX())*t, A.getY() + (B.getY() - A.getY())*t);
return P;
}
class point {
public:
int x, y;
};
void Canvas::drawTween(Point2 A[], Point2 B[], int N, float t)
{
for (int i = 0; i < N; i++)
{
Point2 P;
P = Tween(A[i], B[i], t);
if (i == 0) moveTo(P.getX(), P.getY());
else lineTo(P.getX(), P.getY());
}
}
Canvas cvs(640, 480, "my prog");
void ngon(int n, float x, float y, float radius, float rot)
{
if (n < 3)
return;
double angle = rot * 3.141592 / 180;
double angleinc = 2 * 3.141592 / n;
cvs.moveTo(radius*cos(angle) + x, radius*sin(angle) + y);
for (int i = 0; i < n; i++)
{
angle += angleinc;
cvs.lineTo(radius*cos(angle) + x, radius*sin(angle) + y);
}
glFlush();
}
void display(void)
{
glColor3ub(255, 60, 60);
ngon(100, 300, 320, 60, 90);//top
ngon(100, 300, 200, 60, 90);//bottom
ngon(100, 240, 260, 60, 90);//left
ngon(100, 360, 260, 60, 90);//right
ngon(100, 260, 300, 60, 90);//top left
ngon(100, 340, 300, 60, 90);//top right
ngon(100, 260, 220, 60, 90);//bottom left
ngon(100, 340, 220, 60, 90);//bottom right
glFlush();
}
void main(void)
{
glutDisplayFunc(display);
glutMainLoop();
}
The code is not valid since C++11 and makes use of a deprecated conversion before that. A string literal is not convertible to char*, only to const char*.
So the constructor should read
Canvas(int width, int height, const char* windowTitle); //constructor
^^^^^
Depending on the settings, Visual Studio will accept the wrong version anyway, even in newer standard version modes, but that is not standard-conform.
Interestingly the constructor parameter seems to be unused in the definition of the constructor, so maybe just remove it completely.
The constructor also has another issue: glutInit is supposed to be passed argc and argv from main's parameters directly. The shown code passes a pointer to an uninitialized array instead. Depending on what glutInit does with this, it is likely to cause undefined behavior for reading uninitialized values of dummyString.
void main(void) is also not legal standard C++. The return type of main should always be int. Using void as single parameter for an empty parameter list is allowed, but unconventional, in C++. It comes from C, where it has a different meaning from an empty parameter list. In C++ one would usually just write () instead of (void).
I'm trying to create a triangle mesh of a sphere surface and draw it with OpenGL 4.1.
This is the code that I'm currently using obtained from the second answer of that question, The vertex layout is [x, y, z, r, b, g, a] that's why there is 7 float foreach vertex:
std::vector<float> vertices;
std::vector<unsigned int> indices;
const float dLambda = 2 * glm::pi<float>() / meridianNumber;
const float dPhi = glm::pi<float>() / parallelNumber;
unsigned int lastVertex = 0;
for (int i = 0; i < parallelNumber; ++i) {
for (int j = 0; j < meridianNumber; ++j) {
std::cout << "lot: " << glm::degrees(j * dLambda);
std::cout << "\tlat: " << glm::degrees(i * dPhi);
std::cout << std::endl;
float lambda1 = j * dLambda;
float phi1 = i * dPhi;
float lambda2 = j+1 == parallelNumber ? 2 * glm::pi<float>()
: (j+1) * dLambda;
float phi2 = i+1 == meridianNumber ? glm::pi<float>()
: (i+1) * dPhi;
// vertex 1
vertices.emplace_back(cosf(lambda1) * sinf(phi1) * radius);
vertices.emplace_back(cosf(phi1) * radius);
vertices.emplace_back(sinf(lambda1) * sinf(phi1) * radius);
vertices.emplace_back(0.5f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
// vertex 2
vertices.emplace_back(cosf(lambda1) * sinf(phi2) * radius);
vertices.emplace_back(cosf(phi2) * radius);
vertices.emplace_back(sinf(lambda1) * sinf(phi2) * radius);
vertices.emplace_back(0.5f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
// vertex 3
vertices.emplace_back(cosf(lambda2) * sinf(phi1) * radius);
vertices.emplace_back(cosf(phi1) * radius);
vertices.emplace_back(sinf(lambda2) * sinf(phi1) * radius);
vertices.emplace_back(0.5f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
// vertex 4
vertices.emplace_back(cosf(lambda2) * sinf(phi2) * radius);
vertices.emplace_back(cosf(phi2) * radius);
vertices.emplace_back(sinf(lambda2) * sinf(phi2) * radius);
vertices.emplace_back(0.5f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
indices.emplace_back(lastVertex);
indices.emplace_back(lastVertex+1);
indices.emplace_back(lastVertex+2);
indices.emplace_back(lastVertex+1);
indices.emplace_back(lastVertex+3);
indices.emplace_back(lastVertex+2);
lastVertex += 4;
}
But I am doing something wrong because that's what I'm drawing:
the code that I'm using to draw is:
GLCall(glDrawElements(
GL_TRIANGLES,
indicesNumber,
GL_UNSIGNED_INT,
(const void*) 0
));
EDIT 1:
The VAO settings are pretty complicated because I wrote a little layer of abstraction over opengl...
I have a class called VertexBuffer that creates, keeps alive and destroys an OpenGL array buffer.
Another class IndexBuffer is very similar to the previous one that manages the Element array buffer.
This two classes are very simple to use they can be constructed, binded, unbinded and destroyed, nothing more.
There is a third class that represents a layout of a single vertex in an OpenGL vertex buffer; this class called VertexLayout contains all the data that is necessary to call the glVertexAttribPointer.
hpp:
class VertexLayout {
private:
struct Element {
unsigned int type;
unsigned int count;
unsigned char normalized;
size_t typeSize;
Element(
unsigned int type, unsigned int count, unsigned char normalized,
size_t typeSize
);
};
std::vector<Element> elements;
unsigned int stride;
public:
VertexLayout();
template<typename T>
VertexLayout &push(unsigned int count, unsigned char normalized = GL_FALSE){
std::fputs(
"this function has to be implemented for desired type",
stderr
);
assert(false);
return *this;
}
const std::vector<Element> &getElements() const;
unsigned int getStride() const;
};
cpp:
template<>
VertexLayout &VertexLayout::push<unsigned int>(
unsigned int count, unsigned char normalized
) {
elements.emplace_back(
GL_UNSIGNED_INT, count, normalized, sizeof(unsigned int)
);
stride += count * sizeof(unsigned int);
return *this;
};
template<>
VertexLayout &VertexLayout::push<unsigned char>(
unsigned int count, unsigned char normalized
) {
elements.emplace_back(
GL_UNSIGNED_BYTE, count, normalized, sizeof(unsigned char)
);
stride += count * sizeof(unsigned char);
return *this;
};
template<>
VertexLayout &VertexLayout::push<float>(unsigned int count, unsigned char normalized){
elements.emplace_back(GL_FLOAT, count, normalized, sizeof(float));
stride += count * sizeof(float);
return *this;
}
VertexLayout::Element::Element(
unsigned int type, unsigned int count,
unsigned char normalized, size_t typeSize
) : type(type), count(count), normalized(normalized), typeSize(typeSize) {}
const std::vector<VertexLayout::Element> &VertexLayout::getElements() const {
return elements;
}
unsigned int VertexLayout::getStride() const {
return stride;
}
VertexLayout::VertexLayout() : stride(0) {}
So an instance of VertexLayout should be created foreach VertexBuffer object and foreach opengl attribute should be called a push<type>(numberOfElementOfThatType).
The fourth and last class is the VertexArray class that represents a VAO: this last class keeps trace of all the VertexBuffer and IndexBuffer objects that are connected to the vao and sets the layout calling glVertexAttribPointer when a VertexBuffer is added using the following method:
void VertexArray::addBuffer(
const VertexBuffer &buffer, const VertexLayout &layout
) {
GLCall(glBindVertexArray(id));
buffer.bind();
const auto &elements = layout.getElements();
size_t offset = 0;
for (unsigned int i = 0; i < elements.size(); ++i) {
const auto &element = elements[i];
GLCall(glEnableVertexAttribArray(i));
GLCall(glVertexAttribPointer(
i, element.count, element.type, element.normalized,
layout.getStride(), (const void *)offset
));
offset += element.count * element.typeSize;
}
vertexBuffers.emplace_back(buffer);
}
GLCall is a macro that does nothing in release while in debug is clears the OpenGL erros and prints the new errors.
EDIT 2:
This is the class VertexBuffer that represents one VBO:
hpp
class VertexBuffer {
private: // static
static std::map<unsigned int, unsigned int> references;
private: // member
unsigned int rendererID;
public:
VertexBuffer();
VertexBuffer(
const void *data, unsigned long size,
unsigned int usage = GL_STATIC_DRAW
);
VertexBuffer(const VertexBuffer &oth);
VertexBuffer &operator=(const VertexBuffer &rhs);
~VertexBuffer();
void bind() const;
void unbind() const;
};
cpp:
std::map<unsigned int, unsigned int> VertexBuffer::references;
VertexBuffer::VertexBuffer(
const void *data,
unsigned long size,
unsigned int usage
) {
GLCall(glGenBuffers(1, &rendererID));
GLCall(glBindBuffer(GL_ARRAY_BUFFER, rendererID));
GLCall(glBufferData(GL_ARRAY_BUFFER, size, data, usage));
references.insert_or_assign(rendererID, 1);
}
VertexBuffer::VertexBuffer(const VertexBuffer &oth) {
if (oth.rendererID != 0){
auto ref = references.find(oth.rendererID);
assert(ref != references.end());
ref->second++;
}
rendererID = oth.rendererID;
}
VertexBuffer &VertexBuffer::operator=(const VertexBuffer &rhs) {
if (rendererID != 0) {
auto refs = references.find(rendererID);
assert(refs != references.end());
if (--refs->second == 0) {
GLCall(glDeleteBuffers(1, &rendererID));
references.erase(refs);
}
}
if (rhs.rendererID != 0){
auto ref = references.find(rhs.rendererID);
assert(ref != references.end());
ref->second++;
}
rendererID = rhs.rendererID;
return *this;
}
VertexBuffer::VertexBuffer() : rendererID(0) {}
VertexBuffer::~VertexBuffer() {
if (rendererID != 0) {
auto ref = references.find(rendererID);
assert(ref != references.end());
if (--ref->second == 0) {
GLCall(glDeleteBuffers(1, &rendererID));
references.erase(ref);
}
}
}
void VertexBuffer::bind() const {
GLCall(glBindBuffer(GL_ARRAY_BUFFER, rendererID));
}
void VertexBuffer::unbind() const {
GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0));
}
In the sphere I have only one big buffer that contains both positions and colors.
I found the solution. It was a very stupid error: The constructor of the VertexBuffer class needs the size of the buffer in bytes but when I called it I passed only the size of the std::vector that is the number of elements.
I'm doing a c++ app and I'm having some troubles while doing it and I can't fix the error.
I have a class ratinmaze that I'd like to use in the main class, but i get a lot of errors..
ratinmaze.h
#ifndef RATINMAZE_H
#define RATINMAZE_H
#include <iostream>
class ratinmaze
{
public:
ratinmaze();
int [5][5] solution; //error
virtual ~ratinmaze();
ratinmaze(int N);
void solveMaze(int [][5] maze, int N);
bool findPath(int [][5] maze, int x, int y, int N, std::string direction);
bool isSafeToGo(int [][5] maze, int x, int y, int N);
void print(int [][5] solution, int N);
void print(int [][5] solution, int N);
protected:
private:
std::string direction;
int N;
int y;
int x;
};
#endif // RATINMAZE_H
main.cpp
#include <iostream>
#include "ratinmaze.h"
using namespace std;
int main()
{
int N = 5;
int[5][5] maze = {{ 1, 0, 1, 1,1 },
{ 1, 1, 1, 0,1 },
{ 0, 0,0, 1, 1 },
{ 0, 0, 0, 1,0 },
{ 0, 0,0, 1, 1 }};
ratinmaze r = new ratinmaze(N);
r.solveMaze(maze, N);
return 0;
}
I get the error in the first line :
expected unqualifie-id before '[' token and in the following i get
"error : expected ',' or '...' before 'maze'
If u guys could help me I would be really appreciated!
#update
I figured out that I was not creating the right way my arrays but I have an error here on my main : ratinmaze r = new ratinmaze(N);
the error is :
invalid conversion from 'ratinmaze*' to 'int' [-fpermissive]
Any idea ?
Arrays in C++ are declared with the square brackets after the variable name, like so:
int solution[5][5];
Some time ago, Borland have introduced in their BCB evironment an extension to C++ language. This extension is a __closure keyword. The question is, if it is possible to implement such functionality in plain C++ or C++11? If you are not familiar with __closure keyword, below code provides explanation in comments.
Thanks in advance!
Toreno
#include <stdio.h>
// __closure keyword is used here !
typedef void (__closure * MemberCallback)(int x, int y, int z);
class A
{
private:
MemberCallback callback;
public:
A() : callback(NULL)
{
}
void setCallback(MemberCallback newCallback)
{
callback = newCallback;
}
void call(int x, int y, int z)
{
if(callback)
callback(x, y, z);
else
printf("NOT SET(%i, %i, %i)\n", x, y, z);
}
};
class B
{
public:
void func1(int x, int y, int z)
{
printf("FUNC 1(%i, %i, %i)\n", x, y, z);
}
void func2(int x, int y, int z)
{
printf("FUNC 2(%i, %i, %i)\n", x, y, z);
}
};
int main()
{
// A and B classes do not know about each other. There is no possibility
// to for inheritance because B class can implement multiple instances
// of callback function
A a;
B b;
a.call(1, 2, 3); // Prints: NOT SET(1, 2, 3)
a.setCallback(b.func1);
a.call(4, 5, 6); // Prints: FUNC 1(4, 5, 6)
a.setCallback(b.func2);
a.call(7, 8, 9); // Prints: FUNC 2(7, 8, 9)
return 0;
}
std::function is exactly what you're looking for. If you want to learn how such mechanism is actually implemented in the library, here's a good series of blog posts on it. Combine that with lambda functions for capturing of local variables.
Re-hash of the previous answer with the full code, for others like me that want a quick reference to a common pattern:
#include <functional>
#include <stdio.h>
// __closure replacement
typedef std::function<void(int x, int y, int z)> MemberCallback;
class A
{
public:
void setCallback( MemberCallback newCallback ) {
callback_ = newCallback;
}
void call( int x, int y, int z ) {
if ( callback_ )
callback_( x, y, z );
else
printf( "NOT SET(%i, %i, %i)\n", x, y, z );
}
private:
MemberCallback callback_;
};
class B
{
public:
void func1( int x, int y, int z ) {
printf( "FUNC 1(%i, %i, %i)\n", x, y, z );
}
void func2( int x, int y, int z ) {
printf( "FUNC 2(%i, %i, %i)\n", x, y, z );
}
};
int main( )
{
// A and B classes do not know about each other. There is no possibility
// to for inheritance because B class can implement multiple instances
// of callback function
A a;
B b;
a.call( 1, 2, 3 ); // Prints: NOT SET(1, 2, 3)
a.setCallback( [&b](int x, int y, int z){ b.func1(x, y, z); } );
a.call( 4, 5, 6 ); // Prints: FUNC 1(4, 5, 6)
a.setCallback( [&b](int x, int y, int z){ b.func2(x, y, z); } );
a.call( 7, 8, 9 ); // Prints: FUNC 2(7, 8, 9)
return 0;
}
Output:
NOT SET(1, 2, 3)
FUNC 1(4, 5, 6)
FUNC 2(7, 8, 9)
I cannot modify the member values of a class object because of the above error:
When I call my initialize function to initialize my "garo" object, I receive the following run-time error,
"Unhandled exception at 0x01323976 in Heretic.exe: 0xC0000005: Access violation reading location 0x00000008."
NOTE: The class Garo is a child of Object.
THE CODE
Garo.h
#pragma once
#include "Object.h"
class Garo : public Object
{
private:
int animationRow;
public:
Garo();
void Destroy();
void Init(ALLEGRO_BITMAP *image = NULL);
void Update();
void Render();
void MoveLeft();
void MoveRight();
void Idle();
void SetAnimationRow(int row);
};
Garo.cpp
#include "Garo.h"
Garo::Garo()
{
Object::Init(20, 200, 3, 0, 0, 0, 16, 24);
}
void Garo::Init(ALLEGRO_BITMAP *image)
{
Object::Init(20, 200, 3, 0, 0, 0, 16, 24);
SetID(PLAYER);
SetAlive(true);
maxFrame = 3;
curFrame = 0;
frameWidth = 32;
frameHeight = 48;
animationColumns = 4;
animationDirection = 1;
animationRow = 0;
if(image != NULL)
Garo::image = image;
}
... the rest has been abbreviated
Object.h
#pragma once
#include <iostream>
#include <allegro5/allegro5.h>
#include <allegro5/allegro_primitives.h>
#include "Globals.h"
class Object
{
private:
int ID;
bool alive;
bool collidable;
protected:
float x;
float y;
float velX;
float velY;
int dirX;
int dirY;
int boundX;
int boundY;
int maxFrame;
int curFrame;
int frameCount;
int frameDelay;
int frameWidth;
int frameHeight;
int animationColumns;
int animationDirection;
ALLEGRO_BITMAP *image;
public:
Object();
void virtual Destroy();
void Init(float x, float y, float velX, float velY, int dirX, int dirY, int boundX, int boundY);
void virtual Update();
void virtual Render();
float GetX() {return x;}
float GetY() {return y;}
void SetX(float x) {Object::x = x;}
void SetY(float y) {Object::y = y;}
int GetBoundX() {return boundX;}
int GetBoundY() {return boundY;}
int GetID() {return ID;}
void SetID(int ID) {Object::ID = ID;}
bool GetAlive() {return alive;}
void SetAlive(bool alive) {Object::alive = alive;}
bool GetCollidable() {return collidable;}
void SetCollidable(bool collidable) {Object::collidable = collidable;}
bool CheckCollisions(Object *otherObject);
void virtual Collided(int objectID);
bool Collidable();
};
Object.cpp
#include "Object.h"
Object::Object()
{
x = 0;
y = 0;
velX = 0;
velY = 0;
dirX = 0;
dirY = 0;
boundX = 0;
boundY = 0;
maxFrame = 0;
curFrame = 0;
frameCount = 0;
frameDelay = 0;
frameWidth = 0;
frameHeight = 0;
animationColumns = 0;
animationDirection = 0;
image = NULL;
alive = true;
collidable = true;
}
void Object::Init(float x, float y, float velX, float velY, int dirX, int dirY, int boundX, int boundY)
{
std::cout << "HERE?" << std::endl;
Object::x = x;
Object::y = y;
Object::velX = velX;
Object::velY = velY;
Object::dirX = dirX;
Object::dirY = dirY;
Object::boundX = boundX;
Object::boundY = boundY;
}
The Calling Code
Garo *garo;
// ...
garo->Init(garo_img);
This is where I receive the run-time error. I'm using Allegro libraries, so feel free to ask about any weird types you may see. I am stilling learning C++, so please help me to understand in rudimentary terms.
You need to instantiate an object instance to operate on. For example:
garo = new Garo();
By missing this out you are trying to invoke methods on an uninitialized variable. You should probably consider using some form of smart pointer to ensure that the object is destroyed.