ok

img

BVH

Lettore animazioni bvh.
Il progetto si compone di una libreria generica in grado di interpretare l'animazione e di visualizzarne il risultato con un metodo demo.
Oltre alla libreria il progetto contiene un'applicazione che consente di applicare e visualizzare le animazioni della Bandai-Namco, che, non si basano, come le bvh classiche, sulla T-pose.

//
// bvh.h
//
#ifndef animation_h
#define animation_h

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

class bvh
{
	private:
	
		struct Bone
		{
			std::string name;
			unsigned int id;
			float offset[3];
			unsigned short n;
			std::string channels;
			unsigned int hierarchy;
			unsigned int parent;
			std::vector <unsigned int> children;
			bool noRot;
			std::vector <float> data;
		};

		float time;
		unsigned int frames;
		float frameTime;
		std::string load(std::string filename);
		unsigned int parent(unsigned int id, unsigned int hierarchy);
		void parseHierarchy(std::string str);
		void parseMotion(std::string str);

	public:
		
		bvh(std::string filename);
		~bvh();
		bool active;
		std::vector <Bone> bone;
		void demo();
		unsigned int getFrames();
		float getFrameTime();
		unsigned int play();
		void rotate(unsigned int frame, unsigned int id);
		void show();
};
#endif

//
// bvh.cpp
//
#include "bvh.h"
#include <GL/freeglut.h>
// https://github.com/BandaiNamcoResearchInc/Bandai-Namco-Research-Motiondataset/find/master


bvh::bvh(std::string filename)
{
	this->active = true;
	this->time = 0;
	std::string doc = this->load(filename);
	int p = doc.find("MOTION");
	std::string hierarchy = doc.substr(0, p);
	std::string motion = doc.substr(p);
	this->parseHierarchy(hierarchy);
	this->parseMotion(motion);
}

bvh::~bvh()
{
}

void bvh::demo()
{
	glPointSize(8);
	glLineWidth(3);
	for(unsigned int id = 0; id < this->bone.size(); id++)
	{
		glPushMatrix();
		glTranslated(this->bone[id].offset[0], this->bone[id].offset[1], this->bone[id].offset[2]);
		this->rotate((unsigned int)time, id);
		glBegin(GL_POINTS);
		glVertex3f(0, 0, 0);
		glEnd();
		for(unsigned int k = 0; k < this->bone[id].children.size(); k++)
		{
			glBegin(GL_LINES);
			glVertex3f(0, 0, 0);
			glVertex3fv(this->bone[this->bone[id].children[k]].offset);
			glEnd();
			glBegin(GL_POINTS);
			glVertex3fv(this->bone[this->bone[id].children[k]].offset);
			glEnd();
		}
		int u = -1;
		if(id == this->bone.size() - 1)
		{
			u = this->bone[id].hierarchy;
		}
		else
		{
			u = this->bone[id].hierarchy - this->bone[id + 1].hierarchy;
		}
		if(u > 0)
		{
			for(unsigned int k = 0; k < (unsigned int)u + 1; k++)
			{
				glPopMatrix();
			}
		}
	}
	this->play();
}

unsigned int bvh::getFrames()
{
	return this->frames;
}

float bvh::getFrameTime()
{
	return this->frameTime;
}

std::string bvh::load(std::string filename)
{
	std::ifstream in;
	in.open(filename, std::ios::in);
	if(in.is_open())
	{
		std::string s, tp;
		while(!in.eof())
		{
			getline(in, s);
			tp += s + "\n";
		}
		in.close();
		return tp;
	}
	else
	{
		in.close();
		std::cout << "file: " << filename << " not found. ERROR\n";
		system("pause");
		exit(0);
	}
}

unsigned int bvh::parent(unsigned int id, unsigned int hierarchy)
{
	unsigned int i = id - 1;
	while(this->bone[i].hierarchy != hierarchy - 1) // si risale nel vettore finchè l'elemento di gerarchia hierarchy non incontra un elemento di gerarchia hierarchy - 1
	{
		i--;
	}
	return i;
}

void bvh::parseHierarchy(std::string str)
{
	std::string row;
	unsigned int id = 0, hierarchy = 0;
	Bone current;
	bool h = true;
	while(h)
	{
		int line_feed = str.find("\n");
		if(line_feed != -1) // finchè il documento ha una nuova riga
		{
			row = str.substr(0, line_feed);
			if(row.find("ROOT") != -1)
			{
				current.name = row.substr(row.find("ROOT") + 5); // viene ricavato il nome del primo elemento della gerarchia
				current.id = id;
				current.hierarchy = hierarchy;
				current.noRot = false;
				id++;
			}
			else if(row.find("{") != -1)
			{
			}
			else if(row.find("OFFSET") != -1)
			{
				std::string s = row.substr(row.find("OFFSET") + 7); // vengono estrapolati i 3 valori dell'offset
				current.offset[0] = atof(s.substr(0, s.find(" ")).c_str());
				s = s.substr(s.find(" ") + 1);
				current.offset[1] = atof(s.substr(0, s.find(" ")).c_str());
				s = s.substr(s.find(" ") + 1);
				current.offset[2] = atof(s.substr(0, s.find(" ")).c_str());
			}
			else if(row.find("CHANNELS") != -1)
			{
				std::string s = row.substr(row.find("CHANNELS") + 9);
				unsigned short n = atoi(s.substr(0, s.find(" ")).c_str()); // numero di canali (3 o 6)
				s = s.substr(s.find(" ") + 1);
				if(n == 6)
				{
					s = s.substr(30); // salta le stringhe XYZposition
				}
				current.n = n; // viene salvato anche il numero di canali
				current.channels += s[0]; // la stringa channels contiene l'ordine di rotazione
				s = s.substr(10);
				current.channels += s[0];
				s = s.substr(10);
				current.channels += s[0];
			}
			else if(row.find("JOINT") != -1)
			{
				this->bone.push_back(current);
				std::string s = row.substr(row.find("JOINT") + 6);
				current.name = s;
				current.id = id;
				current.channels = "";
				hierarchy++;
				current.hierarchy = hierarchy;
				current.noRot = false;
				current.parent = this->parent(id, hierarchy);
				id++;
			}
			else if(row.find("End Site") != -1)
			{
				this->bone.push_back(current);
				current.name = "End Site";
				current.id = id;
				current.channels = "";
				hierarchy++;
				current.hierarchy = hierarchy;
				current.noRot = true;
				current.parent = this->parent(id, hierarchy);
				id++;
			}
			else if(row.find("}") != -1)
			{
				hierarchy--;
			}
			str = str.substr(line_feed + 1); // viene eliminata la riga appena letta
		}
		else // arrivato alla fine del documento
		{
			this->bone.push_back(current); // viene salvato il dato corrente
			for(unsigned int id = 1; id < this->bone.size(); id++) // ricava i figli di ogni elemento 
			{
				this->bone[this->bone[id].parent].children.push_back(id);
			}
			h = false; // e chiuso il ciclo
		}
	}
}

void bvh::parseMotion(std::string str)
{
	int line_feed = str.find("\n");
	str = str.substr(line_feed + 1); // viene eliminata la riga MOTION
	//
	line_feed = str.find("\n");
	std::string row = str.substr(0, line_feed); // viene letta la riga Frames
	std::string s = row.substr(row.find("Frames:") + 8);
	this->frames = atoi(s.c_str());
	str = str.substr(line_feed + 1); // viene eliminata la riga Frames
	//
	line_feed = str.find("\n");
	row = str.substr(0, line_feed); // viene letta la riga Frame Time
	s = row.substr(row.find("Frame Time:") + 12);
	this->frameTime = atof(s.c_str());
	str = str.substr(line_feed + 1); // viene eliminata la riga Frame Time
	//
	for(unsigned int i = 0; i < this->frames; i++)
	{
		line_feed = str.find("\n");
		row = str.substr(0, line_feed);
		for(unsigned int j = 0; j < this->bone.size(); j++) // per ogni elemento
		{
			if(!this->bone[j].noRot) // solo gli elementi che presentano dati di rotazione
			{
				if(this->bone[j].n == 6) //
				{
					for(unsigned short k = 0; k < 3; k++)
					{
						row = row.substr(row.find(" ") + 1); // sono ignorate le traslazioni
					}
				}
				for(unsigned short k = 0; k < 3; k++) // vengono salvati solo i dati delle rotazioni
				{
					s = row.substr(0, row.find(" "));
					this->bone[j].data.push_back(atof(s.c_str()));
					row = row.substr(row.find(" ") + 1);
				}
			}
		}
		str = str.substr(line_feed + 1); // elimina la riga letta e passa alla successiva
	}
}

unsigned int bvh::play()
{
	this->time += .5;
	if(this->time > (this->frames - 1))
	{
		this->time = 0;
	}
	return (unsigned int) this->time;
}

void bvh::rotate(unsigned int frame, unsigned int id)
{
	if(this->active)
	{
		if(id < this->bone.size()) // la rotazione si applica solo alle ossa contenute nel vettore
		{
			if(frame > this->frames - 1) // se viene inserito un frame oltre il limite viene richiamato il frame corrispondente
			{
				frame = frame % this->frames;
			}
			std::string p = this->bone[id].channels;
			if(p == "XYZ")
			{
				glRotated(this->bone[id].data[frame * 3    ], 1, 0, 0);
				glRotated(this->bone[id].data[frame * 3 + 1], 0, 1, 0);
				glRotated(this->bone[id].data[frame * 3 + 2], 0, 0, 1);
			}
			else if(p == "XZY")
			{
				glRotated(this->bone[id].data[frame * 3    ], 1, 0, 0);
				glRotated(this->bone[id].data[frame * 3 + 1], 0, 0, 1);
				glRotated(this->bone[id].data[frame * 3 + 2], 0, 1, 0);
			}
			else if(p == "YXZ")
			{
				glRotated(this->bone[id].data[frame * 3    ], 0, 1, 0);
				glRotated(this->bone[id].data[frame * 3 + 1], 1, 0, 0);
				glRotated(this->bone[id].data[frame * 3 + 2], 0, 0, 1);
			}
			else if(p == "YZX")
			{
				glRotated(this->bone[id].data[frame * 3    ], 0, 1, 0);
				glRotated(this->bone[id].data[frame * 3 + 1], 0, 0, 1);
				glRotated(this->bone[id].data[frame * 3 + 2], 1, 0, 0);
			}
			else if(p == "ZXY")
			{
				glRotated(this->bone[id].data[frame * 3    ], 0, 0, 1);
				glRotated(this->bone[id].data[frame * 3 + 1], 1, 0, 0);
				glRotated(this->bone[id].data[frame * 3 + 2], 0, 1, 0);
			}
			else if(p == "ZYX")
			{
				glRotated(this->bone[id].data[frame * 3    ], 0, 0, 1);
				glRotated(this->bone[id].data[frame * 3 + 1], 0, 1, 0);
				glRotated(this->bone[id].data[frame * 3 + 2], 1, 0, 0);
			}
		}
		else
		{
			std::cout << "ERROR: bone over size in rotate method...\n";
			system("pause");
			exit(0);
		}
	}
}

void bvh::show()
{
	for(unsigned int i = 0; i < this->bone.size(); i++)
	{
		std::cout << "id : " << this->bone[i].id << "\n";
		std::cout << "name : " << this->bone[i].name << "\n";
		std::cout << "hierarchy : " << this->bone[i].hierarchy << "\n";
		if(i > 0)
		{
			std::cout << "parent : " << this->bone[this->bone[i].parent].name << "\n";
		}
		else
		{
			std::cout << "parent :\n";
		}
		std::cout << "children : "; for(unsigned int k = 0; k < this->bone[i].children.size(); k++){std::cout << this->bone[i].children[k] << " ";} std::cout << "\n";
		std::cout << "offset : " << this->bone[i].offset[0] << " " << this->bone[i].offset[1] << " " << this->bone[i].offset[2] << "\n";
		std::cout << "n channels : " << this->bone[i].n << "\n";
		std::cout << "channels : " << this->bone[i].channels << "\n";
		std::cout << "\n";
	}
	system("pause");
}

// main.cpp
#include <GL/freeglut.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <string>
#include "bvh.h"

int TIME = 0;
unsigned int f = 0;

const float CAMERA_DISTANCE = 10.0f;
int screenWidth;
int screenHeight;
bool mouseLeftDown;
bool mouseRightDown;
bool mouseMiddleDown;
float mouseX, mouseY;
float cameraAngleX;
float cameraAngleY;
float cameraDistance;

bvh *myBvh;

void resizeCallback(int width, int height)
{
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0f, (GLfloat)(width)/(GLfloat)(height), 0.1f, 1000.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void boneX(float y, float z, float t)
{
	float A[3] = {0, y/2.0f,-z/2.0f};
	float B[3] = {0, y/2.0f, z/2.0f};
	float C[3] = {0,-y/2.0f, z/2.0f};
	float D[3] = {0,-y/2.0f,-z/2.0f};
	float E[3] = {t, y/2.0f, z/2.0f};
	float F[3] = {t, y/2.0f,-z/2.0f};
	float G[3] = {t,-y/2.0f,-z/2.0f};
	float H[3] = {t,-y/2.0f, z/2.0f};
	glBegin(GL_QUADS);
	glVertex3fv(A); glVertex3fv(B); glVertex3fv(C); glVertex3fv(D);
	glVertex3fv(E); glVertex3fv(F); glVertex3fv(G); glVertex3fv(H);
	glVertex3fv(B); glVertex3fv(E); glVertex3fv(H); glVertex3fv(C);
	glVertex3fv(F); glVertex3fv(A); glVertex3fv(D); glVertex3fv(G);
	glVertex3fv(A); glVertex3fv(F); glVertex3fv(E); glVertex3fv(B);
	glVertex3fv(D); glVertex3fv(C); glVertex3fv(H); glVertex3fv(G);
	glEnd();
}

void boneY(float x, float z, float t)
{
	float A[3] = { x/2.0f, 0,-z/2.0f};
	float B[3] = { x/2.0f, 0, z/2.0f};
	float C[3] = {-x/2.0f, 0, z/2.0f};
	float D[3] = {-x/2.0f, 0,-z/2.0f};
	float E[3] = { x/2.0f, t, z/2.0f};
	float F[3] = { x/2.0f, t,-z/2.0f};
	float G[3] = {-x/2.0f, t,-z/2.0f};
	float H[3] = {-x/2.0f, t, z/2.0f};
	glBegin(GL_QUADS);
	glVertex3fv(A); glVertex3fv(B); glVertex3fv(C); glVertex3fv(D);
	glVertex3fv(E); glVertex3fv(F); glVertex3fv(G); glVertex3fv(H);
	glVertex3fv(B); glVertex3fv(E); glVertex3fv(H); glVertex3fv(C);
	glVertex3fv(F); glVertex3fv(A); glVertex3fv(D); glVertex3fv(G);
	glVertex3fv(A); glVertex3fv(F); glVertex3fv(E); glVertex3fv(B);
	glVertex3fv(D); glVertex3fv(C); glVertex3fv(H); glVertex3fv(G);
	glEnd();
}

void boneZ(float x, float y, float t)
{
	float A[3] = { x/2.0f,-y/2.0f, 0};
	float B[3] = { x/2.0f, y/2.0f, 0};
	float C[3] = {-x/2.0f, y/2.0f, 0};
	float D[3] = {-x/2.0f,-y/2.0f, 0};
	float E[3] = { x/2.0f, y/2.0f, t};
	float F[3] = { x/2.0f,-y/2.0f, t};
	float G[3] = {-x/2.0f,-y/2.0f, t};
	float H[3] = {-x/2.0f, y/2.0f, t};
	glBegin(GL_QUADS);
	glVertex3fv(A); glVertex3fv(B); glVertex3fv(C); glVertex3fv(D);
	glVertex3fv(E); glVertex3fv(F); glVertex3fv(G); glVertex3fv(H);
	glVertex3fv(B); glVertex3fv(E); glVertex3fv(H); glVertex3fv(C);
	glVertex3fv(F); glVertex3fv(A); glVertex3fv(D); glVertex3fv(G);
	glVertex3fv(A); glVertex3fv(F); glVertex3fv(E); glVertex3fv(B);
	glVertex3fv(D); glVertex3fv(C); glVertex3fv(H); glVertex3fv(G);
	glEnd();
}

void axis()
{
	glLineWidth(1);
	glPushMatrix();
	glBegin(GL_LINES);
	glColor3f(1.0f, 0.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(200.0f, 0.0f, 0.0f);
	glColor3f(0.0f, 1.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 200.0f, 0.0f);
	glColor3f(0.0f, 0.0f, 1.0f);
	glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 200.0f);
	glColor3f(1.0f, 1.0f, 1.0f);
	glEnd();
	glPopMatrix();
}

void shapeBandai()
{
	glPushMatrix();
	myBvh->rotate(f, 1);
	boneX(20,10,-10); // bacino
	
		glPushMatrix();
		myBvh->rotate(f, 2);
		boneX(10,15,20); // addome
			glPushMatrix();
			glTranslated(20,0,0);
			myBvh->rotate(f, 3);
			boneX(10,30,15); // petto
			
				glPushMatrix();
				glTranslated(15,0,10);
				myBvh->rotate(f, 7);
				boneX(10,10,10); // spalla sx
					glPushMatrix();
					glTranslated(10,0,0);
					myBvh->rotate(f, 8);
					boneX(10,10,30); // braccio sx
						glPushMatrix();
						glTranslated(30,0,0);
						myBvh->rotate(f, 9);
						boneX(10,10,30); // avambraccio sx
							glPushMatrix();
							glTranslated(30,0,0);
							myBvh->rotate(f, 10);
							boneX(5,5,10); // mano sx
							glPopMatrix();
						glPopMatrix();
					glPopMatrix();
				glPopMatrix();
				
				glPushMatrix();
				glTranslated(15,0,-10);
				myBvh->rotate(f, 12);
				boneX(10,10,-10); // spalla dx
					glPushMatrix();
					glTranslated(-10,0,0);
					myBvh->rotate(f, 13);
					boneX(10,10,-30); // braccio dx
						glPushMatrix();
						glTranslated(-30,0,0);
						myBvh->rotate(f, 14);
						boneX(10,10,-30); // avambraccio dx
							glPushMatrix();
							glTranslated(-30,0,0);
							myBvh->rotate(f, 15);
							boneX(5,5,-10); // mano dx
							glPopMatrix();
						glPopMatrix();
					glPopMatrix();
				glPopMatrix();
			
				glPushMatrix();
				glTranslated(15,0,0);
				myBvh->rotate(f, 4);
				boneX(8,8,5); // collo
					glPushMatrix();
					glTranslated(5,0,0);
					myBvh->rotate(f, 5);
					boneX(10,10,10); // testa
					glPopMatrix();
				glPopMatrix();
				
			glPopMatrix();
		glPopMatrix();
		
		glPushMatrix();
		glTranslated(-10,5,0);
		myBvh->rotate(f, 22);
		boneX(10,10,-50); // gamba dx
			glPushMatrix();
			glTranslated(-50,0,0);
			myBvh->rotate(f, 23);
			boneX(10,10,-50); // tibia dx
				glPushMatrix();
				glTranslated(-50,0,0);
				myBvh->rotate(f, 24);
				boneX(5,5,-10); // piede dx
				glPopMatrix();
			glPopMatrix();
		glPopMatrix();
			
		glPushMatrix();
		glTranslated(-10,-5,0);
		myBvh->rotate(f, 17);
		boneX(10,10,50); // gamba dx
			glPushMatrix();
			glTranslated(50,0,0);
			myBvh->rotate(f, 18);
			boneX(10,10,50); // tibia dx
				glPushMatrix();
				glTranslated(50,0,0);
				myBvh->rotate(f, 24);
				boneX(5,5,10); // piede dx
				glPopMatrix();
			glPopMatrix();
		glPopMatrix();
			
	glPopMatrix();
	f = myBvh->play();
}

void displayCallback()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glPushMatrix();
	glTranslatef(0, 0, -cameraDistance);
	glRotatef(cameraAngleX, 1, 0, 0);
	glRotatef(cameraAngleY, 0, 1, 0);
	axis();
	//myBvh->demo();
	shapeBandai();
	glPopMatrix();
	glutSwapBuffers();
}

std::string readText(std::string filename)
{
	std::fstream file;
	file.open(filename, std::ios::in);
	std::string r;
	if(file.is_open())
	{
		getline(file, r);
		file.close();
	}
	return r;
}

void init()
{
	mouseLeftDown = mouseRightDown = mouseMiddleDown = false;
	mouseX = mouseY = 0;
	cameraAngleX = cameraAngleY = 0.0f;
	cameraDistance = CAMERA_DISTANCE;
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	myBvh = new bvh(readText("test.txt"));
}

void mouseCallback(int button, int state, int x, int y)
{
	mouseX = x; mouseY = y;
    	if(button == GLUT_LEFT_BUTTON)
	{
		if(state == GLUT_DOWN)
		{
			mouseLeftDown = true;
		}
		else if(state == GLUT_UP)
		{
			mouseLeftDown = false;
		}
	}
	else if(button == GLUT_RIGHT_BUTTON)
	{
        	if(state == GLUT_DOWN)
        	{
			mouseRightDown = true;
		}
        	else if(state == GLUT_UP)
		{
			mouseRightDown = false;
		}
	}
	else if(button == GLUT_MIDDLE_BUTTON)
	{
        	if(state == GLUT_DOWN)
        	{
			mouseMiddleDown = true;
        	}
        	else if(state == GLUT_UP)
		{
			mouseMiddleDown = false;
		}
	}
}

void mouseMotionCallback(int x, int y)
{
	if(mouseLeftDown)
	{
		cameraAngleY += (x - mouseX);
		cameraAngleX += (y - mouseY);
		mouseX = x;
		mouseY = y;
	}
	if(mouseRightDown)
	{
		cameraDistance -= (y - mouseY) * 0.2f;
		mouseY = y;
	}
}

int main(int argc, char **argv) 
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(0, 0);
	glutInitWindowSize(640, 480);
	glutCreateWindow("bvh");
	glutDisplayFunc(displayCallback);
	glutReshapeFunc(resizeCallback);
	glutIdleFunc(displayCallback);
	glutMouseFunc(mouseCallback);
    	glutMotionFunc(mouseMotionCallback);
	init();
	glutMainLoop();
	return 1;
}

Biografia

Mi chiamo Cosimo Saccone e sono un programmatore napoletano di 44 anni con oltre 35 anni di esperienza nella programmazione (BASIC, Assembly). Realizzo progetti e programmi utilizzando i principali e più diffusi linguaggi (C, C++, PHP, Javascript, HTML) e software per la grafica (Photoshop, Illustrator, 3dsMax). Anche se la grafica rappresenta il mio principale settore di interesse, non disdegno il lavoro di back-end e di organizzazione dati e sono attento agli aspetti di efficienza e di risparmio delle risorse tipica della programmazione di basso livello (specie nel settore della grafica 3d). Realizzo siti internet, applicativi desktop e servizi di vario tipo. Ho una buona conoscenza della libreria OpenGL per lo sviluppo di applicazioni 3d interattive in C/C++. Cerco di adottare uno stile di programmazione fortemente ordinato e modulare. Possiedo, inoltre, una buona capacità di elaborazione della documentazione. Ho vari hobbies tra cui la pittura, la ginnastica e le lunghe passeggiate in solitudine.

facebook instagram youtube
HTML5 Template create by Cosimo Saccone 2022

Al fine di migliorare l’esperienza di navigazione sul nostro sito noi di cosimosaccone.com e i nostri partner selezionati elaboriamo i dati personali, compreso l’indirizzo IP e le pagine visitate, in relazione alla tua navigazione nei contenuti del sito, per i seguenti scopi:

Accesso alle informazioni
Dati precisi di geolocalizzazione
Misurazione del pubblico
Pubblicità e contenuti personalizzati
Ti ricordiamo che il termine cookie si riferisce a una porzione di dati inviati al tuo browser da un web server. Il cookie viene memorizzato sul tuo dispositivo e riconosciuto dal sito web che lo ha inviato in ogni navigazione successiva. Se vuoi saperne di più e compiere una scelta diversa, come il rifiuto del trattamento dei tuoi dati personali, clicca qui sulla nostra privacy policy. Potrai sempre modificare le tue scelte e impostare i singolo cookies selezionando il bottone qui sotto.
OK