i am new to ogre and have read the basic tutorials but unable to understand how to create a orbit camera with mouse wheel zooming.
here is my camera code
// Create the scene node(orbit camera)
node = mSceneMgr->getRootSceneNode()->createChildSceneNode("orbit", Ogre::Vector3(0, 100, -150));
node->attachObject(mCamera);
// create the second camera node(freecam)
node = mSceneMgr->getRootSceneNode()->createChildSceneNode("free", Ogre::Vector3(0, 100, 400));
// create the third camera node (3rd person robot cam)
node = mSceneMgr->getRootSceneNode()->createChildSceneNode("robocam", Ogre::Vector3(0, 100, -80));
And here is my keypress function
bool BasicTutorial05::processUnbufferedInput(const Ogre::FrameEvent& evt)
{
Ogre::Vector3 transVector1 = Ogre::Vector3::ZERO;
if (cam1 == true)//when cam 1 is selected, bool cam1 will be true;
{
if (mKeyboard->isKeyDown(OIS::KC_S))
{
mSceneMgr->getSceneNode("orbit")->pitch(Ogre::Radian(-0.012f));
}
if (mKeyboard->isKeyDown(OIS::KC_W))
{
mSceneMgr->getSceneNode("orbit")->pitch(Ogre::Radian(0.012f));
}
if (mKeyboard->isKeyDown(OIS::KC_A))
{
mSceneMgr->getSceneNode("orbit")->yaw(Ogre::Radian(0.012f));
}
if (mKeyboard->isKeyDown(OIS::KC_D))
{
mSceneMgr->getSceneNode("orbit")->yaw(Ogre::Radian(-0.012f));
}
}
mSceneMgr->getSceneNode("orbit")->translate(transVector1 *evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL);
}
and the mouse wheel zooming
//zooming for orbit camera
Ogre::Vector3 transVector2 = Ogre::Vector3::ZERO;
if (mMouse->getMouseState().Z.rel != 0){
transVector2.z = -mMouse->getMouseState().Z.rel;
}
but i can able to sort of orbit around the point where the camera is but only when i use the wheel scroll zoom, instead of rotating around a point it rotates where the camera is.
How do i change it that it only rotates at a point?
Create two nodes for your camera - the first one is the target and it's placed at the point you want to rotate around.
The second node should be created at some distance from the first one. You should attach it as the child of the target and attach your camera to this node. Finally, you should point your camera at the target node (the first one).
With this setup you'll just need to put your target node at the point of your interest and rotate it as you want. The camera position will follow the target, because it's his child. And by moving your camera node closer to the target node you can change your zoom level.
Related
I am writing a tile map editor in SFML and C++. I have been having all sorts of troubles with the mouse. I am using the built in SFML Mouse:: static functions and recently managed to get a custom cursor moving on the screen and pointing accurately to a tile by doing as follows:`
Sprite cursor;
bool focus = false;
RenderWindow window(VideoMode(512, 288), "Tilemap editor");
window.setFramerateLimit(60);
Texture cursorTexture;
if(!cursorTexture.loadFromFile("Graphics/Cursor.png")) {
std::cout << "Failed to load cursor texture\n";
return 0;
}
cursor.setTexture(cursorTexture);
Mouse::setPosition(mousePos);
While(window.isOpen()) {
window.setMouseCursorVisible(focus);
if(Mouse::getPosition().x != lastMousePos.x) {
mousePos.x = mousePos.x + (Mouse::getPosition().x - lastMousePos.x);
}
if(Mouse::getPosition().y != lastMousePos.y) {
mousePos.y = mousePos.y + (Mouse::getPosition().y - lastMousePos.y);
}
cursor.setPosition(mousePos.x, mousePos.y);
lastMousePos = Mouse::getPosition();
window.clear();
window.draw(cursor)
window.display()
}
The built-in Mouse functions only display relativity to the desktop or the window and as I am using this app in a small window in which my view moves, I can't use either. The solution above moves a cursor independent of the desktop and with the ability to move the cursor if and when I want to move my view.
The issue is that my mouse will move off the side of the app when I try to click items in the top left corner.
Is there a good cross-platform (I'm on Linux BTW) way to trap the mouse inside of the window unless I enter a keystroke (like a VM window)? Also, is there a better way to do cross-platform mouse support in general? SFML kinda sucks. (Code obviously needs to be in a main function and the namespace must be sf with SFML/Graphics.hpp included)
There is already a method for that.
void setMouseCursorGrabbed (bool grabbed)
// Grab or release the mouse cursor.
You can also use these methods to convert your screen coordinates to mouse coordinates and vice versa.
Vector2f mapPixelToCoords (const Vector2i &point) const
// Convert a point from target coordinates to world coordinates, using the current view.
Vector2f mapPixelToCoords (const Vector2i &point, const View &view) const
// Convert a point from target coordinates to world coordinates.
Vector2i mapCoordsToPixel (const Vector2f &point) const
// Convert a point from world coordinates to target coordinates, using the current view.
Vector2i mapCoordsToPixel (const Vector2f &point, const View &view) const
// Convert a point from world coordinates to target coordinates.
sf::RenderWindow Class Reference
I have a view with same dimensions of original window (500,300)
I apply view.zoom(2) to leave the view at half the size.
Now the view is centered. I want to move the view to the upper left corner of the original window. So I put view.setCenter(500,300);
The view is now correctly positioned in the upper corner of the original window. But now I want to rotate the view, making the center of the view its own top left corner, ie (0,0): view.setRotation(5);
As you can see, the center of the axis of rotation should be 0.0 but not respected.
The problem is that if I do view.setCenter (0,0), the whole view returns to the middle of the original window.
How to solve this?
Instead of using view.setCenter(500,300); move it via view.move(x_offset, y_offset);. Then applying setCenter(...) won't redefine the center and it won't get reset.
I recommend consulting the API reference of View for further reading.
You might also be interested in void sf::View::setViewport(const FloatRect& viewport) or void sf::View::reset(const FloatRect& rectangle).
This code, kindly provided by Geheim, solves the problem and also teaches a more practical approach to SFML.
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window({500, 300}, "SFML Views", sf::Style::Close);
window.setFramerateLimit(120);
bool useViewPort {true}; // change this to false to see the other version
sf::View camera {{0, 0}, static_cast<sf::Vector2f>(window.getSize())};
if (useViewPort)
{
camera.setViewport({-0.5f, -0.5f, 1, 1});
camera.rotate(5);
}
else
camera.setCenter(camera.getSize());
camera.zoom(2);
window.setView(camera);
sf::RectangleShape background {camera.getSize()};
sf::RectangleShape square {{50, 50}};
square.setFillColor(sf::Color::Red);
sf::RenderTexture texture;
texture.create(window.getSize().x, window.getSize().y);
while (window.isOpen())
{
for (sf::Event event; window.pollEvent(event);)
if (event.type == sf::Event::Closed)
window.close();
window.clear();
if (useViewPort)
{
window.draw(background);
window.draw(square);
}
else
{
texture.clear();
texture.draw(background);
texture.draw(square);
texture.display();
sf::Sprite content {texture.getTexture()};
content.rotate(-5); // you have to rotate in the other disquareion here, do you know why?
window.draw(content);
}
window.display();
}
return EXIT_SUCCESS;
}
I'm glad you got the result you wanted by applying viewports using Geheim's code.
However, if you don't want to be using viewports to clip areas of the window and such, you can still rotate a view around a specific point other than its centre. You just need a little bit of mathematics...
Take the different between the target point (in the view's co-ordinate system) and the view's centre and rotate that point by the amount you wish to rotate the view and around the view's centre. Then, calculate the difference between those points (the target point and the rotated point). Once you have this difference, simply rotate the view (around its centre as normal) but then move the view by that difference.
It might sound complicated so you might want to just use this free function that I made that does it all automatically; it's on the SFML Wiki:
RotateViewAt
I am currently working on a project where all my players use different Camera.
I first thought using UCameraComponent, but each camera has to turn around a certain point and not moving with the movement of the pawns.
So I decided to Spawn a Camera Actor in the BeginPlay() of my pawn.
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
if (!hasCamera) { // Camera not set yet
FVector vectpos; // Target position of the camera
vectpos.X = -1130;
vectpos.Y = 10;
vectpos.Z = 565;
FRotator rotation;
rotation.Pitch = -22;
rotation.Yaw = 0;
rotation.Roll = 0;
APlayerController* controller = Cast<APlayerController>(GetController());
if (controller == NULL) // When I'm on client, the GetController() return NULL.
{
// Trying to find the controller of my client
for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
{
controller = *Iterator;
//On client, there is only 1 controller according to the documentation.
}
}
if (controller != NULL)
{
controller->SetViewTarget(Camera); // Set the view with the new camera
}
SetCamera(true); // Call and RPC Function to update the hasCamera variable
}
}
This is working for the first player, and after that it depends. Sometimes, the second player get a camera that works fine, but sometimes, he is viewing through the wrong camera and the Camera variable is not the same one he is looking in. Sometimes, when a new players join the game, it make the first/second player looking through the wrong camera.
Here is the GameInstance Blueprint we use to make the LAN Connection bewteen the clients and the server(the first client to create the game)
If someone can find why the camera is not working as expected , it would be very nice ! Thanks you all in advance for your help.
Apparently, You choose the wrong way.
In UE4 'ACharacter' (APawn to be precise) is a character representation in the world, so you will have one for every single player. Thus, it is strange to put your camera code in it.
You should make your own controller (ex. 'AMyPlayerController') and control camera from it. Obviously, only for a local player.
I'm building a game with Cocos2d-x version 3.13.1 and I've decided to go with the built-in physics engine (Chipmunk 2D) to accomplish animations and collision detection. I have a simple projectile called BulletUnit that inherits from cocos2d::Node. It has a child sprite that displays artwork, and a rectangular physics body with the same dimensions as the artwork.
The BulletUnit has a method called fireAtPoint, which determines the angle between itself and the point specified, then sets the initial velocity based on the angle. On each update cycle, acceleration is applied to the projectile. This is done by applying impulses to the body based on an acceleration variable and the angle calculated in fireAtPoint. Here's the code:
bool BulletUnit::init() {
if (!Unit::init()) return false;
displaySprite_ = Sprite::createWithSpriteFrameName(frameName_);
this->addChild(displaySprite_);
auto physicsBody = PhysicsBody::createBox(displaySprite_->getContentSize());
physicsBody->setCollisionBitmask(0);
this->setPhysicsBody(physicsBody);
return true;
}
void BulletUnit::update(float dt) {
auto mass = this->getPhysicsBody()->getMass();
this->getPhysicsBody()->applyImpulse({
acceleration_ * mass * cosf(angle_),
acceleration_ * mass * sinf(angle_)
});
}
void BulletUnit::fireAtPoint(const Point &point) {
angle_ = Trig::angleBetweenPoints(this->getPosition(), point);
auto physicsBody = this->getPhysicsBody();
physicsBody->setVelocityLimit(maxSpeed_);
physicsBody->setVelocity({
startingSpeed_ * cosf(angle_),
startingSpeed_ * sinf(angle_)
});
}
This works exactly as I want it to. You can see in the image below, my bullets are accelerating as planned and traveling directly towards my mouse clicks.
But, there's one obvious flaw: the bullet is remaining flat instead of rotating to "point" towards the target. So, I adjust fireAtPoint to apply a rotation to the node. Here's the updated method:
void BulletUnit::fireAtPoint(const Point &point) {
angle_ = Trig::angleBetweenPoints(this->getPosition(), point);
// This rotates the node to make it point towards the target
this->setRotation(angle_ * -180.0f/M_PI);
auto physicsBody = this->getPhysicsBody();
physicsBody->setVelocityLimit(maxSpeed_);
physicsBody->setVelocity({
startingSpeed_ * cosf(angle_),
startingSpeed_ * sinf(angle_)
});
}
This almost works. The bullet is pointing in the right direction, but the trajectory is now way off and seems to be arcing away from the target as a result of the rotation: the more drastic the rotation, the more drastic the arcing. The following image illustrates what's happening:
So, it seems that setting the rotation is causing the physics engine to behave in a way I hadn't originally expected. I've been racking my brain on ways to correct the flight path, but so far, no luck! Any suggestions would be greatly apprecitated. Thanks!
In my scene I have terrain that I want to "grab" and then have the camera pan (with its height, view vector, field of view, etc. all remaining the same) as I move the cursor.
So the initial "grab" point will be the working point in world space, and I'd like that point to remain under the cursor as I drag.
My current solution is to take the previous and current screen points, unproject them, subtract one from the other, and translate my camera with that vector. This is close to what I want, but the cursor doesn't stay exactly over the initial scene position, which can be problematic if you start near the edge of the terrain.
// Calculate scene points
MthPoint3D current_scene_point =
camera->screenToScene(current_point.x, current_point.y);
MthPoint3D previous_scene_point =
camera->screenToScene(previous_point.x, previous_point.y);
// Make sure the cursor didn't go off the terrain
if (current_scene_point.x != MAX_FLOAT &&
previous_scene_point.x != MAX_FLOAT)
{
// Move the camera to match the distance
// covered by the cursor in the scene
camera->translate(
MthVector3D(
previous_scene_point.x - current_scene_point.x,
previous_scene_point.y - current_scene_point.y,
0.0));
}
Any ideas are appreciated.
With some more sleep :
Get the initial position of your intersected point, in world space and in model space ( relative to the model's origin)
i.e use screenToScene()
Create a ray that goes from the camera through the mouse position : {ray.start, ray.dir}
ray.start is camera.pos, ray.dir is (screenToScene() - camera.pos)
Solve NewPos = ray.start + x * ray.dir knowing that NewPos.y = initialpos_worldspace.y;
-> ray.start.y + x*ray.dir.y = initialpos_worldspace.y
-> x = ( initialpos_worldspace.y - ray.start.y)/rad.dir.y (beware of dividebyzeroexception)
-> reinject x in NewPos_worldspace = ray.start + x * ray.dir
substract initialpos_modelspace from that to "re-center" the model
The last bit seems suspect, though.