Загрузка данных


#include <windows.h>
#include <GL/glut.h>
#include <GL/glu.h>
#include <cmath>
#include <vector>
#include <algorithm>

// ---------- Глобальные переменные ----------
const double PI = 3.14159265358979323846;

int winW = 800, winH = 600;

// Параметры поверхности
int   nLon   = 80;      // разбиение по долготе
int   nLat   = 60;      // разбиение по широте
double R     = 1.5;     // базовый радиус

// Углы проекции
double alpha = 30.0 * PI / 180.0;  // поворот вокруг Z
double beta  = 20.0 * PI / 180.0;  // наклон камеры
bool perspective = false;          // режим проекции
double cameraDist = 8.0;           // дистанция для перспективы

// Режимы отображения
bool wireframe = false;            // каркас / заливка
bool useTexture = false;           // текстура вкл/выкл
GLuint textureId = 0;              // ID текстуры

// Освещение (позиция, компоненты)
GLfloat lightPos[]      = { 5.0f, 5.0f, 5.0f, 1.0f };
GLfloat ambientLight[]  = { 0.25f, 0.25f, 0.25f, 1.0f };
GLfloat diffuseLight[]  = { 0.9f, 0.9f, 0.9f, 1.0f };
GLfloat specularLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };

// Материал
GLfloat matAmbient[]  = { 0.1f, 0.15f, 0.35f, 1.0f };
GLfloat matDiffuse[]  = { 0.2f, 0.3f, 0.8f, 1.0f };
GLfloat matSpecular[] = { 0.7f, 0.7f, 0.7f, 1.0f };
GLfloat matShininess   = 80.0f;

// Z-буфер
std::vector<float> zBuffer;
int zbufW = 0, zbufH = 0;

// ---------- Генерация текстуры в памяти (процедурная) ----------
void generateTexture() {
    const int texSize = 256;
    GLubyte* texData = new GLubyte[texSize * texSize * 3];
    
    for (int i = 0; i < texSize; i++) {
        for (int j = 0; j < texSize; j++) {
            int idx = (i * texSize + j) * 3;
            // Шахматный паттерн с цветовым градиентом
            int check = ((i / 16) + (j / 16)) % 2;
            texData[idx]     = check ? 100 + (i % 32) * 4 : 200 - (i % 32) * 2;
            texData[idx + 1] = check ? 150 + (j % 32) * 3 : 100 + (j % 32) * 3;
            texData[idx + 2] = check ? 200 - (i % 32) * 2 : 150 + (i % 32) * 4;
        }
    }
    
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texSize, texSize, 0, 
                 GL_RGB, GL_UNSIGNED_BYTE, texData);
    
    delete[] texData;
}

// ---------- Преобразование: мировые → экранные координаты ----------
void worldToScreen(double xw, double yw, double zw,
                   double &xs, double &ys, double &zs) {
    // Поворот вокруг оси Z (alpha)
    double x1 = xw * cos(alpha) - yw * sin(alpha);
    double y1 = xw * sin(alpha) + yw * cos(alpha);
    double z1 = zw;
    
    // Поворот вокруг оси X (beta)
    double y2 = y1 * cos(beta) - z1 * sin(beta);
    double z2 = y1 * sin(beta) + z1 * cos(beta);
    double x2 = x1;
    
    if (!perspective) {
        // Аксонометрия
        xs = x2; ys = y2; zs = z2;
    } else {
        // Перспектива
        double d = cameraDist - z2;
        if (d < 0.01) d = 0.01;
        xs = x2 * cameraDist / d;
        ys = y2 * cameraDist / d;
        zs = z2;
    }
}

// ---------- Параметризация поверхности с новым сдвигом ----------
// Формула: "Сдвиг пропорционален квадрату широты"
// x = x_ш + R·(B/45)²,  y = y_ш,  z = 2·z_ш
void dropSurface(double B, double L, double &x, double &y, double &z) {
    // B ∈ [-π/2, +π/2] — широта (радианы), L ∈ [0, 2π] — долгота
    
    double cosB = cos(B);
    double sinB = sin(B);

    // Базовая форма "капли"
    double radiusFactor = (1.0 - sinB) * 0.5 + 0.05;  // 1.05 → 0.05
    double r = R * cosB * radiusFactor;

    // Координаты до сдвига (индекс "ш" — "широтные")
    double x_sh = r * sin(L);
    double y_sh = r * cos(L);
    double z_sh = R * sinB * (1.0 + 0.8 * (sinB + 1.0) * 0.5);

    // === ПРИМЕНЕНИЕ СДВИГА ПО ФОРМУЛЕ ===
    double B_deg = B * 180.0 / PI;           // радианы → градусы
    double shift = R * (B_deg / 45.0) * (B_deg / 45.0);  // (B/45)²
    
    x = x_sh + shift;   // сдвиг по X пропорционален B²
    y = y_sh;           // Y без изменений
    z = 2.0 * z_sh;     // масштабирование по Z
}

// ---------- Инициализация Z-буфера ----------
void initZBuffer(int w, int h) {
    zbufW = w; zbufH = h;
    zBuffer.assign(w * h, -1e30f);  // минимальное значение глубины
}

// ---------- Вывод пикселя с тестом глубины ----------
void putPixel(int x, int y, float z, float r, float g, float b) {
    if (x < 0 || x >= zbufW || y < 0 || y >= zbufH) return;
    int idx = y * zbufW + x;
    if (z > zBuffer[idx]) {  // Z-тест
        zBuffer[idx] = z;
        glColor3f(r, g, b);
        glBegin(GL_POINTS);
        glVertex2i(x, y);
        glEnd();
    }
}

// ---------- Модель освещения Фонга ----------
void computeLight(double xw, double yw, double zw,
                  double nx, double ny, double nz,
                  float &r, float &g, float &b) {
    // Нормализация нормали
    double len = sqrt(nx*nx + ny*ny + nz*nz);
    if (len < 1e-9) { r=g=b=0.5f; return; }
    nx /= len; ny /= len; nz /= len;

    // Вектор к источнику света
    double lx = lightPos[0] - xw;
    double ly = lightPos[1] - yw;
    double lz = lightPos[2] - zw;
    double llen = sqrt(lx*lx + ly*ly + lz*lz);
    if (llen < 1e-9) llen = 1.0;
    lx /= llen; ly /= llen; lz /= llen;

    // Диффузная составляющая
    double diff = nx*lx + ny*ly + nz*lz;
    if (diff < 0.0) diff = 0.0;

    // Зеркальная составляющая (halfway vector)
    double hx = lx, hy = ly, hz = lz + 1.0;  // направление к камере ~ (0,0,1)
    double hlen = sqrt(hx*hx + hy*hy + hz*hz);
    hx /= hlen; hy /= hlen; hz /= hlen;
    double spec = nx*hx + ny*hy + nz*hz;
    if (spec < 0.0) spec = 0.0;
    spec = pow(spec, matShininess);

    // Итоговый цвет
    r = matAmbient[0] * ambientLight[0] +
        matDiffuse[0] * diffuseLight[0] * diff +
        matSpecular[0] * specularLight[0] * spec;
    g = matAmbient[1] * ambientLight[1] +
        matDiffuse[1] * diffuseLight[1] * diff +
        matSpecular[1] * specularLight[1] * spec;
    b = matAmbient[2] * ambientLight[2] +
        matDiffuse[2] * diffuseLight[2] * diff +
        matSpecular[2] * specularLight[2] * spec;

    // Ограничение диапазона [0,1]
    r = (r > 1.0f) ? 1.0f : r;
    g = (g > 1.0f) ? 1.0f : g;
    b = (b > 1.0f) ? 1.0f : b;
}

// ---------- Текстурные координаты ----------
void computeTexture(double B, double L, float &u, float &v) {
    u = (L + PI) / (2.0 * PI);  // долгота → [0,1]
    v = (B + PI/2.0) / PI;       // широта → [0,1]
}

// ---------- Основная отрисовка ----------
void drawSurface() {
    double dB = PI / nLat;
    double dL = 2.0 * PI / nLon;

    // Включение текстуры при необходимости
    if (useTexture && textureId) {
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, textureId);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    }

    for (int i = 0; i < nLat; i++) {
        double B1 = -PI/2.0 + i * dB;
        double B2 = B1 + dB;
        
        for (int j = 0; j < nLon; j++) {
            double L1 = j * dL;
            double L2 = L1 + dL;

            // 4 вершины квада в мировых координатах
            double xw[4], yw[4], zw[4];
            dropSurface(B1, L1, xw[0], yw[0], zw[0]);
            dropSurface(B2, L1, xw[1], yw[1], zw[1]);
            dropSurface(B2, L2, xw[2], yw[2], zw[2]);
            dropSurface(B1, L2, xw[3], yw[3], zw[3]);

            // Проекция в экранные координаты
            double xs[4], ys[4], zs[4];
            for (int k = 0; k < 4; k++)
                worldToScreen(xw[k], yw[k], zw[k], xs[k], ys[k], zs[k]);

            // Преобразование в пиксели
            int px[4], py[4];
            double scale = zbufW / 5.0;
            for (int k = 0; k < 4; k++) {
                px[k] = (int)((xs[k] + 2.5) * scale);
                py[k] = (int)((2.5 - ys[k]) * (zbufH / 5.0));
            }

            // Нормаль к грани (векторное произведение)
            double ux = xw[1]-xw[0], uy = yw[1]-yw[0], uz = zw[1]-zw[0];
            double vx = xw[3]-xw[0], vy = yw[3]-yw[0], vz = zw[3]-zw[0];
            double nx = uy*vz - uz*vy;
            double ny = uz*vx - ux*vz;
            double nz = ux*vy - uy*vx;

            // Back-face culling (только для заливки)
            double camX = sin(alpha)*sin(beta);
            double camY = cos(alpha)*sin(beta);
            double camZ = cos(beta);
            if (!wireframe && (nx*camX + ny*camY + nz*camZ < 0)) continue;

            // Расчёт освещения (центр грани)
            float cr = 1.0f, cg = 1.0f, cb = 1.0f;
            if (!wireframe) {
                double cx = (xw[0]+xw[1]+xw[2]+xw[3]) / 4.0;
                double cy = (yw[0]+yw[1]+yw[2]+yw[3]) / 4.0;
                double cz = (zw[0]+zw[1]+zw[2]+zw[3]) / 4.0;
                computeLight(cx, cy, cz, nx, ny, nz, cr, cg, cb);
            }

            // Текстурные координаты для углов
            float tu[4] = {0}, tv[4] = {0};
            if (useTexture) {
                computeTexture(B1, L1, tu[0], tv[0]);
                computeTexture(B2, L1, tu[1], tv[1]);
                computeTexture(B2, L2, tu[2], tv[2]);
                computeTexture(B1, L2, tu[3], tv[3]);
            }

            // Разбиение квада на 2 треугольника
            for (int t = 0; t < 2; t++) {
                int idx[3] = {0, 1 + t, 2 + t};
                
                // === КАРКАСНЫЙ РЕЖИМ ===
                if (wireframe) {
                    glColor3f(cr, cg, cb);
                    glBegin(GL_LINE_LOOP);
                    for (int k = 0; k < 3; k++)
                        glVertex2i(px[idx[k]], py[idx[k]]);
                    glEnd();
                    continue;
                }
                
                // === РЕЖИМ ЗАЛИВКИ С Z-БУФЕРОМ ===
                // Сортировка вершин по Y для сканирующей растеризации
                if (py[idx[0]] > py[idx[1]]) std::swap(idx[0], idx[1]);
                if (py[idx[0]] > py[idx[2]]) std::swap(idx[0], idx[2]);
                if (py[idx[1]] > py[idx[2]]) std::swap(idx[1], idx[2]);

                int yStart = std::max(0, py[idx[0]]);
                int yEnd   = std::min(zbufH - 1, py[idx[2]]);

                for (int y = yStart; y <= yEnd; y++) {
                    float xLeft = 1e9f, xRight = -1e9f;
                    float zLeft = 0, zRight = 0;
                    float uLeft = 0, uRight = 0, vLeft = 0, vRight = 0;
                    
                    // Поиск пересечений рёбер с текущей строкой
                    for (int e = 0; e < 3; e++) {
                        int v0 = idx[e], v1 = idx[(e+1)%3];
                        if ((py[v0] <= y && py[v1] > y) || (py[v1] <= y && py[v0] > y)) {
                            float tEdge = float(y - py[v0]) / (py[v1] - py[v0]);
                            float xInter = px[v0] + tEdge * (px[v1] - px[v0]);
                            float zInter = zs[v0] + tEdge * (zs[v1] - zs[v0]);
                            float uInter = useTexture ? tu[v0] + tEdge * (tu[v1] - tu[v0]) : 0;
                            float vInter = useTexture ? tv[v0] + tEdge * (tv[v1] - tv[v0]) : 0;
                            
                            if (xInter < xLeft)  { xLeft = xInter; zLeft = zInter; uLeft = uInter; vLeft = vInter; }
                            if (xInter > xRight) { xRight = xInter; zRight = zInter; uRight = uInter; vRight = vInter; }
                        }
                    }
                    
                    int xL = std::max(0, (int)xLeft);
                    int xR = std::min(zbufW - 1, (int)xRight);
                    
                    for (int x = xL; x <= xR; x++) {
                        float tX = (xR == xL) ? 0.5f : float(x - xL) / (xR - xL);
                        float zVal = zLeft + tX * (zRight - zLeft);
                        
                        if (useTexture) {
                            float u = uLeft + tX * (uRight - uLeft);
                            float v = vLeft + tX * (vRight - vLeft);
                            glTexCoord2f(u, v);
                        }
                        putPixel(x, y, zVal, cr, cg, cb);
                    }
                }
            }
        }
    }
    
    if (useTexture) glDisable(GL_TEXTURE_2D);
}

// ---------- Callback: отрисовка ----------
void display() {
    glClear(GL_COLOR_BUFFER_BIT);
    initZBuffer(winW, winH);
    drawSurface();
    glutSwapBuffers();
}

// ---------- Callback: изменение размера окна ----------
void reshape(int w, int h) {
    winW = w; winH = h;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, w, h, 0);  // 2D-проекция для растеризации
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    initZBuffer(w, h);
}

// ---------- Меню ----------
enum { MENU_AXON, MENU_PERSP, MENU_WIRE, MENU_FILL, MENU_TEX, MENU_EXIT };
void menu(int item) {
    switch (item) {
        case MENU_AXON:  perspective = false; break;
        case MENU_PERSP: perspective = true;  break;
        case MENU_WIRE:  wireframe = true; break;
        case MENU_FILL:  wireframe = false; break;
        case MENU_TEX:   useTexture = !useTexture; break;
        case MENU_EXIT:  exit(0);
    }
    glutPostRedisplay();
}

// ---------- Обработка клавиатуры ----------
void keyboard(unsigned char key, int, int) {
    switch (key) {
        // Вращение объекта
        case 'a': alpha += 5.0 * PI / 180.0; break;
        case 'd': alpha -= 5.0 * PI / 180.0; break;
        case 'w': beta  += 5.0 * PI / 180.0; break;
        case 's': beta  -= 5.0 * PI / 180.0; break;
        
        // Переключение режимов
        case 'p': perspective = !perspective; break;
        case 'f': wireframe = !wireframe; break;
        case 't': useTexture = !useTexture; break;
        
        // Управление источником света
        case '1': lightPos[0] += 0.5f; break;  // X+
        case '2': lightPos[0] -= 0.5f; break;  // X-
        case '3': lightPos[1] += 0.5f; break;  // Y+
        case '4': lightPos[1] -= 0.5f; break;  // Y-
        case '5': lightPos[2] += 0.5f; break;  // Z+
        case '6': lightPos[2] -= 0.5f; break;  // Z-
        
        // Свойства материала
        case '7': matShininess = std::min(128.0f, matShininess + 8.0f); break;
        case '8': matShininess = std::max(1.0f, matShininess - 8.0f); break;
        case '9':  // ярче диффузный свет
            diffuseLight[0] = diffuseLight[1] = diffuseLight[2] = 
                std::min(1.0f, diffuseLight[0] + 0.1f); break;
        case '0':  // тусклее диффузный свет
            diffuseLight[0] = diffuseLight[1] = diffuseLight[2] = 
                std::max(0.1f, diffuseLight[0] - 0.1f); break;
        
        // Параметры поверхности
        case '+': R += 0.1; break;
        case '-': R = std::max(0.5, R - 0.1); break;
        
        case 27: exit(0);  // ESC
    }
    glutPostRedisplay();
}

// ---------- Инициализация ----------
void init() {
    glClearColor(0.1f, 0.1f, 0.15f, 1.0f);
    
    // Генерация текстуры
    generateTexture();
    
    // Создание меню
    glutCreateMenu(menu);
    glutAddMenuEntry("Аксонометрия", MENU_AXON);
    glutAddMenuEntry("Перспектива",  MENU_PERSP);
    glutAddMenuEntry("Каркас (wireframe)", MENU_WIRE);
    glutAddMenuEntry("Заливка (filled)", MENU_FILL);
    glutAddMenuEntry("Текстура: вкл/выкл", MENU_TEX);
    glutAddMenuEntry("Выход", MENU_EXIT);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
}

// ---------- Точка входа ----------
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(winW, winH);
    glutCreateWindow("Сдвиг ~ B^2 | Вариант пользователя");
    
    init();
    
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    
    glutMainLoop();
    return 0;
}