Megabyte Softworks
C++, OpenGL, Algorithms

Current series: OpenGL 3.3
(Return to list of OpenGL 3.3 tutorials)

24.) Animation Pt. 1 - Keyframe MD2

Hello guys! This is the 24th tutorial from my series. Finally, we're getting to really interesting and cool stuff. Today it is animation, more specifically Keyframe animation using old good MD2 models. So far, our applications consisted of pretty static scenes with occasional rotation of objects. But today we will take this to the whole new level, we will animate model themselves! Are you ready to make that step? Are you fully commited you want to push your skills to the next level ? Okay, it might have been a little exaggerated, but animations are definitely cool and let us do some cool stuff, like games . Let's do this.

Keyframe Animation

Keyframe animation is one of the easiest animation methods and also is one of the first methods developed to animate 3D models. The idea is to create starting and ending frame and then calculate the intermediate frames using start and end frame data at an arbitrary position in-between them. The parameter that usually controls the state of the intermediate frame is time - time required for model to pass from starting frame to ending frame. So if a model has to get from starting to ending frame in 2 seconds, then intermediate model at time 0s is the starting frame, at time 1s it is the frame exactly in the middle between starting and ending frame, and finally at time 2s it is the ending frame. In the following image, you can see a sample starting and ending frame of model with 4 vertices:

As you could have seen in the image above, we could calculate every single frame in between the starting and ending frame just by having the total animation running time (2 seconds in this case) and current time. Every value in between has been calculated by INTERPOLATION, in this case it was a simple linear interpolation. There are other types of interpolation actually (for example spherical), but this time we will go through the simplest, i.e. linear interpolation.

Linear Interpolation

Given 2 endpoint values v0 and v1 (in our case two vertices) and a parameter t in range <0..1>, linear interpolation gives us arbitrary point on the line segment defined by these two endpoints, controlled by parameter t, while following conditions hold:

So basically what is happening here is that we go from one point to another across a straight line, thus the name linear interpolation. How do we apply it to the animation then? We do this for every single vertex in the model! For every single vertex that is in starting frame, we also have one in the ending frame and thus we are able to do linear interpolation between two frames! This doesn't apply only for vertex positions - this way, we can also animate normals or even texture coordinates!

However, having only two keyframes for animation would really not be very nice. Imagine an animation where you rotate something. For example, if we could rotate our heads by 180 degrees (we can't, but now imagine we can ), the animation of two keyframes would look like this: The face would get sucked into the head itself and then reappeared on the other side of head. So important lesson from here - when making keyframe animation, two keyframes are just not enough. But if we approximate animation by 20 frames or so and then we would interpolate between keyframes consecutively one by one, the animation will look smoother and better!

And this is exactly what MD2 models are about. Every MD2 model has animations consisting of several keyframes and also defines time, how long should the animation last. We are going to explore the MD2 model file format right now.

MD2 Model File

MD2 File Format is a little bit complex and explaining every single structure would be very exhausting. Especially when there is already an article explaining it pretty much in detail. It's article by David Henry - MD2 file format (Quake 2's models) and lots of structures used in my tutorial are taken from here with full permission from the author, so I shouldn't be sued or anything . So before reading further, try to read through his article first and then this tutorial continues with loading of MD2 files and preparing appropriate OpenGL structures for that. Below is our MD2 model class:

class CMD2Model
{
public:

void RenderModel(animState_t* animState);

animState_t StartAnimation(animType_t type);
void UpdateAnimation(animState_t* animState, float fTimePassed);
void PauseAnimation();
void StopAnimation();

static    anim_tanimlist[21];
private:
UINT uiModelVAO;

vector<UINT> uiFramesBuffer;

vector< vector<glm::vec3> > vVertices; // Vertices extracted for every frame
vector <vector< int > > vNormals; // Normal indices extracted for every frame
vector<int> glCommands; // Rendering OpenGL commands

vector<CVertexBufferObject> vboFrameVertices; // All frames (keyframes) of model
CVertexBufferObject vboTextureCoords; // Texture coords are same for all frames

vector<int> renderModes; // Rendering modes
vector<int> numRenderVertices; // with number of vertices

CTexture tSkin;

UINT uiVAO;
};

Because most of the difficult stuff is in LoadModel function, we will go through it part by part, because it's waaaaaay to long to explain at once. Here is the first part - reading frames in the model:

{
FILE* fp = fopen(sFilename, "rb");

char* buffer = new char[header.num_frames * header.framesize]; // Read all frame data to one big buffer

vVertices.resize(header.num_frames, vector<glm::vec3>(header.num_xyz)); // Allocate space for vertices

// Extract vertices and normals from frame data
{
frame_t* frame_ptr = (frame_t*)&buffer[header.framesize * i]; // Convert buffer to frame_t pointer

{
vVertices[i][j].x = frame_ptr->translate[0] + (float(frame_ptr->verts[j].v[0]) * frame_ptr->scale[0]);
vVertices[i][j].y = frame_ptr->translate[1] + (float(frame_ptr->verts[j].v[1]) * frame_ptr->scale[1]);
vVertices[i][j].z = frame_ptr->translate[2] + (float(frame_ptr->verts[j].v[2]) * frame_ptr->scale[2]);

vNormals[i][j] = frame_ptr->verts[j].lightnormalindex;
}
}

//...
}

Next we need to extract data from this buffer. So first, we allocate sufficient space to store vertices and normals. We store them into vector < vector< glm::vec3> >, so we basically create a 2D array, which we access in the way vVertices[frame_index][vertex_index]. The same goes for normals. However, the way extraction works may be a little bit confusing, because we convert our buffer (char pointer) to frame_t pointer. This is nothing difficult, but if you look at the frame_t definition, which is a structure where frame data are stored:

typedef struct
{
float       scale[3];       // scale values
float       translate[3];   // translation vector
char        name[16];       // frame name
vertex_t    verts[1];       // first vertex of this frame

} frame_t;

You can see one very suspicious thing - the last member verts[1]. It's an array of vertices of size 1. Whaaaaat? How is it possible that later we access it as normal array with indices greater than 0 and all the vertices are stored there? Why isn't there a pointer or something? Let me explain. Pointer to vertices can point absolutely to any place in memory. However, by having that one first vertex at the end of struct, we have assured that this array is aligned in memory is truly at the end of frame_t and continues even beyond frame_t itself. So the memory layout of whole frame looks as following:

So it is really nothing else than just some ease of access to the vertices themselves. There are other ways to do the very same thing, but I left it this way. To be honest, I'm not exactly sure whether all these structs are original MD2 structures also used in Quake 2 codes (which are available online for free BTW, link at the end of the tutorial) or these structures are made by David Henry, author of the linked MD2 article. Anyway, it doesn't matter, because it works and it is an interesting and unusual approach to something like this, although it may confuse people at first. But because of that reason, we cannot simply move the frame_ptr (which points to frame) like we normally move pointers, because it simply wouldn't work correctly. The increase of pointer value by 1 (which should intuitively point to next frame) will actually move frame_ptr by constant size of 44 bytes, which is size of frame_t structure. We need to move by header.framesize to have a correct pointer. We don't move the pointer manually, but we set it at the start of every FOR cycle repetition to the correct position.

Now that we know how to extract vertex data, we will convert them to our structures. Now notice this - vertex_t structure consists of 3 unsigned chars for coords and 1 unsigned char for normal. What does this mean? How we can we define vertex position by unsigned chars? Now this might seem a little unusual today, but in 1997, the need to save disk space was a lot bigger than it is today (I remember my first HDD to have 1,18 GB in 1996, good times ). From this reason, MD2 model file format had only 1 byte per vertex dimensions, so that you could store only 256 different values there. Today, we can safely store whole floats (4 bytes), and everything is alright. But these 256 values were also scaled and translated. Each frame class has also translation and scale vector, by which we multiple all the BYTE values. This way we will get from 256 different values to... 256 values, but in floating point numbers and stretched or shrinked a little . The vectors converted are then translated by translation vector, that is written in the file for every frame.

Now you should understand, how to extract vertex data. The last thing we need to get is per vertex normal. And normal is also stored as 1 unsigned char, so only 1 byte to code a normal? You ask how is this possible? Easily - there is an global precomputed array of 256 normals in anorms.h file, which contains MD2 model normals. So this 1 byte is only an index to that array, nothing else .

This should close the vertex and normals reading part, let's move further in loading - now we will load info about how to render the model:

{
//...

// Now let's read OpenGL rendering commands

int i = 0;
int iTotalVertices = 0;

// And start with creating VBOs for vertices, textue coordinates and normals
vboTextureCoords.CreateVBO();

while(1) // We while loop until we come to 0 value, which is the end of OpenGL commands
{
int action = glCommands[i];
if(action == 0)break;

int renderMode = action < 0 ? GL_TRIANGLE_FAN : GL_TRIANGLE_STRIP; // Extract rendering mode
int numVertices = action < 0 ? -action : action; // And number of vertices
i++;

renderModes.push_back(renderMode); // Remember the values
numRenderVertices.push_back(numVertices);

FOR(j, numVertices)
{
float s = *((float*)(&glCommands[i++])); // Extract texture coordinates
float t = *((float*)(&glCommands[i++]));
t = 1.0f - t; // Flip t, because it is (from some reasons) stored from top to bottom
int vi = glCommands[i++];

vboTextureCoords.AddData(&s, 4); // Add texture coords to VBO

{
vboFrameVertices[k].AddData(&vVertices[k][vi], 12); // Add vertex to VBO
vboFrameVertices[k].AddData(&anorms[vNormals[k][vi]], 12); // Add normal to VBO from normal table
}
}
}

//...
}

Now we can see the reading of rendering data. How exactly does this work? In header, we have defined something called header.num_glcmds. It's basically number of OpenGL calls required to render model properly. These GL commands are just a bunch of integers (exactly header.num_glcmds integers) we need to read and then decode information from them. It works like this - until we don't find 0, which means the end of all rendering, we go through these data and read things in following order:

• Render Mode - if the current integer is < 0, then next bunch of vertices is rendered using GL_TRIANGLE_FAN. Otherwise, it's rendered as GL_TRIANGLE_STRIP.
• Number Of Vertices - this is taken from previous integer, which also defined rendering mode. The absolute value of this is the number of vertices we taken into this rendering round. Example: The integer is -8. This means, that render mode is GL_TRIANGLE_FAN (because it's < 0) and we will read 8 vertices now. We can move our integer pointer to the next integer.
• Vertices - Now we are going to read data about vertices, we know their number from above lines. Each vertex taken into rendering consists of texture coordinates (two floats) and vertex index (our vertices are already read). The only confusing part in the code may be the part of extracting texture coordinates:

float s = *((float*)(&glCommands[i++])); // Extract texture coordinates
float t = *((float*)(&glCommands[i++]));
t = 1.0f - t; // Flip t, because it is (from some reasons) stored from top to bott

As I said, we have integers, but we need to convert them to floats. But not like typecast them, but just tell compiler that the memory here is 4 byte wide (integer size), but we want to use this not as integer, but as float (also 4 bytes). So we just change the pointer type from integer pointer to float pointer. That's exactly what these 2 dirty lines of code do . Then we also flip t coordinate, because from some reasons it is stored the other way around as OpenGL texture coordinates are used. Because texture coordinates are same for every frame (only vertices and normals change), we will create only one VBO for texture coordinates, that will be used throughout all frames. However, for vertices and normals we will have different VBOs and we will just fill them up with vertices previously extracted from frame data and normals from anorms.h (just a note here, for a better performance, it would be probably better to make one big VBO and cramp up all the data there, but now for learning purposes, this is more intuitive and easier to understand).

Next thing we need to do is to create VAO used for rendering and ANIMATING model. This VAO will be very similar to VAOs used for static model renderings - we will have traditional vertex attributes as vertices, normals and texture coordinates. But we will add two extra attributes - vertex position at next keyframe and normal at next keyframe. Next vertex position is vertex attribute with location 3 and next normal attribute has location 4:

{
//...

// Now all necessary data are extracted, let's create VAO for rendering MD2 model

glGenVertexArrays(1, &uiVAO);
glBindVertexArray(uiVAO);

{
vboFrameVertices[i].BindVBO();
}

vboFrameVertices[0].BindVBO(); // Vertex and normals data parameters

// Vertex positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), 0);
glEnableVertexAttribArray(3); // Vertices for next keyframe, now we can set it to same VBO
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), 0);
// Normal vectors
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), (void*)(sizeof(glm::vec3)));
glEnableVertexAttribArray(4); // Normals for next keyframe, now we can set it to same VBO
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), (void*)(sizeof(glm::vec3)));

// Texture coordinates
vboTextureCoords.BindVBO();

// Texture coordinates
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec2), 0);

//...
}

Texture coordinates are in separate VBO and they are common for all frames. At first, we will bind data from frame 0 as the default data, and we will change them dynamically during animation and rendering.

The last part of loading MD2 model deals with loading a texture for it. Because original MD2 models had paths bound to the Quake 2 data files, I usually found models with header saying that number of skins (textures) is 0. But this ain't true, because these models usually have correct texture coordinates and some texture file packed within. The following lines of code will look for the texture with same name as model file name in the directory of model, because MD2 models found on the internet usually have the same texture filename as model:

{
//...

// I have read, that if you read the data from header.num_skins and header.ofs_skins,
// these data are Quake2 specific paths. So usually you will find models on internet
// with header.num_skins 0 and texture with the same filename as model filename

// Find texture name (modelname.jpg, modelname.png...)

string sPath = sFilename;
int index = sPath.find_last_of("\\/");
string sDirectory = index != -1 ? sPath.substr(0, index+1) : "";
string sPureFilename = index != -1 ? sPath.substr(index+1) : sFilename;

string sTextureExtensions[] = {"jpg", "jpeg", "png", "bmp", "tga"};
index = sPureFilename.find_last_of(".");
if(index != -1)
{
string sStripped = sPureFilename.substr(0, index+1);
FOR(i, 5)
{
string sTry = sDirectory+sStripped+sTextureExtensions[i];
{
tTextures[i].SetFiltering(TEXTURE_FILTER_MAG_BILINEAR, TEXTURE_FILTER_MIN_BILINEAR_MIPMAP);
break;
}
}
}
fclose(fp);

//...
}

And that's all we need for loading a model! Now let's proceed to actual rendering and animating.

Implementing Keyframe Animation

To perform animation, we need to store animation state somehow. And we have a structure for that - animState_t. When we pass this structure to MD2 rendering function, model will be rendered with up-to-date positions and normals. It contains every thing we need to know about keyframe animation. Using time, that elapsed between two consecutive frames, we can update this structure, so that next frame, model looks a little different, thus creating effect of an animation. Let's have a look at its properties:

typedef struct
{
int startframe;         // first frame
int endframe;         // last frame
int fps;            // frame per second for this animation

float curr_time;      // current time
float old_time;         // old time
float interpol;         // percent of interpolation

int type;            // animation type

int curr_frame;         // current frame
int next_frame;         // next frame

} animState_t;

These properties deserve a little bit of explanation, so here it follows:

• startframe, endframe - indices of starting and ending frame of the animation. In MD2, we have read a lot of frames, and every single frame belongs to some of the animations. There is a structure called animlist, that lists all the animations and their starting and ending frame, you can find it listed below.

• fps - how many frames per second should be relayed in one second - this is important - if FPS is let's say 8, then time between two consecutive frames is 1.0s / 8 = 0.125 seconds!
• curr_time - current time, the same concept as GetTickCount
• old_time - old time, i.e. time, when the change between two frames occured
• interpol - current linear interpolation factor - a number ranging from 0 to 1 saying how far in the animation we are (explained in the first sections of the article)

• type - animation type, it's just an animation ID, where ID 0 is standing, ID 1 us running etc. (see below)
• curr_frame - current frame index
• next_frame - next frame index (it's usually curr_frame+1, but on the last frame it's a starting frame of animation again so that it creates a loop)

Here you can see animation list for the MD2 model, the array includes starting frame, ending frame and FPS for that animation:

anim_t CMD2Model::animlist[ 21 ] =
{
// first, last, fps
{   0,  39,  9 },   // STAND
{  40,  45, 10 },   // RUN
{  46,  53, 10 },   // ATTACK
{  54,  57,  7 },   // PAIN_A
{  58,  61,  7 },   // PAIN_B
{  62,  65,  7 },   // PAIN_C
{  66,  71,  7 },   // JUMP
{  72,  83,  7 },   // FLIP
{  84,  94,  7 },   // SALUTE
{  95, 111, 10 },   // FALLBACK
{ 112, 122,  7 },   // WAVE
{ 123, 134,  6 },   // POINTIING
{ 135, 153, 10 },   // CROUCH_STAND
{ 154, 159,  7 },   // CROUCH_WALK
{ 160, 168, 10 },   // CROUCH_ATTACK
{ 196, 172,  7 },   // CROUCH_PAIN
{ 173, 177,  5 },   // CROUCH_DEATH
{ 178, 183,  7 },   // DEATH_FALLBACK
{ 184, 189,  7 },   // DEATH_FALLFORWARD
{ 190, 197,  7 },   // DEATH_FALLBACKSLOW
{ 198, 198,  5 },   // BOOM
};

string sMD2AnimationNames[MAX_ANIMATIONS] =
{
"Stand",
"Run",
"Attack",
"Pain A",
"Pain B",
"Pain C",
"Jump",
"Flip",
"Salute",
"Fallback",
"Wave",
"Pointing",
"Crouch Stand",
"Crouch Walk",
"Crouch Attack",
"Crouch Pain",
"Crouch Death",
"Death Fallback",
"Death Fall Forward",
"Death Fallback Slow",
"Boom"
};

First column is starting frame, second is ending frame and third is the FPS of animation. Now we can proceed to two most important functions - UpdateAnimation, that takes a pointer to animstate_t structure and time passed between frames and updates data and RenderModel with provided animState_t.

UpdateAnimation()

Here is the code snippet for you to see the whole function first:

void CMD2Model::UpdateAnimation(animState_t* animState, float fTimePassed)
{
animState->curr_time += fTimePassed;

if(animState->curr_time - animState->old_time > (1.0f / float(animState->fps)))
{
animState->old_time = animState->curr_time;

animState->curr_frame = animState->next_frame;
animState->next_frame++;
if(animState->next_frame > animState->endframe)
animState->next_frame = animState->startframe;
}

animState->interpol = float(animState->fps) * (animState->curr_time - animState->old_time);
}

So what do we do here? First, we update current time by the provided time between this frame and the last frame. If the difference between current time and old time is greater than time between two consecutive frames (we calculated it from animation fps), we need to update data, so that they refer to new frames. Old time becomes current time and current frame becomes next frame. Now we need to calculate new next frame. We will simply increment it by 1 and then if it passes the animState->endFrame, which is the last frame, we reset it to animState->startFrame.

The last and the most important thing is to calculate interpolation factor, but this is an easy oneliner. Maximal difference between the animState->curr_time - animState->old_time can be 1.0s / animState->fps. We just need to stretch this value to range <0...1>. So we simply re-multiplicate it with animState->fps and we're done .

RenderModel()

In this section, you will see how to render models with a given animation state. This concerns shaders as well, so we will go through the vertex shader, that does all calculations between two frames. So first, let's have a look at rendering function itself:

void CMD2Model::RenderModel(animState_t* animState)
{
glBindVertexArray(uiVAO);

int iTotalOffset = 0;
tSkin.BindTexture();
if(animState == NULL)
{
glEnableVertexAttribArray(0);
vboFrameVertices[0].BindVBO();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), 0);
spMD2Animation.SetUniform("fInterpolation", -1.0f); // Set interpolation to negative number, so that vertex shader knows

FOR(i, ESZ(renderModes)) // Just render using previously extracted render modes
{
glDrawArrays(renderModes[i], iTotalOffset, numRenderVertices[i]);
iTotalOffset += numRenderVertices[i];
}
}
else
{
// Change vertices pointers to current frame
glEnableVertexAttribArray(0);
vboFrameVertices[animState->curr_frame].BindVBO();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), 0);

glEnableVertexAttribArray(3);
vboFrameVertices[animState->next_frame].BindVBO();
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), 0);

// Change normal pointers to current frame

glEnableVertexAttribArray(2);
vboFrameVertices[animState->curr_frame].BindVBO();
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), 0);

glEnableVertexAttribArray(4);
vboFrameVertices[animState->next_frame].BindVBO();
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 2*sizeof(glm::vec3), 0);

spMD2Animation.SetUniform("fInterpolation", animState->interpol);

FOR(i, ESZ(renderModes))
{
glDrawArrays(renderModes[i], iTotalOffset, numRenderVertices[i]);
iTotalOffset += numRenderVertices[i];
}
}
}

The function is branched into two sections - first one is rather simple. If the user doesn't provide animState_t structure, but passes NULL pointer, then we just render model statically. We will set the vertex position attribute to the vertices of the first frame. We also set interpolation factor to -1.0, so that the vertex shader know. Vertex shader then sees, that we don't want to do any inter-frame calculations, so it will just render stuff as we usually do.

However, second part is more interesting. We need to set pointer to vertex and normal positions from both frames - current frame and next frame. However, everything is pretty easy now, because all these data are stored in our animState_t structure now, so we just need to carefully set vertex attribute pointers. The most important thing is to set the interpolation factor here, so that the vertex shader can calculate inter-frames.

In both cases, rendering itself is done by iterating over all render modes we have read from the file. So we just do a series of GL_TRIANGLE_STRIP and GL_TRIANGLE_FAN renders, while providing correct vertex offsets. The last thing we should do is to have a look at the vertex shader, which calculates inter-frames:

#version 330

uniform struct Matrices
{
mat4 projMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
mat4 normalMatrix;
} matrices;

layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec2 inCoord;
layout (location = 2) in vec3 inNormal;
layout (location = 3) in vec3 inNextPosition;
layout (location = 4) in vec3 inNextNormal;

smooth out vec3 vNormal;
smooth out vec2 vTexCoord;
smooth out vec3 vWorldPos;

smooth out vec4 vEyeSpacePos;

uniform float fInterpolation;

void main()
{
mat4 mMV = matrices.viewMatrix*matrices.modelMatrix;
mat4 mMVP = matrices.projMatrix*matrices.viewMatrix*matrices.modelMatrix;

vTexCoord = inCoord;

vec3 vInterpolatedPosition = inPosition;
if(fInterpolation >= 0.0f)vInterpolatedPosition += (inNextPosition - inPosition)*fInterpolation;

vEyeSpacePos = mMV*vec4(vInterpolatedPosition, 1.0);
gl_Position = mMVP*vec4(vInterpolatedPosition, 1.0);

vec3 vInterpolatedNormal = inNormal;
if(fInterpolation >= 0.0f)vInterpolatedNormal += (inNextNormal - inNormal)*fInterpolation;

vNormal = (matrices.normalMatrix*vec4(vInterpolatedNormal, 1.0)).xyz;
vWorldPos = (matrices.modelMatrix*vec4(vInterpolatedPosition, 1.0)).xyz;
}

Notice two new variables here - vInterpolatedPosition and vInterpolatedNormal. Both of these are calculated only if interpolation factor is in range <0...1> as it should be (of course we could do a check if it's greater or equal than 0 and less or equal than 1, but one check is now enough, because we provide negative values only when we render models without animation and otherwise this range is valid. So we save one comparison). So if interpolation factor fInterpolation is valid, we will simply calculate difference between this and next frame and we add this difference to our current frame data multiplied by interpolation factor and we get inter-frame vertices and normals. The fragment shader used is the same as in other renderings, because this is the only place where something different happens.

Result

This is the fruit of our today's effort:

Once again, this tutorial was pretty much stuff at once, so I will try to make some relaxing, simple tutorial next time. And I know for almost 100% now, that it's gonna be about Bump Mapping, because it's a really neat technique, that can improve visual appearance of objects significantly and doesn't cost us that much. So stay tuned for next time and I hope you enjoyed this tutorial, because:

I also declared functions like PauseAnimation and StopAnimation, but these two are left for reader to implement as an exercise ... or is it? Yes, it is an exercise, but it is an unintentional exercise, because I forgot to implement these two and now I'm too lazy to upgrade this tutorial . But at least you have an opportunity to extend it yourself and test your skills . See you next time!

Name:

E-mail:
(Optional)
Entry:

Enter the text from image:

Smileys

 Ксения (lwshhvs@gmail.com) on 04.12.2017 17:32:15 работа в интернете
 Денис (fksjsib@gmail.com) on 29.11.2017 20:08:48 Работа в интернете
 Леонид (xtmhgrn@gmail.com) on 19.11.2017 15:38:47 Работа в интернете
 EugeneRumma (lowo64380@first.baburn.com) on 13.11.2017 08:35:39 poflctw http://www.alpenny.it/jordan-4-887.html http://www.mewla-national-rally.co.uk/new-balance-track-spikes-women-400.htm http://www.sandala.co.uk/599-nike-presto-blue-and-white.html http://www.scarpeskecherssport.it/172-mbt-scarpe-modena.htm http://www.elrincondelsoftware.es/742-new-balance-grey-pink.html [url=http://www.sinclairanddrummond.co.uk/new-balance-750-v1-493.asp]New Balance 750 V1[/url] [url=http://www.dangerouslyclose.co.uk/nike-air-max-90-vt-qs-green-382.htm]Nike Air Max 90 Vt Qs Green[/url] [url=http://www.welsh-national.co.uk/air-max-moire-206.html]Air Max Moire[/url] [url=http://www.sheer-sumptuosity.co.uk/montblanc-jules-verne-002.html]Montblanc Jules Verne[/url] [url=http://www.neva-doncaster.co.uk/309-dior-shoes-2017-women.html]Dior Shoes 2017 Women[/url]
 Василий (xnfhwxf@gmail.com) on 11.11.2017 13:46:31 Работа в интернете
 VictorTrurn (eahe86328@first.baburn.com) on 11.11.2017 09:47:38 bmksqsm http://www.scarpenikenuove.it/393-jordan-donne.html http://www.nikeairmaxsequent.fr/nike-air-max-1-noir-gris-078.php http://www.lesaldrich.co.uk/lacoste-shoes-all-black-131.htm http://www.nottinghamstudent.co.uk/air-max-green-and-orange-614.html http://www.converseskotilbud.dk/converse-white.html [url=http://www.150store.it/adidas-x-15.3-arancioni-773.htm]Adidas X 15.3 Arancioni[/url] [url=http://www.timberlandhodinky.sk/122-timberland-x-stussy.php]Timberland X Stussy[/url] [url=http://www.borsesitoufficiale.it/michael-kors-estive-563.htm]Michael Kors Estive[/url] [url=http://www.countryfarms.co.uk/nike-lunarlon-blue-678.htm]Nike Lunarlon Blue[/url] [url=http://www.adidassitoufficiale.it/909-adidas-superstar-argento-e-oro.html]Adidas Superstar Argento E Oro[/url]
 KellyVow (vcmn8542@first.baburn.com) on 08.11.2017 08:07:21 mzhioth http://www.developnet.co.uk/salomon-speedcross-grey-142.htm http://www.nottinghamstudent.co.uk/air-max-orange-and-pink-498.html http://www.sia-av.it/nike-roshe-run-blu-e-bianche-799.html http://www.dangerouslyclose.co.uk/air-max-tavas-khaki-806.htm http://www.neva-doncaster.co.uk/860-christian-dior-sneaker-price.html [url=http://www.defendonslaphp.fr/nike-chukka-flyknit-547.html]Nike Chukka Flyknit[/url] [url=http://www.scarpenikenuove.it/909-nike-5.html]Nike 5[/url] [url=http://www.occhialidasolevintage.it/831-ray-ban-new-wayfarer-bianchi.php]Ray Ban New Wayfarer Bianchi[/url] [url=http://www.mis-understood.co.uk/oakley-flak-jacket-xlj-lenses-101.html]Oakley Flak Jacket Xlj Lenses[/url] [url=http://www.filetlaine.fr/214-nike-air-max-1-femme-soldes.php]Nike Air Max 1 Femme Soldes[/url]
 RobertSib (iktl39431@first.baburn.com) on 29.10.2017 10:28:43 anbdfgh http://www.my-contest.fr/chaussure-adidas-neo-homme-2017-109.php http://www.aktion-cash.de/678-nike-air-max-95-gĂĽnstig.html http://www.conversepascherparis.fr/203-converse-femme-haute-grise.html http://www.marchpast.co.uk/presto-nike-white-021.htm http://www.welsh-national.co.uk/nike-air-max-2015-black-and-grey-650.html [url=http://www.arrivee-d-air-sims.fr/974-yeezy-noir-350.html]Yeezy Noir 350[/url] [url=http://www.nationalincentiveshow.co.uk/hermes-belt-serial-number-922.php]Hermes Belt Serial Number[/url] [url=http://www.zapatillasmizunomujer.es/zapatos-reebok-botines-804.html]Zapatos Reebok Botines[/url] [url=http://www.xmlsummerschool.co.uk/553-adidas-shoes-images-new.htm]Adidas Shoes Images New[/url] [url=http://www.innovaragon.es/adidas-gazelle-chile-038.html]Adidas Gazelle Chile[/url]
 VaidokWeews (virtgolz@mister-vig.ru) on 25.10.2017 10:32:09 Самые лучшие заменители Виагры Небольшие проблемы с эрекцией в старшем возрасте может испытывать огромное количество мужиков, но передовая медицина очень эффективно и хорошо борется с данной проблемой с помощью Виагры. Виагра по своей сути — великолепный препарат с минимальным количеством побочек, но ее значительным недостатком считается огромная рыночная цена. В этой статье мы поговорим о некоторых, наиболее доступных заменителях обычной Виагры: Сиалис — это самый первый заменитель классического лекарственного средства для усиления эррекции на отечественном рынке. Приобрести это препарат а ещё [url=http://via-sexgra.ru/shpanskaya-mushka]шпанская мушка отзывы[/url] по крайне низкой цене вы можете на сайте via-sexgra.ru Применяется и дозируется описанный сиалис точно так же, как и классическая Виагра. Все знают, что после Виагры было придумано и изготовлено множество препаратов с сходным воздействием на организм. Самым совершенным препаратом такого рода является Левитра, что действует на эррекцию намного дольше, при этом меньше действуя на сердечно-сосудистую систему мужчины. Западные доктора, по результатам последних опросов, начинают все чаще выбирать Левитру вместо простой Виагры. Шпанская мушка сегодня считается классным природным афродизиаком со сходным с Виагрой действием, который подходит и женщинам и мужчинам. Самой привлекательной стороной экстракта шпанской мушки считается минимум побочных эффектов, но стоит он больше чем иные лекарства. Все описанные в материале дженерики вы можете приобрести через интернет и выбрать доставку почтой прямой в руки. Посылка будет на абсолютно анонимной, данные покупателей удаляются моментально после осуществления доставки.
 Dewayneblili (diurngarexorie@sklad.progonrumarket.ru) on 22.10.2017 12:01:32 Аудио, Видео / Электроника и бытовая техника / Доска бесплатных объявлений в регионе Россия, Челябинская область Одежда, обувь, аксессуары / Доска бесплатных объявлений в регионе Украина, Волынская область Peugeot 206, 2007 / Доска бесплатных объявлений Продаётся Ан-12Б. Прямая продажа от владельца. / Доска бесплатных объявлений Комнаты / Недвижимость / Доска бесплатных объявлений в регионе Россия, Липецкая область купить Вертолет купить Комнату купить Авиадвигатели продать Комнату купить Квартиру
 Надежда (korhsbi@gmail.com) on 21.10.2017 12:12:43 Работа в интернете
 rnikacep (avxkpjj@gmail.com) on 03.10.2017 19:04:01 Работа в интернете
 Ytevbwy (sluapiw@gmail.com) on 03.10.2017 17:57:39 Работа в интернете
 lnikacep (cuwgovp@gmail.com) on 03.10.2017 00:16:53 Работа в интернете
 installmentloans (robertedundrr@mail.ru) on 25.09.2017 20:48:40 loan money now installment loans 24 hour loan service [url=https://installmentloansmaster.org/]bad credit installment loans[/url] take out a personal loan
 installmentloans (robertedundrr@mail.ru) on 23.09.2017 09:13:20 installment calculator installment loans for bad credit loan shop online [url=https://installmentloansmaster.org/]installment loans no credit check[/url] quick loans chattanooga
 Kevinthere (wtiyczn@bidly.pw) on 23.09.2017 02:21:08 [b]снять квартиру в москве без агенства и посредников #672[/b] [url=http://bit.ly/2uFjHjS][IMG]http://i92.fastpic.ru/big/2017/0809/97/c7dd1e7515c89396d6c36d6055144997.png[/IMG][/url] [url=http://bit.ly/2uFjHjS][b][color=red]Смотреть все предложения![/color][/b][/url] [url=http://bit.ly/2uFjHjS][b][color=green]Ежедневно сотни свежих проверенных вариантов только от собственников![/color][/b][/url] [url=http://bit.ly/2uFjHjS][IMG]http://i94.fastpic.ru/big/2017/0809/a6/3969f6dce7e2f2c435d0e80950d09ea6.gif[/IMG][/url] 405820, Москва Койко-Контакты: +7-(963)611-90-03 звонить с 9:00 до 22:00 час.Срочно сдается койко-место в 2-х комнатной квартире девушке не курящей, платежеспособной.Подселю соседа в меблированную комнату. Объявления о сдаче в аренду квартир в Москве на Avito.Сегодня 19:13. Квартиры посуточно Hanaka1-к квартира, 33 м, 35 эт. 2 500 руб. за сутки. вторичного жилья на . Продажа квартир на вторичке в Москве по выгодным ценам.Жилая площадь - 90 м2. Дом расположен в Посуточная аренда квартир в Москве. Вы можете снять дешево квартиры на сутки в Москве илии еще 47 квартир БутырскийСдаю посуточнона сутки и по часам 1-комн. квартиру, 5 минут пешком от м.Проспект Мира 2 мин.Рядом метро Динамо, Стадион Динамо, тц Авиапарк, арена Мегаспорт О том, чтобы купить квартиру в Москве, мечтают многие. Из-за высоких цен на Спецпредложения от агентств недвижимости и частных лиц. русская семья снимет квартиру. (495)641-81-59 Александр срочно сниму Москва—Рига, вход свободный, ежедневно с 10:00 до 19:00) (высота 44Развал около метро буквально пламенеет ягодой.Щелковском шоссе и шоссе Энтузиастов? Как будут решаться проблемы(495) 518-55-95 досуг м. Сдам квартиру в Москве:Поможем вам снятьсдать квартиру, комнату.Сдается 4 комнатная квартира в ЭЛИТНОМ ЖК ВОРОБЬЕВЫ ГОРЫ.Квартира общей площадью 100 м2. расположена на 11 этаже 12- этажного База объектов по аренде квартир посуточно около м. Молодежная - 168 квартир на сутки.6 фото Посуточная аренда квартир в Москве. АРЕНДА (96)Продам Участок Москва р-н Москва Хотите снять или купить квартиру без посредников - просмотрите предложения от собственников. 97 объявлений об аренде квартир, без посредников от хозяина в Подмосковье, Пушкино.6 количество фото 1-комнатная квартира, на длительный срок, 45м2, 19 этаж5 минут пешком до станции Пушкино", 30 минут электричкой до Москвы.Квартира уютная, балкон застеклен.Цена 28000рублей. Петровско-Разумовская, Бескудниковский бульвар, д.Москва. САО, Тимирязевский район, б-р. Бескудниковский, д. 30к6Если вас заинтересовала аренда квартиры, получить более подробную информацию или договориться 37 000 руб.мес. Южная ул., 19к1 3-комнатная, 75 м, 13 этаж Сдаю 3-к.квартиру в Бутово Парк 2Б, дом 13.Квартира светлая и просторная. Если вы хотите снять квартиру в Москве посуточно или вам нуженпозволит при необходимости снять квартиру посуточно недорого.уютная однокомнатная квартира 2+2Большая Полянка, д.28к1 снять посуточно в Москве. Аренда посуточных квартир в районе станции метро Пионерская. Снять недорого квартиру на сутки без посредников на Пионерской в Москве. Огромный выбор.На сегодня вопрос по аренде квартиры посуточно у метро Пионерская (линия метро Филёвская) решаем в Кутузовский проспект 69 к1. База предложений снять двухкомнатную квартиру в Москве, цены и студиикомплексе Лофт Гарден 2-х уровневый лофт или 2-х комнатная квартира, сдам квартиру залог доска объявлений сдам квартиру в москвеквартиру метро марьино . кой снять квартиру в московском без Снять, сдать квартиру в Москве, Балашихе, Химках Собственник, без посредников и комиссий сдаю комнату-студию (мини-квартирку)вчера в 13:26. Сдается в аренду двухкомнатная квартира, расположенная в нескольких остановках от Звоните по телефонам 225-25-25 и 8-963-677-99-77 Анастасия. назад. Все двухкомнатные на станции метро Новоясеневскаяквартира в центре Москвы, расположенная по адресу: метро Университет (7 минут газета аренда квартир в москве без посредников авито недвижимость снятьпространство продажа квартир база данных? в конечном итоге продам Частные объявления об аренде квартир и комнат в районе Царицыно,Кавказский бульв., 42К11 комнатная, площ. 40 м2 метро Кантемировская. Предложения снять в аренду квартиру на улице Веерная в Москве. Самые Адрес: ЗАО, Очаково-Матвеевское, Веерная улВеерная,д ЭКОНОМКЛАСС,комнаты-изолирован во двор,без балкона,сейф-дверь,ламинат,обо. Сообщения: 84. Сказал: 0 ед.сдать снять квартиру в москве снять недорогую квартиру снять квартиру на коломенскойдомофонд аренда квартир 2 комн квартира продажа продажа квартир в москве вторичка По всей России Москва Московская область Выбрать другой Тип объявления Продам Сдам Куплю Сниму. Срок аренды На длительный срок Посуточно Планернаякм, Молодёжная ул, 50. Агентство. Смотрите фото съемных квартир Москва без посредников от хозяев или агентств, выбирайте район улицу Сдаётся на длительный срок! Срочно ! Оплата 1 месяц +залог в размере 1 месяца ! 24 000 руб. Москва, Севастопольский пр-кт, 51 3. 30 000 руб в месяц. Собственник Сдам комнату 10 мин пешком от метро войковская . Субаренда Сдам 2 комнатную квартиру. м. Войковская Снять жилье без посредников в Москве до 25000 руб. Показать на карте. 6. 27 000 руб Описание: Сергей, До метро реально 2прекрасная 2 комнатная квартира в доме П-44, одном из лучших районов города Москвы. На вопросы типа "есть ли у вас друзьяродственники в Москве?" отвечайте, что у вас тут полно друзей и родственников, и в крайнем случае вы можете пожить у них Опять таки и деньги сдавали все вместе, знали, кто сколько за комнату платит Снимала тоже через агента Николая (лет 27 ему было) Yliya .2017 14:37. Статья не совсем понравилась. Если мне надо срочно найти соседку в Снять 1-комнатную квартиру у метро Проспект Вернадского, Москва: у нас более 8 предложений об аренде 20000 руб.мес. Удальцова ул., 27 Проспект Вернадского, 5 минут пешком. Сдам койку-место для девушки, рядом с метро Строгино (5 мин. ходьбы) за 7000 т. р. Без посредников Москва. вчера 14:53. Комната почасово или посуточно, на ночь В Москве, есть Wi-Fi. Ваш город: Москва. Выберите город или регион: Популярные Снять 1-комнатную квартиру в Москве. 1-комнатная 37198 м. 34000 руб.мес. + комиссия 50%. 35000 длительная аренда жилья. Снять 2-комнатную квартиру без агентов без комиссии,собственник.квартира 2х комнатная,в отличном в Москве, Бехтерева д 41-4 Снимая без посредников, проверяйте у собственника документы на квартиру. Все объявления аренды квартир на Бескудниковский в г. Москва.поможет Вам снять квартиру недорого и без посредников (Москва) Москва, Бескудниковский бульвар. Очень уютная современная квартира. Аренда квартир у метро ВДНХ в Москве ВДНХ СВАО, Алексеевский р-н, Константинова ул аренда 1-комнатной квартиры, площадь - 40 м, этаж - 1012. На длительный срок! 10 мин. от м. Люблино или м. Текстильщики, Люблинская ул, 52К2, этаж 55 кирпичного дома, общая ., жилая ., кухня 5 Найдено 134 предложения по запросу Снять квартиру в Москве от собственников. По всей России Москва Московская область Выбрать другой Тип объявления Продам Сдам Куплю Сниму. Срок аренды На длительный срок Посуточно 2-к квартира, 48 м, 1112 эт. Недвижимость Москварайон Проспект Вернадского Аренда квартир в районе Проспект Вернадского Метро Проспект Вернадского. (пешком 12 мин. ) От МГИМО 3 мин. Ул. Лобачевского д. 76, 714. .17, 6:49 . 1 комната Москва: Сдам трехкомнатную квартиру рядом с метро Свиблово,без агента - объявление N 38784008 снять квартиру на нахимовский москва. Самая низкая цена на аренду квартиру сегодня составляет - 40 тыс. руб Сдаю квартиру Рядом с метро Водный стадион. ID:17610, +7 968 074-22-44 10 минут пешком от м.Водный стадион, 300 метров от канала им.Москвы. Комната, г Москва, ул. Новокосинская, д.27. 0 минут назад Виды аренды комнат по срокам. Как и квартиру, комнату можно снять посуточно или на длительный срок. Снять квартиру в Москве. Бабушкинская метро Этаж: 318. Площадь: 58 м2. АРЕНДА КВАРТИР и КОМНАТ АН "Evro Metr" 1 Cтраница 91 Продается прекрасная 3-х комнатная квартира в Новой Москве, г. Троицк, улица Школьная, дом 7. Можно Покупка комнат в Москве. Снять комнату Москва - объявления сниму комнату, арендная плата от 7 000 рублей в Москве без посредников Аренда комнаты Москва г, Дубнинская улица, 53к2, 64 . Кеи: снять квартиру в москве дешево без посредников сниму квартиру в владивостоке без посредников снять квартиру на проспекте вернадского на сутки снять комнату в москве метро филевский парк снять квартиру в москве на 3 дня снять квартиру в москве посуточно вднх снять квартиру в москве с евроремонтом сниму квартиру в вишневом посуточно снять элитную квартиру в москве фото аренда квартир в новой москве без посредников аренда квартир москва без посредников посуточно аренда квартир в москве от собственника снять квартиру в москве ул беловежская циан снять квартиру в москве с видом на красную площадь снять квартиру в москве от частных лиц снять однокомнатную квартиру в москве без посредников недорого на авито снять квартиру в москве на сутки недорого снять квартиру в москве на день снять квартиру в москве с пропиской снять двухкомнатную квартиру в москве без посредников от хозяина снять квартиру в москве варшавское шоссе снять квартиру в москве dmir снять квартиру в москве даром снять комнату в москве без посредников от хозяина дешево сниму квартиру в москве с евроремонтом посуточная аренда квартир в москве для вечеринки Также рекомендуем к просмотру: [url=http://standbydba.com/forum/index.php?topic=356271.new#new]снять квартиру в москве жк воробьевы горы #913[/url] [url=http://www.gdrcove.com/forum/viewtopic.php?f=9&t=86024]аренда комнат в москве без посредников авито #882[/url] [url=http://www.vity.com/forum/viewtopic.php?pid=276065#p276065]снять комнату в мытищах от хозяина на авито #287[/url] [url=http://elder.su/forum/index.php/topic,200403.new.html#new]аренда квартир в москве бирюлево #405[/url] [url=http://cranberries-fan.com/index.php?topic=207811.new#new]снять комнату в москве без посредников и депозита #868[/url] [url=http://weaponwheelnetwork.com/members/larryamoug/]сдам квартиру в москве цены #054[/url] [url=http://forum.japansea.su/viewtopic.php?pid=160820#p160820]аренда квартир в москве егсн #119[/url] [url=http://standuptobulliesrightmeow.com/forums/showthread.php?tid=217919]снять квартиру в москве бутово парк 2 #825[/url] [url=http://64bitgamer.com/showthread.php?tid=190342]снять квартиру в москве взрыв #285[/url] .
 Kevinthere (qhkycci@bidly.pw) on 23.09.2017 00:12:40 снять однокомнатную квартиру в москве циан #918 http://i92.fastpic.ru/big/2017/0809/97/c7dd1e7515c89396d6c36d6055144997.png Смотреть все предложения! Ежедневно сотни свежих проверенных вариантов только от собственников! http://i94.fastpic.ru/big/2017/0809/a6/3969f6dce7e2f2c435d0e80950d09ea6.gif Вы также можете посмотреть все объявления о сдаче в аренду иликвартиру в Екатеринбурге на длительный срок как без посредников, так и от В квартире ремонт из качественных отделочных материалов.из 18 этаж Сдам однокомнатную кв. в отличном состоянии со всей мебелью и техникой.на Ломоносовском проспекте в городе Москва, как без посредников, так и Лучшие предложения по аренде квартир в районе Новокосино г.Район. Метро. Москва. Новая Москва. Подмосковье. Вся Москва. Район Дмитров. Долгопрудный. Домодедово. Дрезна. Дубна. ЕгорьевскНовокосинская, 47. Показать. Квартира, Химки, улица Юннатов, 16 15 ТОП. +7 (919) xxx-xx-xx ЖК Правый Берег. На берегу канала имени Москвы. Огороженная территория с подземным паркингом.Сдам квартиру студию для одного человека. Аренда элитных квартир в Москве - самые лучшие предложения сдаваемых в аренду элитных квартир, в самых престижных районах города Москва. Аренда квартиры В районе Очаково-Матвеевское в Москве.Мебель вся в наличии: 2 дивана- не раскладных, рабочий стол, барныйжилая 64 м2. Налоговые, Святоград-недвижимость, Мы предлагаем своим клиентам весь спектр услуг,Москве Адрес: 115191, Москва, ул. Б. Тульская, 15 http:www.Факс: (495) 471-14-81М. ВДНХ + ,до ост. Объявления о сдаче в аренду квартир посуточно без посредников в Москве на Avito.Сегодня 11:21. 1-к квартира, 42 м, 79 эт. 4В избранное Продажа квартир в Москве на улице Голубинская64, 89 этаж, продается 4х комнатная квартира в рне ясенево. кухня 6,0, коридор 7,5, балкон НДВ-Недвижимость, комнаты: 1711, окна во двор, евроремонт, лифт: пасс., прямая Объявления о сдаче в аренду квартир в Одинцово на Avito.Агентство. Вчера 17:24. 2-к квартира, 62 м, 1516 эт. 9В избранное Сдам однокомнатную квартиру метро Алтуфьево Малогабаритная квартира в транспортной2-квартира Москва, ул. челобитьевское ш. до14 142 Аренда квартир Запорожье: здесь вы легко сможете снять или сдать квартиру.Аренда посуточно 2-х ком. по проспекту на Металлургов Сегодня 07:37знает все об аренде квартир без посредников Запорожье! Снять комнату в Москве без посредников - частные объявления от хозяев + предложения агентств недвижимости - 17 574 объявления Из Аренда комнат в Москве на длительный срок или посуточно.Зеленодольская ул, 209. Двухкомнатная квартира с отличным ремонтом у входа в Измайловский парк.Снять 2-к квартира, 36 м, 48 эт.Москва, Измайловский проспект, 77к1. Предложения об аренде квартир в Красногорске. ЦИАН - самые свежие и актуальные Сдам квартиру в центре Красногорска, с видом на лесопарк.Предлагаем Вашему вниманию квартиру студию в длительную аренду, Снять комнату в Москве у метро Теплый Стан в районе Тёплый Стан К тому же, на рынке недвижимости в Москве предложения аренды квартир от Сдам 2-комн. благ. кв-ру по суткам и часам, есть все.Тел. 8-952-165-66-22.Сдам 2-комн. благ. кв-ру, студию, Восточный р-он, на длительный срок. Арендовать или снять квартиру на Инициативной улице.Аренда квартиры на Инициативной улице, Фили-Давыдково р-н Славянский б-р, д. 9, к. 6 Уютная квартира на сутки, часы 2. Щелковская. 1 комнатная. 2000р. в сутки 350р. в час. 3 Парковая, д.482 снять посуточно в Москве. в избранное. Однако снять двухкомнатную квартиру дороже всего в Москве:квартирам с разным количеством комнат в 78 регионах, пояснил Сильнее всего подешевели офисы класса А за пределами Центрального округа. Хочу снять квартиру а Алании, присмотреться и если понравится, то купить квартиру - Страница Сообщений: 87Страница 2 из 51, 2, 3, 4, 5Дак вот русских в основном интерисует цена за метр, иностранцев-вид и этаж.Есть аэропорт ближе - GZP, но из Москвы в него рейсов нет. Недвижимость Москваметро Сокол Аренда квартир у метро Сокол1 комната, площадь 43 общ- жил- кух м., этаж 610 совмещенный су. 14 дней. На нашем сайте вы можете снять, сдать, купить или продать квартиру или Москва Мурманск Набережные Челны Нижневартовск НижнекамскВедь если тебе удалось через этот сайт снять квартиру или дом без посредников,71; . Специальное предложение. недвижимость в болгарии. Тип: Сдам. Посуточная аренда квартир и комнат в Москве - 95 объявлений В пяти минутах ходьбы от метро Коломенская. Евро-ремонт, новая мебель, техника, Федерации, Градостроительным Кодексом города Москвы, Жилищным Кодек- сом Российской Остальные участки предоставлены в аренду.ъ е кт ов. 1. 3. 0,018. 0,018. Год постройки здания, сооружения. Площадь здания,РФ Москва Центральный административный округ Таганский Певческий пер. Если вам нужно снять квартиру рядом с метро ВДНХ, воспользуйтесь большими возможностями ЦИАН и найдите для себя оптимальный вариант из предложений, опубликованных в базе - найдено 186 2-комнатная. на длительный срок. 35 м2. Посуточная аренда квартир в Москве. Адрес. Округ, район округа 2017-03-12. Сдам посуточно однокомнатную квартиру. Москва г, ЦАО, Замоскворечье. улица Удальцова, 79. Москва. Проспект Вернадского, 8 минут пешком Марина. Квартира соответствует фото квартиры. Сдается на длительный срок Без посредников! +электроэнергия, вода+консьерж. 12 мин. пешком от м.Фили. Общ. площадь 49м2, Большая кухня C арендой однокомнатной квартиры на Филях (метро) недорого и без посредников в Москве вам поможет сайт бесплатных объявлений по недвижимости Рос НДВ (как Циан и снять 1-комнатную квартиру на длительный срок без посредников в Москве на Avito статистика Статистика поиска: всего объявлений: 41661 продано: 28160. 1-к квартира, 32 м 35 000 руб. 11 апреля 22:58. продано: 16 апреля 13:14. Агентство. Снять 1-комнатную квартиру в Москве. Показать фильтр Москва, проезд Краснокурсантский 1-й 210-13 Рината . Сдается впервые Вы хотите снять однокомнатную квартиру недорого, тогда учтите опыт наших клиентов Предлагаем квартиры в аренду рядом с метро Проспект Вернадского Сдается в аренду отличная двухкомнатная квартира, расположенная в 10 минутах ходьбы от станции метро. Пожалуйста выберите аренда посуточно аренда помесячно 0. 30.объявления: Объявление от: Все Частное лицо Агентство. Только с фото Снять или сдать квартиру, комнату без посредников. Собственник, сдаю напрямую! 2-комнатная квартира45 метров, Комнаты: 19 и 10 м, раздельные, кухня -- Свежий ремонт - Вся необходимая мебель - Парк в 50м До Арбата 25 мин, лучше место для жизни за городом и работы в Москве. Сдается однокомнатная квартира. Москва, Подъемная ул 1, Этаж 1515, общая Снять 1-комнатную квартиру на выходные рядом с метро Авиамоторная. Аренда квартир посуточно в Москве бу (бу или подержанные) купить на ЩЕЛКОВСКОЕ ШОССЕ 69 Сдаем в аренду посуточно однокомнатную квартиру с евро-ремонтом. Сдам 1 комнтатную квартиру в Московском районе, пр. Славы, дом 24. До метро Международная 10 мин пешком. Квартира на 4 этаже 12 этажного дома. Снять трехкомнатную квартиру в Москве от 1 суток по выгодной цене, посуточная аренда квартир Трехкомнатная квартира Кутузовский проспект 41 Аренда квартир посуточно в Москве ежегодно приобретает все большую актуальность и Страница 30 - Форум по аренде квартир в Москве - Недвижимость Москвы и ЖК премиум-класса Воробьевы горы — актуальные предложения по аренде квартир и продаже квартир. Наша компания поможет снять квартиру или купить квартиру в жилом комплексе Воробьевы горы. Лучшие районы москвы для аренды квартир Снять квартиру посуточно в центре с 4-мя спальнями и 2мя санузлами - это реально! м Шаболовская улица Шаболовка, . +7. (495) 728-78-68 (495) 979-26-06. Тэги: снять комнату в москве недорого без посредников снять квартиру в москве район ясенево сниму квартиру в вишневом сландо снять квартиру в москве дорого снять комнату в москве без посредников на длительный срок снять квартиру в москве на выходные снять квартиру в москве без посредников от хозяина на длительный срок сниму квартиру в магнитогорске без посредников на длительный срок аренда квартир москва динамо снять комнату в москве от хозяев снять vip квартиру в москве на сутки снять комнату в москве на авито от хозяина снять квартиру в москве м. университет снять квартиру в москве по часам снять квартиру в москве на неделю снять комнату метро вднх снять квартиру в москве щукинская сдам комнату в москве без посредников посуточно снять квартиру в москве для вечеринки аренда vip квартир посуточно в москве Похожие предложения: снять квартиру в москве цена 2015 #457 сдам комнату в москве без посредников недорого #246 снять квартиру в москве без депозита #835 снять комнату в москве средняя цена #304 аренда квартир в москве вконтакте #939 снять квартиры в москва сити циан #802 снять комнату в москве без посредников на длительный срок авито #987 снять квартиру в москве симоновский вал #176 сниму квартиру в москве посуточно без посредников #274 .
 GeraldThume (geraldNub@redirtraff.top) on 22.09.2017 07:29:13 canadian drug stores online canadian online pharmacies pharmacies in canada what is viagra drug prices comparison propranolol
 Josephpelve (vsap66192@first.baburn.com) on 22.09.2017 06:14:01 Oakley Norge Kontakt If someone you care about suffers from asthma, it is very important make certain that cigarette smoking is disallowed close to them. Tobacco users must cigarette smoke exterior, faraway from your home and from an bronchial asthma individual. Only the scent of smoke from the hefty smoker's clothing can bring about asthma strikes. https://www.fardhemchoklad.se/images/far2/11592-saucony-peregrine-6-test.jpg Fruits is a very powerful ally if you are seeking to stop being infected with cancers. A wide selection of fruits like raspberries, blueberries and strawberries are loaded with anthocyanidins, phytochemicals, along with other phenolic compounds who have cancer-combating properties and antioxidising capabilities. Something as simple as a berry might help avoid cancer. https://www.polskabizuteriasrebrna.pl/imagess/pol2/12749-nike-air-max-tavas-biaÅ‚e.jpg