Jump to content

  • Log In with Google      Sign In   
  • Create Account

Nanoha

Member Since 26 Feb 2009
Offline Last Active Today, 06:02 AM

#5302143 I Can't Properly Rotate The Enemy To Look At Me All The Time.

Posted by Nanoha on Today, 05:15 AM

What do you mean - inefficient?


void Enemy::moveTowardsPlayer( glm::vec3 playerPos )
{
    crossVec = glm::cross( faceDirection, upDirection ); //6 multiplications, 3 subtractions
    movementDirection = normalize( playerPos - position ); //3 multiplications, 3 additions, 3 subtractions, 3 divisions, 1 square root
    float dotProduct = dot( movementDirection, faceDirection ); // 3 multiplications, 3 additions
    float crossVecDotProduct = dot( movementDirection, crossVec ); // 3 multiplications, 3 additions
 
    if( crossVecDotProduct < 0 ) // If I turned left
    {
        rotation.w = std::acos( dotProduct ); // trig functions (these can be slow)
    }
    else //else I turned right
    {
        rotation.w = std::acos( -dotProduct ) + M_PI;  // trig func, addition
    }
 
    rotation.y = 1.0f;
}

15 multiplications, 8(9) additions, 6 subtractions, 3 divisions, 1 square root, 1 trig function
 

movementDirection = playerPos - position; // 3 subtractions
// going to ignore elevation
movementDirection .y = 0;
movementDirection = normalize( movementDirection ); // 3 multiplications, 3 additions, 3 divisions, 1 square root
rotation.x = 0;
rotation.y = 1;
rotation.z = 0;
rotation.w = atan2(movementDirection.z, movementDirection.x); // trig func

3 multiplications, 3 additions, 3 subtractions, 3 divisions, 1 square root, 1 trig function.

Obviously I may have missed something and a few odd bits I didn't count. I don't know how glm is calculating it's dot prodcts or cross products but there's a limit to how efficient it can be made. I don't know how atan2/acos are compared to each other but the second option appears to do less work. It depends largely on how slow the trig functions are, if there's a huge difference then that may be the deciding factor. Of course it's likely that you don't have any performance issues so it's no problem anyway. Your solution certainly does what it's intended to and it's easy to see why you came up with that (it makes sense).




#5301986 I Can't Properly Rotate The Enemy To Look At Me All The Time.

Posted by Nanoha on Yesterday, 11:04 AM

I personally find angles really horrible to work with and store orientations as. This would be a lot easier if you stored orientations as a matrix or a quaternion. Also quaternions would give you SLERP which is great for doing an animated rotation towards your target. What you want is something along the lines to atan2 which is great because your enemy faces x by default.

movementDirection = playerPos - position;
// going to ignore elevation
movementDirection .y = 0;
movementDirection = normalize( movementDirection );
rotation.x = 0;
rotation.y = 1;
rotation.z = 0;
rotation.w = atan2(movementDirection.z, movementDirection.x); // this won't work if the enemy doesn't face x by default
position += movementDirection/50.0f;

Ideally you should set things by angles but it's easier to store and manipulate things by avoiding angles. Creating this kinda thing here doesn't even require any angles, it just needs a forward vector (movementDirection), an up vector (0, 1, 0) and a cross product. That will give you enough to build an orthogonal basis which you'd use as your orientation matrix. Those vectors would just be rows in the matrix.

 

I remember using angles a lot in the early days and I have to say it is so much easier without them. Of course at times you do need them but you should drop them as soon as possible (accept angles as initialisation because they are user friendly but internally work with quaternions or matrices). Also same goes for degrees, drop degrees as soon as you can and use radians. Use them if you are exposing an angle to a user/designer because degrees are more intuitive but drop it as soon as possible and store it as radians. Convert from degrees as soon as possible and to degrees as late as possible.




#5301812 User Interaction System Ideas

Posted by Nanoha on 21 July 2016 - 01:25 PM

So you are checking every single object in turn rather than say responding to an event? I don't have a specific method in mind but you could do this as an even rather than a per update. User clicks (nothing in particular) then you do a quick search for items in range, items in front of user, check if user can interact with the item. That should give you a very small list (if any) objects. Then you can raycast to check if the user is pointing at the object and finally do the action.

 

The only difficult part that might get convoluted would be the part that checks if the user can interact (the part, do they have an axe) depending on how varied your system is. I would definitely go for this approach rather than the dig the player click tree1, did the player click tree2, did the player click tree3 etc. You could have each object having a method that takes a player object: bool CanInteract(Player). The object itself can then check for any required tools and you could also use scripting here for anything more complicated.




#5301718 How To Use Dx Ortho Projection Matrix

Posted by Nanoha on 21 July 2016 - 05:02 AM

TLDR: use XMMatrixOrthographicOffCenterLH if you don't need to move a camera as this is simpler and it's one less matrix multiplication to do (since you don't need a view matrix) Otherwise you will have to use XMMatrixOrthographicLH and also provide your own view matrix.

I was going to post something yesterday but I wasn't 100% sure I was correct but seen as you haven't had many responses I'll post anyway but bare in mind I might be slightly off.

Both are creating an orthogonal projection but one is more flexible than the other. XMMatrixOrthographicOffCenterLH essential also contain your 'view' position, it will move your camera to the centre of the area you define. For example, if you set the left to 0 and the right to 800 then it should (I believe..) make it such that the left is 0, the right is 800 (unsurprising) and your camera is at 400. You don't really need a 'view' transform with it. That is probably the better choice for you if you have a static 2D game/window without scrolling.

XMMatrixOrthographicLH almost does the exact same thing but it doesn't contain a 'view' (well it kinda does with the z axis). If you set width to be 800 then the left of your screen will be -400 and the right of your screen will be 400. It will make it such that your 'view' s at 0,0. That will be good if you intend to have your own view transform that you want to move around yourself such as a side scrolling platform type game.

You can see what is happening from the links that NikitaBlack provided. The bottom row can be thought of as the translation, for XMMatrixOrthographicLH the x/y values of that are set as 0 but they are not for XMMatrixOrthographicOffCenterLH . You could use XMMatrixOrthographicOffCenterLH to create the same matrix by using left=-400, right = 400.

The third column/fourth row element looks different in both zn/(zn-zf) versus -zn/(zf-zn) but actually they are exactly the same if you rearrange them algebraically. Not sure why they are arranged differently there.


#5301373 The Near View Is Not Shown Properly

Posted by Nanoha on 19 July 2016 - 09:16 AM

Hard to really tell without knowing more about how big everything is. Have you tried reducing your near plane value? 

Matrix.frustumM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1, 1, 1, 150);

One of those is probably it. I don't know what you are using there so I'm not entirely sure but you could try something like:
Matrix.frustumM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1, 1, 0.1, 150);
 
Is this the function you are using?

https://developer.android.com/reference/android/opengl/Matrix.html#frustumM(float[], int, float, float, float, float, float, float)

 

You might be better of using perspectiveM to setup your projection.




#5301183 Platform On A Pedastle Rotation

Posted by Nanoha on 18 July 2016 - 04:38 AM

Not entirely sure what you want to achieve, do you want the platform to lean over as the player reaches the edge as though they are balancing? For example, if the player is in the centre then there is no tilt, if they are at the north edge then the platform tilts downwards to the north side? I'm not sure if what you suggest will do that.

 

Do you have access to some kind of axis-angle rotation? If so then this should be relatively easy. First get a vector from the centre of the platform to the player:

dir = player.pos - platform.pos;
//You're going to need it normalised so get the length and also check it isn't 0 (if it is you don't need to do anything)
length = dir.length();
if(length == 0)
   return;
dir /= length;
// Also, the centre of the player might be above the centre of the platform depending how you define things
// So you should check that too
if(DotProduct(dir, up) == 0)
   return;
// Now you can find your axis of rotation which is just a vector at right angle to your direction
// You can get that with a cross product
axis = CrossProduct(dir, up); // Might have to swap dir/up around
// Now you just need to decide how much of an angle to rotate by
// You could use the length and the platform radius to decide it, tilting maximum as the length reaches radius
angle = length*maxAngle/radius;

How you actually turn that axis-angle into your desired rotation will depend on what you are using. Is it only euler angles you have access too?




#5301065 Move The Player Along The Direction It Is Facing

Posted by Nanoha on 17 July 2016 - 06:26 AM

Since the game is supposed to be in space, the spaceship may be pointing upward and slightly on the left and thus thrust forward in that direction. So yeah, I need to do both yaw and pitch. Am I right?

Yeah you have the right idea, just in the example you posted you are choosing the wrong axis to rotate about. Eventually you probably will want to also rotate about the z-axis (roll). Since your game is a space game I would suggest you stop using euler angles as soon as possibly, they are a terrible choice when you want full 3d movement and it will cause you no end of misery! For example if your ship were to pitch up 90 degrees and then decide to yaw 90 degrees it will probably roll instead. You might notice that kinda behaviour happening a lot in fps games but it's not really a problem there.
 
Instead you should try to use quaternions to represent your ship's orientation. I briefly Googled to check and glm does support quaternions. I came across this tutorial that is doing rotations using glm quaternions, it might be of some use to you:
http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/
 
Your ship would instead of having angles would have a quaternion orientation. Then in your move forward you would do something like:

void InputPlayer::MoveForward(float dt)
{
glm::vec3 temp(0, 0, 5);
temp = player->GetOrientation() * temp; // Where GetOrientation() returns a quaternion
player->SetPos(player->GetPos() + temp * dt);
}

I'm not familiar with glm so I can't say that will compile right off but that is the general idea.




#5301056 Move The Player Along The Direction It Is Facing

Posted by Nanoha on 17 July 2016 - 03:44 AM

Simply put you are rotating about the wrong axis. Your temp vec points down the z-axis and then you rotate it about the z-axis, all that does is to roll the vector which doesn't really do anything, it still points down the z-axis. Have you tried debugging this and steping through? You will probably see that temp is still (0, 0, 5) after doing rotateZ.

 

What is it you expect to happen? Are you trying to yaw or pitch? If you want to yaw then you need to rotate about the y-axis (I assume up is y?), for pitch you want to rotate about the x axis. This assumes up/down is y, left/right is x and forward/back is z.




#5301017 "no Shaders Attached To Current Program" Only In Release Build

Posted by Nanoha on 16 July 2016 - 05:23 PM

I edited my post before I saw your reply. Check the last bit as I think that might be your issue.

 

 

 

I thought It is always a good idea to pass by reference so you wouldn't wast memory and cpu cycles copying from one variable to the other. This way both variables have the same memory address.

 

I'm no expert on this so take it with a grain of salt but you should be very very careful when using pass by reference, passing things into functions by reference is often a good idea particularly with very large objects (otherwise they have to be copied each time). You should try to pass in const references though unless you know the function will/must modify your object. It's often unnecessary to pass things like ints and floats by reference but passing something like a string by reference usually makes sense.

 

Where it all goes very bad is when you start returning references. This can have it's uses but don't use it as a rule  of thumb, it's more an exception rather than a norm and you need to be very careful that the thing you are returning the reference from is going to outlive the thing you are returning it to. Also you are just returning an int which will likely be the same size as an int& (depending on your system) so you don't gain any extra memory from doing it. The same goes for passing references into functions for types such as ints, it's likely the same size as a reference (again, depending on your system).

 

It is as you say, the reference is essentially just a pointer pointing to memory and you return a reference to something allocated on the stack, as soon as that function returns the local variable no longer exists but the memory still does and could contain anything. The way you are using it I guess there isn't a chance for that memory to get reused so it's lucky the 'value' stays the same but you shouldn't do that.




#5301011 "no Shaders Attached To Current Program" Only In Release Build

Posted by Nanoha on 16 July 2016 - 04:44 PM

Alright, I did some more investigating and for some reason glAttachShader is return an error code of 1281. I have no idea why. In debug mode it returns 0.

 

In that case which of these values are not as expected?

            glAttachShader(programID, vertexShader);
            glAttachShader(programID, fragmentShader);

If programID ok, is vertexShader ok, is fragmentShader ok?

 

I just noticed this:

GLuint &GLShader::CompileShader(std::string shaderCode, GLenum shaderType)

You are returning a reference to a local variable that is a bad idea. Surprisingly I did a quick test and (in my test at least) it did return the value I would expect in both debug and release build but you really shouldn't be doing that. I am surprised it works but I am no expert. I wouldn't be surprised if that is your issue.

 

You should really check these return values when before you use them too. Don't just print an error then carry on as if there was no error. Could you paste the full log you get when you fail to compile the shader? It might be an idea to post the shader code too. Also, is it the vertex or the fragment shader that isn't compiling?

 

Print the actual code you pass to the shader, ({after doing your split function). Is everything null terminated correctly? I remember having issues at some point where I wasn't doing that step correctly and I had junk on the end of my shader which prevented it from compiling. This isn't an issue in debug because the whole length of memory is usually initialised to 0 so the string ends up being null-terminated but in release this isn't the case. Make sure your code stops where it should stop.




#5300852 Opengl:what Is The Best Way To Get Color Of One Pixel From A 2D Texture?

Posted by Nanoha on 15 July 2016 - 02:24 AM

Curses, I wrote a long post, accidentally pasted over it and clicked submit and lost it :(. Will try again.

 

When you want to sample (take a color from) a texture you have to provide u,v coordinates to opengl to tell it where on that texture to take a color from. Typically people call the horizontal axis u and the vertical axis v. For the u axis, the left of the texture is 0, the right of the texture is 1 so if you wanted that cyan part of your gradient in the central area you would likely provide a value of 0.5 for u. V is then the vertical axis and in opengl the bottom of the texture is v=0 and the top is v=1. All areas inside are then within the range 0-1. If you provide values outside of that range then it will have a different effect depending on the texture wrap you have setup (search opengl texture wrapping for more info).

 

With the gradient texture you have, it is effectively 1D, no matter what v value you give it the color won't change so you can happily use a v value of 0 (the second parameter in glTexcoord2i). We are interested only in the horizontal value u. To get the left we give a value of 0, to get the right we give a value of 1 and to sample the texture in between we give a value between 0 and 1. The problem with glTexcoord2i is that it accepts only integers (0, 1, 2, 3 etc and negatives) so we can never actually give it a value in between so we can never show the whole of your gradient only the left and right pixels. To solve this we can switch to glTexcoord2f instead which takes floats so we can provide values such as 0.5 and thus get that cyan in the central area.

 

The other issue is you are passing in c%16 which will give integer values too which you don't want. From what I understand of your code that line should be a rainbow being repeated every 16 pixels. Hopefully this should help you out:

// It is horribly inefficient to call begin/end for every single primitive so do this 
// outside of the loop
glBegin(GL_POINTS);
for(int c = 0; c < ss; x+=ds, c++)
{
   // You want a value that goes 0-1 every 16c. To get it going every 16c:
   // c = c%16; Then to convert that to 0-1
   // u = c/(16-1); It's -1 since the largest value you can have is 15 which will then give 1
   glTexcoord2f((c%16)/15.0f, 0);
   glVertex2i((int)x, 700);
}
glEnd();
what about v?  in gradient1.png file, there are a lot of redundant colors (all row are in the same colors), but in the future, I will create a new gradient png file, it will be quite different.

 

If you want the vertical aspect then you should stop using points and instead create a quad which will be much more efficient and easier to work with. You would simply define 4 points (the corners) and let opengl do the rest. If you wanted the texture to be repeated, say 10 times, then instead of putting the texture coord to u=1 on the right you'd just set it to 10 (and setup your texture wrapping to repeat). It will save you a lot of trouble. You can make textures procedurally which is sort of the same as drawing a whole bunch of points but a lot more efficient. Ask yourself if you really need to be using points, they are probably a bad choice for what you are trying to do.




#5300515 How to automate texture deleting?

Posted by Nanoha on 13 July 2016 - 07:14 AM

textures.push_back( loadTexture(something.jpg) );

 

Have you considered what happens when the same texture is requested twice? It might be worth checking if it already exists before loading another copy. 




#5300499 Getting address of an item in a std::vector

Posted by Nanoha on 13 July 2016 - 05:39 AM

 

So from my understanding then, with the current 'shallow' copy the vector is causing the destructor to be called which is then deleting cleaning up 'memblock' making the pointer to the copy of 'memblock' invalid. This sound right?

Yes.
 
Google 'C++ rule of three' for some greater enlightenment.

 

 

Which is now the rule of 5 thanks to c++11 which will probably be useful given you are (currently) moving around potentially large objects.

https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)




#5300448 Getting address of an item in a std::vector

Posted by Nanoha on 12 July 2016 - 04:20 PM

A few points.

void data_set(char* data, int size)  
{   
   memblock = new char[size];   
   memblock_size = size;   
   memcpy(memblock, data, size);
}

If memblock isn't null then you have a memory leak. You should check first and delete if it already points to something.

 ~Asset()
 {
  if (memblock)
   delete memblock;
 }

You assign an array so you need to use array delete otherwise you get another memory leak. Any time you do this:

memblock = new char[]

you have to do this:

delete[] memblock; // notice the []

~Asset()
{
   // There's no need to check if it exists before deleting since deleting a nullptr does nothing
   // but if someone disagrees I would love to be corrected on that so I can improve my own coding.
   // since you assigned an array you need to use array delete
   delete[] memblock;
}
 
 void data_set(char* data, int size)
 {
   // You should do some sanity checks here, is data null? is size 0 or negative etc
   // memblock might already contain data so you should delete it before assigning otherwise
   // you will be leaking memory
   delete[] memblock; // array delete
   // If you want to be robust you could assign null to memblock after deleting but here there is
   // no need since you are assigning to it right away
   memblock = new char[size];
   memblock_size = size;
   memcpy(memblock, data, size);
 }

With your vector, if you are pushing Asset objects onto it then you are making a copy. You need to be really careful with that because if you don't make a copy constructor then you have some seriously unpleasant behaviour if your object uses dynamic memory allocations which yours does. The original has a memblock which points to a block of memory, you make a copy (one in the vector), now if you have a default copy constructor then both of those objects have a memblock which points to the same memory. One gets deleted which hopefully deletes it's dynamic memory as it should but then the second one will still have a pointer pointing to that address which is no longer valid. The way you have your global id makes this a bit more complicated so I would disable the copy constructor and make a more constructor then use std::move when you push your assets onto the vector (actually I would rethink the whole design and go for one of the other suggestions in the thread).

// This constructor 'steals' the data/values from the other object so you aren't left with 2
// objects pointing to the same memory. You could make a copy of the data with a copy constructor
// but then you have ambiguity as to how to deal with the id.
Asset(Asset&& asset)
{
   memblock = asset.memblock;
   memblock_size = asset.memblock_size ;
   id = asset.id;

   asset.memblock = nullptr;
   asset.memblock_size = 0;
   asset.id = NO_ID;
 }
// prevent copying and so on
Asset(const Asset&) = delete;
Asset& operator=(const Asset&) = delete;
Asset& operator=(Asset&&) = delete;

I would definitely rethink how you deal with this though. There's been some good suggestions in this thread which I might steal myself (love the one with the smart handles that SeanMiddleditch posted).




#5300335 Getting address of an item in a std::vector

Posted by Nanoha on 12 July 2016 - 02:54 AM

 

A better option would be instead of storing Asset in the vector you store Asset* (or std::shared_ptr<Asset>) and return those.


I'm not happy with that. Unless the sharing of ownership is explicitly desired (and in my experience it usually is not) you should not use shared_ptr. Store unique_ptr in the vector and return the raw pointers. If that is a problem (because Assets might get deleted) then you should fix your messed up ownership semantics first instead of trying to work around it with foolishly shared ownership.

 

 

I do agree with this. As I mentioned, better approach - not ideal.






PARTNERS