Jump to content

  • Log In with Google      Sign In   
  • Create Account


Member Since 12 Mar 2005
Offline Last Active Today, 12:17 AM

#5263512 execute constructor of derived class

Posted by frob on Today, 12:17 AM

TObjectPropertyEditor something;

In that case, get and study a GOOD book about the language.

Based on your comments over the past few months, I recommend current editions of either of these:

# "C++ Primer" by Lippman, Lajoie, and Moo. (NOT "C++ Primer Plus")
# "Accelerated C++" by Andrew Koenig and Barabara Moo

Don't bother with both since they mostly overlap. The first one has a gentler learning curve than the second.

#5263436 execute constructor of derived class

Posted by frob on Yesterday, 12:06 PM

TObjectPropertyEditor() //TForm constructor will be executed right?





~TObjectPropertyEditor() {} //TForm desctructor will be executed right?


Yes with a caveat.


If the function is not marked as virtual, it is possible for the TForm destructor to be called but the TObjectPropertyEditor not get called.


So that works only if the TForm destructor is virtual, which it should be.  Virtual gets inherited, so if TForm derived from something and it was marked virtual, that would also make it happen.


If it is not virtual, than this won't work:

base* b;
b=new derived;
delete b;

In that case if the destructor is not virtual than the derived destructor won't be called, only the base constructor is called.

If the base class destructor is virtual then both the base destructor and derived destructor will be called.



Or more specific to your example:

TForm* form;
delete form;

In that case if the destructor is not virtual only the TForm destructor would be called, which is a bug.


If the destructor is virtual then when the object is deleted the TObjectPropertyEditor destructor gets called, then TForm destructor gets called, which is what you want.


So make sure the base class destructor is virtual, otherwise there can be serious problems.

#5263418 execute constructor of derived class

Posted by frob on Yesterday, 11:00 AM

Your code and your words don't quite match up.



TObjectPropertyEditor is derived from TForm.  That means TForm is the parent class, and TObjectPropertyEditor is the derived class.


When the objects are created, the TForm constructor is executed and run completely before TObjectPropertyEditor constructor is run.  Base class constructors are called first, in the order they are listed in the class definition, then the constructors of sub objects are called in order, then the current class constructor is called.



Your constructor initializer lists should be in the same order the members appear.  If you have five or ten members getting initialized, put the base constructor first and the member variables in order, the same order they appear top to bottom in the class file.


If you need to pass parameters or initialize variables, you can do it using a constructor initializer list, something like this:

struct TObjectPropertyEditor : public TForm
TEntity * apply_to;
TObjectPropertyEditor() : TForm(), apply_to(NULL)

//Adding this one as an example
//If you want to pass a parameter to a base class, you can do it like this
TObjectPropertyEditor(TController *controller) : TForm(controller), apply_to(NULL)

// Double check that your base class destructor is virtual and public.
~TObjectPropertyEditor() {}

When you create a TObjectPropertyEditor it will first call TForm's constructor, and after the TForm is created the above code will initialize apply_to with the value NULL, then it will run the TObjectPropertyEditor constructor body.


In the second constructor I wrote as an example, it will pass the parameter on to the TForm constructor and run the constructor, then initialize the variable to NULL, then run the constructor body.






Base class destructors -- which should usually be virtual and public, are automatically called.   A class that is free standing can have a regular function for a destructor, but base classes have some special rules. Nearly always, and always when you intend to use polymorphic behavior for the class, a base class destructor needs to be public and virtual.  


These destructors are an exception to the normal rule because they automatically call their base class variant, you don't need to call them.


Without that special rule you would need to write:

~TObjectPropertyEditor() {}
 // My own destruction

 TForm::~TForm();   // DON'T DO THIS, Don't chain virtual destructors, the compiler does it for you.

In any other case if you need to call the behavior of a virtual class you would need to chain it manually.  Destructors are a special case, just make sure the base class destructor is virtual and they will all get destroyed in the correct order, ~TObjectPropertyEditor gets called first, then ~TForm is called next.

#5263364 Getting Started With Game Development?

Posted by frob on 23 November 2015 - 08:53 PM

Moving to For Beginners.


You question is listed right at the top of the For Beginners Faq, where it answers the question "where do I start?" with several options.

#5263330 Communicating with Modelers

Posted by frob on 23 November 2015 - 03:47 PM

This type of communication often falls on the shoulders of the designer.  


Designers need to document and explain the design to all the groups; the programmers, modelers, animators, audio, effects, and QA groups all need to understand what the thing is supposed to do.


Part of that understanding includes knowing why a thing needs to be that way.  In this case, perhaps a single line would have prevented the problem:  Doorway needs to be at least 4 units wide to accommodate pathfinding.  


For doorways on other projects we've tended to ignore pathfinding and use a portal system. Doorway elements (bridges, doors, teleporters) include marked locations (like a named joint) to allow animations to hook up cleanly. The object needs to walk to the marked location with the correct orientation, play the animation set associated with the object's motion, then resume from the exit location.  This allows for things like gates that need to be opened and closed, one-way portals, and other object properties.

#5263327 Depth buffer really confuse

Posted by frob on 23 November 2015 - 03:33 PM

Are you sure this is still the truth?  With all modern GPU having Hi-Z, I know at least ATI(AMD) has fast Z clear IIRC using Hi-Z buffer.  I would think drawing the skybox last will potentially save alot of fill depending on how much of the sky is visible.

Ah, the joys of constantly-changing graphics hardware.


Looks like yet another change, where an older best practice is replaced.  


I'm glad I'm not a graphics-centric engineer because it seems every few years it flips itself on its head; the old best practices are discouraged, the old practices to avoid become recommendations.


From some of that reading you linked, if your graphics card has a compressed z-buffer, use a clear operation since it will drop the compressed blocks.  if you are using an older card without compressed Z-buffer, overwrite rather than clear since a clear resets the value to a specific depth and then you'll immediately overwrite it with new depth data.


And if you're targeting cards in between, implement both.


If you implement only one or the other, you're doing it wrong on the opposite era's cards.  blink.png

#5263306 Survival Mmo

Posted by frob on 23 November 2015 - 01:14 PM

The "massively multiplayer" part of MMO was created to show the difference between regular online games.  


Back in the late '90s when it was coined, Massively Multiplayer meant servers with over 10,000 concurrent users. The normal notation is "C" for "concurrent" followed by the number, so in this case C10K.  


Today the number for MMO is usually much larger, C100K or so at a minimum.


Building an online game with a small number of concurrent players, maybe 4 or 8 or even 64 (C4, C8, C64) is relatively easy.  They can usually connect directly to each other, or go through a game hub you set up and control.  You can do this in your basement or through a cheap hosting account with pocket money for budget.


Controlling a larger online game (still not MMO) with many concurrent players, say C1K or C2K, that requires an investment of dedicated hardware and probably multiple data centers. This generally requires a multi-million dollar budget and commitment from a developer.  


Controlling a massive number of concurrent players, C10K or C100K, you are looking at price tags in the hundred million dollar range. 



Unless you happen to be a billionaire hiring a very large team for their dream game, building an MMO is not feasible for an individual.

#5263299 Put game data to files or using database tool

Posted by frob on 23 November 2015 - 12:56 PM

Usually parsing and loading files is a slow process.  Many file formats require decompression to be used. Other file formats like XML are memory-expensive and compute-expensive to parse and validate. 



In the AAA world, there are frequently multiple formats accepted:


First, there is a final packed format. These are designed to be loaded directly into memory without any processing.  Dump it in a big block of memory, fix up some pointers to point inside the memory, and use it immediately.


Second, there is support for development formats. These are the intermediate files, perhaps raw images or textures or model Collada files or wav files. Better engines will monitor the directory tree and swap out the files while the game is running when the files are changed.  These are not preferred because they take more time and effort to parse, but they save significant dev time by allowing artists, modelers, animators, designers, and audio to iterate quickly. They can make a change in their tools, save the file, watch it change in game, make another change, save, and iterate rapidly.


Third, there may be support for modding tools or development tool attachments. These may load and process their own file formats or build data dynamically on the fly.  




The exact details of your file formats are up to you. Use what works for your process.

#5263297 Depth buffer really confuse

Posted by frob on 23 November 2015 - 12:48 PM

Just re-iterating and re-wording:



The depth buffer usually works as a shortcut to see if something should be drawn or discarded. It has several options:  

* NEVER (don't draw)

* ALWAYS (always draw)

* EQUAL (draw if the values are exactly equal)

* NOTEQUAL (draw if the values are different and store this)

* LESS (draw if the value is smaller than what exists, and store this new smaller value)

* LEQUAL (draw if the value is smaller or equal to what exists, and store the new value)

* GREATER (you get it...)

* GEQUAL (Greater or equal)


Generally games will not explicitly clear the depth buffer.  Instead they set the flag to ALWAYS and draw their skybox or draw a distant plane, thus obliterating whatever was stored and setting a new maximum depth.


The depth buffer can be used for other tasks as well as depth.  You can use depth images, or pre-computed depth fields, to mix and match 3D worlds with pre-rendered 2D images by drawing a pre-rendered image and telling the depth buffer what pre-computed depths they represent. You can use depth buffers to help compute fog or atmospheric scattering or depth-of-field distortions.




As for how the depth number is determined, there are many methods.  You can change the values that get used inside your graphics shaders or compute shaders.


Because of the magic of floating point, smaller numbers are more precise than bigger numbers. The sliding scale of the decimal point (hence "floating" point) means the tiniest numbers have the greatest precision. Each time it floats bigger, the precision drops by half.  Floating point precision operates at logarithmic scales.


That means that the nearest items usually have high precision, but distant items can suffer from "z-fighting", where planes that are touching each other will shimmer and shift between the objects as the camera and models moves. 


To counter the sliding precision, people have come up with many different ways to compute the depth values.  


You can use the simple calculation of (object depth / view frustum depth). That is normally the default that happens if you didn't provide a custom value.  Some programs will store the inverse of that distance, termed a 'w-buffer". Some programs will use a logarithmic z value, basically reversing the logarithmic nature of floating point turning it back to a roughly linear scale. Some programs will use different algorithms that suit them better.


The value that you store and compare against is up to you. 

#5262931 MyClass myClass VS myClass = new MyClass()

Posted by frob on 20 November 2015 - 04:05 PM

What is the general consensus on creating objects on the stack and then having to call an init function?

In large games it is critical to manage object lifetimes.


You (and your design) need to understand what it means to be in given states.



In particular, a bit of history is important here.  I've written about it a few times and an article is probably in order, but I'll go over it again.



Many times you will read articles talking about initialization in multiple steps.  It is important to understand what they mean by that.  Some things need to happen in multiple steps, other times there are multiple steps that happen behind your back that once were required but now happen automatically.



Back many years ago, "creating an object" was a two step process.  The first step was to grab a chunk of memory.  You had no knowledge of what was in the chunk of memory, it was just a data block.  The second step was to initialize the chunk of memory to a known value.  The known value may have been zero, and there are functions like bzero() that set a block to zero, or you could use memset() to force it to specific values, or you could make an object with default known values and use memcpy() to assign those values.


When books and other resources tell you to initialize your objects when you create them, that is what they are talking about.  Allocating a chunk of memory and then immediately assigning it a known value.


These days, "creating an object" is normally a single step process.  In C++/Java/C#/etc when you create an instance it also calls a constructor and values assume their default state.  It is generally good practice to assign default initial values, but sometimes it can make sense for some data elements to be ignored.  For example, if you have a buffer and an integer that says the number of elements in the buffer, if you initialize the number to zero you don't need to wipe out the contents of the buffer to any known value since they will be overwritten when an item is added.


Some languages will strictly enforce this rule.  Java and C# by default will complain if you attempt to use a value when the compiler knows you have not set the value. C and C++ have enough historical code and enough corner-cases requiring less visible initialization that the languages do not generally complain about the error.  They will let you blissfully use an unintialized variable, leaving you unaware of your bug.




Typically you want your default constructor to be instant, or as close to instant as possible.  There are many reasons objects get created.  You might create an array of objects, you might create temporary objects, you don't want to wait around for those objects to be created.  if your default constructor needs to do a bunch of work, like allocating large chunks of memory, or even worse jumping out to the disk or across a network, you can be waiting for an extended time for those temporary objects to be created.


You might want to create additional constructors that do additional work. That additional work can take more time and do more work. The extra effort is acceptable in many cases because the programmer asked for that extra work.  These are not temporaries, but actual immediate items.


Let's look at some examples from the C++ standard library.


First, an input file stream fstream.  The default constructor, ifstream(), does the minimal amount of work.  It constructs an empty stream that is not associated with a file. This takes almost no work and returns instantly.  You can create an array of ten thousand file streams created with the default constructor and it will still be nearly instant.  There are additional constructors available. A constructor taking a file name, ifstream(filename) will start by building an empty object, then attempt to open a file.  The process of reaching out to the disk can take an extended time, maybe disk spindles need to start spinning, maybe those disks are located across a network or even on the other side of the world. The disk may even be associated with an advanced tape system where it takes minutes for a machine to rewind a tape, eject it, locate a new tape, install it to the tape reader, find the file on the tape, and then report that it is open.  Attempting to create a large number of files with this parameterized constructor can potentially take an enormous amount of time.


Second, a data container vector.  The default constructor, vector(), does almost no work.  It may set two or three data members to zero and then return instantly.  You can create an array of vectors, that is also nearly instant. There are additional constructors available.  A constructor taking an existing data buffer will create copies of every item to be created.  If you are creating a copy of other objects that are expensive to create, that constructor can take an extended amount of time.




So getting back to your question:


MyClass myClass;


This can make sense in many situations.  The first line, MyClass myClass; runs the default constructor.  It does no work other than setting the object to a minimally empty state. The second line, myClass.Init(); does additional potentially expensive processing.


When it comes to game object lifetimes, often there is a pattern:


1. Create empty.

2. Load, which figures out what goes in the object and creates a proxy placeholder without doing expensive loading of models and textures and such.

3. Place in world, which usually incurs a cost as the object is hooked into various systems.

4. Stream into world, which replaces the proxy placeholder with exact data.

5. Stream out of world, which frees space and replaces it back with a proxy.

6. Remove from world, which removes the object from various systems.

7. Unload, which places the object back in the free pool

8. Destroy.


There may be more or fewer steps in your specific engine, but that is enough to communicate the idea.


Now objects can move through the system in many ways.  


They may have a lifetime where the level gets loaded but the object is never seen because it is far away from the player.  Perhaps an object lifetime of create, load, place in world, remove, place, remove, place, remove, unload, destroy.


They may have a lifetime where they are loaded and right at the edge of the player's simulation:  create, load, place in world, stream in, stream out, stream in, stream out, stream in, stream out ...


They may have a lifetime as a temporary object: create empty, never get used, destroy.


It is important to understand how object lifetimes work in games. 

#5262926 Getting to grips with data oriented component based design

Posted by frob on 20 November 2015 - 03:36 PM

If that is sensible or not depends on your goals and needs.



In some ways, depending on your data access patterns, having them sequential as a structure of arrays can make sense.  Probably not so much sense in what you described, but with some patterns it can make sense. If you are stepping across many components at once or examining clusters of components where you can look at multiple sets of data simultaneously, then the pattern of keeping data in separate containers can make sense. In other ways it looks like a worse decision.  You have indeces (4 bytes) to point to integers (also 4 bytes).  You could have stored the results directly without issue and in less space; it is scattered across more items but it may improve performance because the data is clustered together if you are stepping one component instance at a time.



If this is the first time you've ever implemented anything like this, I would just store the data directly in the object instances and not in separate data arrays.  

#5262919 WPF Jerky Initial Draw

Posted by frob on 20 November 2015 - 02:43 PM

Oh, also moving to For Beginners, I don't think this really fits in the current area.

#5262796 Software Tool Selection Process - Improvements please

Posted by frob on 19 November 2015 - 04:50 PM

I don't think they have a consolidated published list of technologies they use, just announcements from time to time.  



PhysX 3 came with Unity 5 and it was a big announcement. Same with Enlighten.


Umbra came with Unity 4.3. (That was right in the middle of revising my book on the engine, it was one of the choices about if we update a section to hit an ever-moving target.  Not a fun challenge to keep up with the Unity team.)


The reliance on Mono has been there basically from the beginning.


For the others, Google can help you search for entries on the Unity blog.  Heaven help you if you try to use their own search tools for it, just target the site blogs.unity3D.com. 

#5262792 No idea about this warning in VS2015

Posted by frob on 19 November 2015 - 04:28 PM

I already have latest SDK. Just installed it today (Using WIndows SDK installer).

Then your system did not install it correctly, or your project is misconfigured to use a wrong version for something, or you have some other mismatch.



The error message is that your app is linked against an older edition of the Microsoft libraries.  


Put differently, it expects version X but your system doesn't have X, it has has Y. 



The question is, are you intending to hit X or Y?  Are you intending to use version 14.0.22929.0 or are you intending to use version 14.0.23019.0?


If you want to build against 23019 then your app's configuration needs to be updated, it is pointing at the old version.  


If you want to build against 22929 then your system is missing the library, go download that library, uninstall what you currently have if you think it was alerady installed and install it again.

#5262782 obj format question

Posted by frob on 19 November 2015 - 03:27 PM



So for the process of merging meshes, that can be done to effectively make them all a single model.  As Spinningcubes pointed out, you'll need to change the indexes.


The file format is all about the geometry of a single object. The geometric object has a bunch of vertex data, texture coordinates, vertex normals, and other geometry data, then has details to build faces and triangle strips and other shapes based on the index numbers within the vertex data, all of it handled by index. 


When you add the geometric vertex data to the file it does not restart its count at vertex 1. Instead the addition starts at the end of the previous collection.  So if the first object used vertex 1-427, the object you are adding will begin at vertex 428.  You will need to adjust all your indeces for faces, textures, normals, splines, and whatever else so it uses the updated index numbers.  Instead of a face that included vertex 1, 2, and 3, it would need to be updated to vertex 428, 429, and 430.