Is it possible to encapsulate this?

Started by Phemar, Sun 01/07/2018 12:43:21

Previous topic - Next topic

Phemar

I have a couple of structs that relate to the same object, I was wondering if there was a way to encapsulate them into a single object that I can create instances of.

My project is a simple 3D renderer in AGS Script (just for fun) and I'd really like to be able to create several instances of one object.

Right now my code is hard coded to accept only one mesh. I have it set up like this at the moment:

Code: ags
struct Vertex
{
  float world[3];
  float camera[3];
  float screen[3];
  int raster[2];
};

struct Triangle
{
  int vertex[3];
};


The arrays correspond to x, y and z coordinates.

I'd like to have it all encapsulated into one object, something like this:

Code: ags
struct Box
{
  Vertex vertex[8];
  Triangle triangle[12];
};


It would be even better if I could have a Vec3 struct, so I can access the box like

Code: ags
Box * box1;
box1.vertex[4].world.x = 1.4;
box1.triangle[0].vertex[1] = 3;


Is this possible? Sorry if this seems silly, I'm not really a programmer, just a hobbyist.

dayowlron

I believe it should be possible, however I do see one problem. you reference ....world.x but world is an array so it would be world[0] unless you declare world to be another type other than float world[3]
you could create another struct called coordinate so you can reference it with .x, .y, and .z, but then you can't reference it with indexes.
Code: ags

struct Coordinate
{
    float x;
    float y;
    float z;
}
struct Vertex
{
  Coordinate world[3];
  Coordinate camera[3];
  Coordinate screen[3];
  int raster[2];
};
Pro is the opposite of Con                       Kids of today are so much different
This fact can clearly be seen,                  Don't you know?
If progress means to move forward         Just ask them where they are from
Then what does congress mean?             And they tell you where you can go.  --Nipsey Russell

Phemar

#2
Dayowlron, I know, that's why I said it would be nice to have another Vec3 struct, and include that in the other attributes.

Code: ags
Struct Vec3 {
float x; y; z;
};


Currently I'm using an array with an enum (world[eX], world[eY] etc. instead of world.x, world.y), because AGS won't let me include structs within structs.

Crimson Wizard

#3
The limitations of AGS Script currently are that you cannot include structs into structs, but you may include pointers to non-managed structs, e.g.:
Code: ags

managed struct Vec3
{
   float x, y, z;
}

struct Triangle
{
    Vec3 *vertex[3];
}


Other method, people seem to use, is to have large arrays for storing objects, and connect one object with other using array indexes.
EDIT: sorry, scrap this, I did not realize at first that you use that now.

Anyway, there seem to be no other way. You could mix these two methods to some degree.

Snarky

#4
If the intermediate classes are just for convenience, you can also do something like this:

Code: ags
// Header

managed struct Point3D
{
  import static Point3D* Create(float x, float y, float z);

  float x;
  float y;
  float z;
};

managed struct Triangle3D
{
  protected float points[9];

  import attribute Point3D* Point1;
  import attribute Point3D* Point2;
  import attribute Point3D* Point3;
};

// Script

static Point3D* Point3D::Create(float x, float y, float z)
{
  Point3D* p = new Point3D;
  p.x = x;
  p.y = y;
  p.z = z;
  return p;
}

Point3D* get_Point1(this Triangle3D*)
{
  return Point3D.Create(this.points[0], this.points[1], this.points[2]);
}

void set_Point1(this Triangle3D*, Point3D* p)
{
  // TODO: Deal with null
  this.points[0] = p.x;
  this.points[1] = p.y;
  this.points[2] = p.z;
}

// TODO: Same for Point2 = points[3-5], and Point3 = points[6-8]


You use attributes to make it seem like you have a struct as a property, but internally you just store the raw data and encode/decode it on set/get.

It does mean you break the reference link (if you set a triangle to contain a certain point, and then you update the point, the triangle is not going to update), so it's not suitable for every situation.

Edit: Code fix

Phemar

#5
Thanks for the replies guys. Appreciate the long post, Snarky, however I'm not entirely sure what your code does or how it works. I tried to compile it and got an error at
Code: ags
Point3D* Point3D::Create(float x, float y, float z)


"TESTING_HELL.asc(25): Error (line 25): Attributes of identifier do not match prototype"

Anyway, I tried your method Crimson, however I'm getting a "Null Pointer Error" whenever I try and set values. Am I doing something wrong?

Code: ags
//Header

managed struct Vec3f
{
  float x;
  float y;
  float z;
};

struct Tester
{
  Vec3f * vertex[3];
};


Code: ags

Tester aTest;
Tester anotherTest;

void TestingShit ()
{
  aTest.vertex[0].x = 4.0;
  anotherTest.vertex[0].x = 5.0;
}

function on_key_press (int keycode)
{
  if (keycode == eKeyM)
  {
    TestingShit ();
    Display ("aTest %f[anotherTest %f", aTest.vertex[0].x, anotherTest.vertex[0].x);
  }
}


Edit: Also, Crimson, what do you mean by this: "EDIT: sorry, scrap this, I did not realize at first that you use that now." Realize that I use what now?

Snarky

#6
Yeah, I forgot to put the "static" keyword in the function definition.

I think what CW realized you were doing is store array indexes in the struct, and then look up the other struct in a global array using the index. Which is the workaround you have to do when you can't have pointers in the struct (the index is essentially a kind of pseudo-pointer).

Phemar, check out the threads that discuss the new "managed struct" datatype. The key thing is that when you create one, you have to put "new" whatever it is. The Vec3f array in Tester is an array of pointers: they haven't actually been created until you set them with "new" somewhere. (It's like you store an array index, but the array doesn't actually exist or have anything in it.)


Phemar

#7
Awesome, I'm beginning to understand. But if I create a pointer within the struct (like I'm doing at the moment), where do I initialize the managed struct (Vec3f)? I can't do it in the header (which makes sense), but I can't think of where else to put it.

I tried this:
Code: ags
struct Tester
{
  import attribute Vec3f * vertex1;
};


However that just crashes the game upon launch. Compiles fine though. (P.S. What does 'attribute' do? Can't find it in the manual).

Again, thanks for the help. And sorry if I sound absolutely noobish.

Edit: Ignore this, I figured it out! Adding:

Code: ags
void Initialize ()
{
  aTest.vertex[0] = new Vec3f;
  anotherTest.vertex[0] = new Vec3f;
}


fixes it all.

Thanks again!

Crimson Wizard

Quote from: Phemar on Sun 01/07/2018 16:34:52
Awesome, I'm beginning to understand. But if I create a pointer within the struct (like I'm doing at the moment), where do I initialize the managed struct (Vec3f)?

You initialize it where you need, for example when the parent object is initialized.
Code: ags

Tester testers[100];

void CreateTester(int index, float x, float y, float z)
{
   Vec3 *v3 = new Vec3;
   v3.x = x;
   v3.y = y;
   v3.z = z;
   testers[index].vertex1 = v3;
   // other variables
}


You may move the Vec3 initialization into separate constructor-like function, like in the example I gave in the other topic:
http://www.adventuregamestudio.co.uk/forums/index.php?topic=50178.msg636588783#msg636588783


Quote from: Phemar on Sun 01/07/2018 16:34:52However that just crashes the game upon launch. Compiles fine though. (P.S. What does 'attribute' do? Can't find it in the manual).

Attribute is not a real variable (data field) but a convenience syntax for two functions that set or get certain value. If you declare attribute you also must implement functions called get_AttributeName and set_AttributeName.
http://www.adventuregamestudio.co.uk/wiki/Keyword:_attribute

jhonberg

#9
Is there any way this could work for Strings ??  in my case i'm getting this error

Failed to save room room1.crm; details below
room1.asc(4): Error (line 4): Member variable of managed struct cannot be pointer
Code: ags

managed struct ScreenText{
 String text;
 FontType fontType;
};

ScreenText* createScreenText(String text_,FontType fontType_ ){
  
  ScreenText* screenText= new ScreenText();
  screenText.text=text_;
  screenText.fontType=fontType_;
    
  }


Seems that this only works for primitives?   :confused:

Snarky

The problem is that you can't store pointers in a managed struct (for reasons that have to do with AGS not having a garbage collector), and String is a pointer type.

A workaround (as already discussed above in the thread) would be to store the String in an array, and store the array index in the ScreeText (you planning on fixing that typo?). You could then use an attribute to look up the String in the array and make it seem like it's part of the struct.

Crimson Wizard

#11
Quote from: Snarky on Sat 04/08/2018 11:58:43
The problem is that you can't store pointers in a managed struct (for reasons that have to do with AGS not having a garbage collector)

AGS has garbage collector, otherwise managed structs would not work at all. What it does not have is reflection and/or runtime type information. In simplier words it cannot distinct the types of variables inside the struct. With regular structs there is still information telling whether the variable is managed or not which is saved directly as a part of script commands, but with managed structs it does not know even that. Hence, when it deletes the managed struct from memory it won't be able to detect pointers there and would not decrease their use counters, which would lead to them staying in memory forever (memory leak).

Potential solutions do exist, we were discussing them in the past, but no one have tried to actually implement them.

Snarky

Interesting, thanks for the clarification!

jhonberg

Quote from: Snarky on Sat 04/08/2018 11:58:43
The problem is that you can't store pointers in a managed struct (for reasons that have to do with AGS not having a garbage collector), and String is a pointer type.

A workaround (as already discussed above in the thread) would be to store the String in an array, and store the array index in the ScreeText (you planning on fixing that typo?). You could then use an attribute to look up the String in the array and make it seem like it's part of the struct.

Fixed the typo :D

jhonberg

Since i can't pass the struct as parameter i just decomposed the object primitives, i don't feel confortable with this workaround but it serves my initial purpose, which was organize the data and make it easy to manipulate.

 

Code: ags

//header
struct ScreenText{
 String text;
 FontType fontType;
 import function init(String text_,FontType fontType_);
};

//script
Overlay* roomText[];

function displayAnimatedText(String fullText,FontType fontType,int row, int maxRows){
   String message=""; 
   for(int i=0;i<fullText.Length;i++){
     message=message.AppendChar(fullText.Chars[i]);
     roomText[row]=ShowTextinRowLeft(message,53665, row+1, maxRows,fontType);
     Wait(2);
   }
   Wait(80);
}

function room_AfterFadeIn()
{
 int maxRows=10;
 ScreenText textRows[10]; 
 roomText= new Overlay[maxRows];
 textRows[0].init("Earth year 1947",eFontNormal);
 textRows[1].init("70.000 years after the last colony left",eFontSpeech);
 textRows[2].init("A strong high energy pulse was picked up by a consulate",eFontSpeech);
 textRows[3].init("cruiser 2 years earlier, origing is planet Earth.",eFontSpeech);
 textRows[4].init("A scout mission is sent to investigate it's origing,",eFontSpeech);
 textRows[5].init("no intelligent life is expected to be found.",eFontSpeech);
 textRows[6].init("   ",eFontNormal);
 textRows[7].init("Mission:",eFontNormal);
 textRows[8].init("Identify energy source and evalute  explore the abbandoned ",eFontSpeech);
 textRows[9].init("settlement for lost artifacts left behind on the exodus.",eFontSpeech);
 
  
   for(int i=0;i<maxRows;i++){
      displayAnimatedText(textRows[i].text,textRows[i].fontType,i, maxRows);
   }

}

Phemar

#15
Code: ags
 textRows[0].init("Earth year: 1947",eFontNormal);
 textRows[1].init("70 000 years after the last colony left,",eFontSpeech); //you can use a space or a comma here, but I wouldn't recommend using a period. It's a stylistic choice at the end of the day though
 textRows[2].init("a strong energy pulse was picked up by a consulate",eFontSpeech);
 textRows[3].init("cruiser 2 years earlier. Origin is planet Earth.",eFontSpeech); //changed origing to origin
 textRows[4].init("A scout mission has been sent to investigate,",eFontSpeech);
 textRows[5].init("no intelligent life is expected to be found.",eFontSpeech);
 textRows[6].init("   ",eFontNormal);
 textRows[7].init("Mission:",eFontNormal);
 textRows[8].init("Identify the energy source and explore the abandoned ",eFontSpeech); //removed 'evaluate' and changed 'abbandoned' to 'abandoned'
 textRows[9].init("settlement for lost artifacts left behind on the Exodus.",eFontSpeech); //capitalized 'Exodus' (proper noun)


Did some quick proof reading for you. Hope you don't mind :D

SMF spam blocked by CleanTalk