Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Игры";
Текущий архив: 2008.01.13;
Скачать: [xml.tar.bz2];

Вниз

Полусфера на поверхности сферы (OpenGL)   Найти похожие ветки 

 
Creative   (2006-11-28 14:34) [0]

Есть Сфера и есть полусфера. Как сделать следующую вещь:
Нарисовать полусфера таким образом стобы ее можно было возить по сфере , останавливая в произвольной точке?
Была мысль использовать для рисования полусферы gluSphere + glClipPlane, но при тех параметрах, которые принимает в себя glClipPlane, не могу понять, как заставить ее произвольно вертеться.
Подскажите что-нибудь.


 
ancara ©   (2006-11-28 17:03) [1]

можно самому сферу построить, точнее даже часть сферы в дипазонах по зениту и азимуту  и вращать ее glRotate как обычный примитив.
вот код, там дисплейные списки используется, это не совсем "прогресивный" метод, но результат тот же

type
 TVector3f = array [0..2]of single;

var
 zen, azi: real;  //счетчики углов по зениту и азимуту (радианы)
 aziNext, zenNext: real;  //счетчики углов по зен. и аз. для след. шага (радианы)
 aziStartR, aziStopR: real; //диапазон по азимуту
 zenStartR, zenStopR: real; //диапазон по зениту
 stpZ, stpA: real;  //шаги по зен. и азим.
 Vec1, Vec2: TVector3f;  //координаты вершин для опенгл
begin
 aziStartR:=FAzimuthStart*pi/180; //Тут переводим из радианов в градусы
 aziStopR:=FAzimuthStop*pi/180;  //значения углов
 zenStartR:=FZenithStart*pi/180;
 zenStopR:=FZenithStop*pi/180;

 glNewList(ListID, GL_COMPILE);  //ListID - номер диспл. списка
   stpZ:=pi/FSlices;  //считаем шаг по зениту, FSlices - кол-во разбиений по меридиану
   stpA:=pi*2/FLoops;  //считаем шаг по азимуту, FLoops - колво разбиений по параллели
   zen:=zenStartR;  //счетчик зенита в нач. значение
   while zen<=zenStopR do //будем увелич. его на шаг, пока не упремся в конечн. значение
   begin
     if zen=zenStopR then Break; //если start = stop выходим из while .. do
     
     //тут хитро считаем следующий угол по зениту с учетом шага и стопа
     zenNext:=-pi/2;
     while zenNext<=zen do zenNext:=zenNext+stpZ;
     if zenNext>zenStopR then zenNext:=zenStopR;

     
     azi:=aziStartR;  //счетчик азимута в начальное значение
     glBegin(GL_QUAD_STRIP);  //начинаем примитив "лента из четырехугольников"
     while azi<=aziStopR do //будем увелич. азимут на шаг, пока не упремся в конечн. значение
     begin
       //тут считаем координаты первой и второй вершины      
       Vec1[0]:=Sin(azi)*Cos(zen)*FRadius; //FRadius - радиус сферы
       Vec1[1]:=Sin(zen)*FRadius;
       Vec1[2]:=Cos(azi)*Cos(zen)*FRadius;
       Vec2[0]:=Sin(azi)*Cos(zenNext)*FRadius;
       Vec2[1]:=Sin(zenNext)*FRadius;
       Vec2[2]:=Cos(azi)*Cos(zenNext)*FRadius;

       //почему выходим именно тут не помню давно это было :))
       if azi=aziStopR then break;

       //тут хитро считаем следующий угол по азимуту с учетом шага и стопа
       aziNext:=0;
       while aziNext<=azi do aziNext:=aziNext+stpA;
       if aziNext>aziStopR then aziNext:=aziStopR;

       azi:=aziNext; //наращиваем азимут
     end;

     glEnd;
     zen:=zenNext; //наращиваем зенит
   end;
 glEndList;
end;


Ух, запарился каменты писать :)) Тут еще и топик прыгает по веткам :))
Насчет работоспособности именно ЭТОГО куска не уверен, не проверял, выдрал из компонента, может что и забыл убрать лишнее...


 
Creative   (2006-11-28 17:06) [2]

Душевное вам спасибо, и особливо за комменты, пойду пробовать :-)
Результат интересен?


 
ancara ©   (2006-11-28 17:06) [3]

Поправка: в начале кода написал
 aziStartR:=FAzimuthStart*pi/180; //Тут переводим из радианов в градусы
 aziStopR:=FAzimuthStop*pi/180;  //значения углов

там наоборот конечно же, в радианы все гоним!


 
Creative   (2006-11-28 17:11) [4]

Так написано то вроде правильно?


 
ancara ©   (2006-11-28 17:12) [5]

Ой, самое главное вырезал!
После того как координаты обоих вершин посчитали делаем
glVertex3fv(@Vec2);
glVertex3fv(@Vec1);



> Результат интересен?

Отпишись:))
Будут вопросы - излагай :)


 
Creative   (2006-11-28 17:16) [6]

aziStartR:=FAzimuthStart*pi/180;

из класса выдрал? :-)


 
Creative   (2006-11-28 17:24) [7]

а точно вот так glNewList(ListID, GL_COMPILE),
а не glNewList(FListID, GL_COMPILE);


 
Creative   (2006-11-28 17:27) [8]

Вопрос наверное тупой: ListID как инициализировать?


 
ancara ©   (2006-11-28 17:46) [9]


> а не glNewList(FListID,

Не там у меня было именно свойство ListID, в принципе без разницы :))

> ListID как инициализировать?

ListID:=glGenLists(1); //1 - это количество списков,

ListID:= glGenLists(range: Integer) генерирует range идентификаторов диспл. списков, следующих подряд, при этом ListID принимает значение первого из них.
Т.е. если нам требуется 18 списков, мы делаем ListID:= glGenLists(18) и смотрим
чему равен ListID, если он, к примеру, равен 44, то это значит что OpenGL для нас зарезервировал диспл. списки с 44 по 61 включительно и мы их можем использовать. После того как они станут не нужны следует их удалить функцией glDeleteLists.
 Чтобы пересобрать, скажем, 47 дисп. список, его не обязательно удалять и заново создавать, можно прям так и написать glNewList(47, GL_COMPILE) и т.д.


 
Creative   (2006-11-28 17:51) [10]

Следующий тупой вопрос: а как мне понять, какое количество списков мне нужно в данном конкретном случае со сферой?

И если я, например, саму приведенную вами процедуру ставлю в TMyClass.Render, то в конструкторе мне нужно написать FListID:= "собственно нужное мне число списков", так?


 
Creative   (2006-11-28 17:53) [11]

Можно немного посторонний вопрос задать (просто не хочется из-за такой ерунды целую ветку открывать, а вы наверняка знаете): у меня есть два примитива на одной сцене. Как мне каждый из них повернуть на свой угол? А то мне кажется, что glPotate действует аддитивно.


 
ancara ©   (2006-11-28 18:02) [12]


> какое количество списков мне нужно

ну это уже как удобней будет, если известно, что части какого-то объекта не будут перемащаться относит друг друга, то можно все это тела сделать одним списком. Ну если уж переместятся, то список можно пересобрать, но лучше этого избегать ибо ресурсоемко...
 Как крайность, теоретически, можно конечно и всю сцену одним списком собрать, но тогда придется при каждом изменении пересобирать заново и никакого толку от использования списков.. А можно и каждый полигон в свой список запихнуть, но тут никаких списков не напасешься.. :))


> саму приведенную вами процедуру ставлю в TMyClass.Render

Не, не надо ее в рендер ставить, добавь какой-нить метод типа BuildList (или ReBuildList), в нем будем строится список объекта (нажо стремиться делать это как можно реже, лучше вообще один раз:)) ). А в рендере надо просто вызвать этот список glCallList(номер списка) и фсио! :)


 
Creative   (2006-11-28 18:06) [13]

Кажется, эти списки для меня вообще предмет новый. Завтра будет много вопросов.


>
> Не, не надо ее в рендер ставить, добавь какой-нить метод
> типа BuildList (или ReBuildList), в нем будем строится список
> объекта (нажо стремиться делать это как можно реже, лучше
> вообще один раз:)) ). А в рендере надо просто вызвать этот
> список glCallList(номер списка) и фсио! :)


Так может просто в конструкторе его собирать?


 
ancara ©   (2006-11-28 19:42) [14]


> а вы наверняка знаете

завязывай, давай на ты


> Так может просто в конструкторе его собирать?

Ну можно и в конструкторе, только сам код лучше разместить все таки в отдельном методе а из конструктора его вызывать, иначе как быть, если изменится радиус, например, или другие параметры геометрии? Его же опять надо будет вызвать...


> Кажется, эти списки для меня вообще предмет новый.

Ну знать их безусловно надо, но в будущем ориентируйся на DrawArrays и  VBO (Vertex Buffer Object) - более гибкая штука, хотя бывают споры насчет того кто кого обгоняет, главное грамотно и в нужном месте применить, но это уже нюансы, для начала списки самое то.


> у меня есть два примитива на одной сцене. Как мне каждый
> из них повернуть на свой угол? А то мне кажется, что glPotate
> действует аддитивно.

Кароче так, в OpenGL есть три матрицы:
GL_PROJECTION - определяет параметры проекции пространства сцены на область вывода;
GL_TEXTURE - определяет параметры наложения текстур (масштаб смещение поворот)
и GL_MODELVIEW - определяет систему координат пространства сцены.

 Так вот glRotate принимает от нас угол, на кот. будем поворачивать и вектор, вокруг которого крутить и по ним строит матрицу поворота.
 Затем умножает текущую матрицу (GL_MODELVIEW) на матрицу поворота, в итоге матрица GL_MODELVIEW определяет уже повернутую систему координат.

Для взаимодействия с матрицами существуют функции:
 glMatrixMode(GL_MODELVIEW или другая из тех трех) - выбрать матрицу, с которой будут производиться манипуляции;
 glLoadIdentity - сделать текущую матрицу единичной;
 glPushMatrix - поместить текущую матрицу в стек;
 glMultMatrix - умножить тек. матрицу на нек. матрицу, результат будет записан в текущую;
 glLoadMatrix - загрузить свою матрицу в текущую;
 glPushMatrix - поместить еще одну текущую матрицу в стек, а та которая там была сдвинется на второе место соответственно:)) ;
Глубина стека матриц зависит от реализации OpenGL (от модели видеокарты кароче);
 glPopMatrix - достать матрицу из стека и сделать ее текущей;
 glPopMatrix - достать ту, самую первую матрицу :) из стека и сделать ее текущей;
 Что-то я забыл перечислить... смотри RedBook, там все подробно :))

В общем, чтобы не было аддитивности делаем так:
- имеем начальную систему координат;
- работаем с матрицей MODELVIEW: glMatrixMode(GL_MODELVIEW);
- сохраняем текущ. матрицу в стек: glPushMatrix;
- крутим-вертим сист. коорд.:
     - можно glRotate+glTranslate+glScale,
     - а можно  glMultMatrix, если нам известна матрица преобразований,
     - а можно вообще сразу glLoadMatrix, если нам известна результирующая матрица;
- делаем рендеринг объекта в новой сист. коор.: glCallList, например;
- восстанавливаем сист. коорд. в исходную: glPopMatrix;
- ну и опять тоже самое начиная с glPushMatrix для следующего объекта...

Ну вот примерно так...


 
Creative   (2006-11-29 10:44) [15]


> В общем, чтобы не было аддитивности делаем так:
>glMatrixMode(GL_MODELVIEW);
>glPushMatrix;
>glRotate;
> - делаем рендеринг объекта в новой сист. коор.: glCallList,
>  например;


Про рендеринг я впервые слышу, хотя что-то уже как-то рисую. Может это еще каким-то более известным способом делается?


> а можно  glMultMatrix, если нам известна матрица
> преобразований,
> а можно вообще сразу glLoadMatrix, если нам известна
> результирующая матрица;


А как узнать то и другое?


 
Creative   (2006-11-29 11:09) [16]

По поводу дисплейных списов. С первого раза, конечно ничего не заработало. Я правильно делаю:

1. procedure BuildList:
ListID:=1;
glNewList(ListID, GL_COMPILE);

......твой код рисования полусферы

glEndList;

2. procedure Render:
glPushMatrix();
glColor3f(255,255,255);
glCallList(1);
glPopMatrix();

Так?


 
ancara ©   (2006-11-29 11:36) [17]


> А как узнать то и другое?

Да это вобщем-то и не обязательно знать, просто графич. движок может быть устроен таким образом, что у каждого объекта есть своя собственная матрица, однозначно определяющая его положение в пространстве, смещение относит. начала координат и его "вытянутость" по трем осям. В таком случае, перед рендерингом этого объекта, его не нужно будет последовательно крутить вокруг трех осей, затем смещать, затем масштабировать, а можно просто загрузить его собственную матрицу, сделать рендеринг и восстановить исходную, затем также со следующим... . Правда при передвижении или поворотах этого объекта, его матрицу придется перерасчитывать.
 А насчет первого варианта, если известна матрица преобразований... ну я не знаю, но всякое в жизни бывает... Главное то, что есть такая возможность, матрицы перемножать! :))
Да, кстати я забыл перечислить, текущую матрицу можно прочитать ф-цией glGet с аргументом GL_MODELVIEW_MATRIX.


 
ancara ©   (2006-11-29 11:42) [18]


> 1. procedure BuildList:
> ListID:=1;

не, ListID надо сначала получить: ListID:=glGenList(1);
потом мой код, там в нем уже есть glNewList


> 2. procedure Render:

вместо glCallList(1); надо glCallList(ListID);
ну в остальном вроде все верно, только в рендере сейчас PushMatrix и PopMatrix бессмыслены, ибо матрица и так остается неизменной, но если между ними будут преобразования сист. координат (glRotate или glTranslate или glScale) то тогда они будут иметь смысл.


 
Creative   (2006-11-29 11:54) [19]


> не, ListID надо сначала получить: ListID:=glGenList(1);


но компилятор пишет, что glGenList(1) - undeclared identifier


 
ancara ©   (2006-11-29 12:01) [20]

ошибся, glGenLists


 
Creative   (2006-11-29 12:10) [21]

Самое противное заключается в том, что все равно ничего не происходит. Все компилится, все запускается, но ничего не рисуется. И вот теперь я уже не могу сказать, почему.


 
Creative   (2006-11-29 13:11) [22]

Я могу код класса TSphere привести, если это поможет.


 
ancara ©   (2006-11-29 14:58) [23]

Я тут выложил свой рабочий примерчик, там полигон и текстура на нем. Рендеринг через вызов дисплейного списка. Там еще есть парочка "лишних" процедур для управления камерой при помощи мышки, ну думаю разберешься...
 Начинается все с OnCreate формы : создается контекст OpenGL, некот. инициализация камеры и проч. и BuildList - создается список с этим полигоном и все. А дальше вызывается RenderScene, когда надо перерисовать форму (событие OnPaint).


 
ancara ©   (2006-11-29 14:58) [24]

Я тут выложил свой рабочий примерчик:
http://s28.quicksharing.com/v/8118153/OpenGL_texture.rar.html
, там полигон и текстура на нем. Рендеринг через вызов дисплейного списка. Там еще есть парочка "лишних" процедур для управления камерой при помощи мышки, ну думаю разберешься...
 Начинается все с OnCreate формы : создается контекст OpenGL, некот. инициализация камеры и проч. и BuildList - создается список с этим полигоном и все. А дальше вызывается RenderScene, когда надо перерисовать форму (событие OnPaint).


 
ancara ©   (2006-11-29 14:59) [25]

хех, забыл сначала линк вставить :)


 
Creative   (2006-11-29 15:05) [26]

Спасибо! Я постараюсь разобраться обязательно.


 
Creative   (2006-11-29 15:29) [27]

Все бы ничего, только не качается он :-(


 
Creative   (2006-11-29 15:45) [28]

Все в порядке, это у меня глюки


 
Creative   (2006-11-29 16:09) [29]

Я дико извиняюсь, но по моему в твоем архиве чего-то не хватает. В частности файла Main_u.pas.


 
ancara ©   (2006-11-29 16:24) [30]

как так? а остальные есть? не может быть!
вобщем там было 4 (четыре) файла:
main_u.dfm
OpenGL_texture.dpr
1.jpg
main_u.pas

код main_u.pas положу сюда http://www.everfall.com/paste/id.php?icy518q11p56


 
Creative   (2006-11-29 16:28) [31]

Мне архив сказал "неожиданный конец", и файла оказалось только три. Пас как раз и потерялся


 
Creative   (2006-11-29 16:31) [32]

кстати, насчет независимого поволота объектов. Я вот как сделала:

 
 glTranslatef (0.0, 0.0, -5.0);
 glRotatef (30.0, 0.0, 0.0, 1.0);
 glPushMatrix;
 //нарисовала все, что хотела
 glPopMatrix;

 glLoadIdentity;
 glTranslatef (0.0, 0.0, -5.0);
 glRotatef (-30.0, 0.0, 0.0, 1.0);

 glPushMatrix;
  //нарисовала все, что хотела 2
 glPopMatrix;


И получилось вроде правильно.
Это варварство?


 
Creative   (2006-11-29 16:41) [33]

Битый архив не пошел на пользу проекту. Теперь он в усмерть не компилится.


 
Creative   (2006-11-29 16:52) [34]

Все, нормально открылось. Буду смотреть


 
ancara ©   (2006-11-29 17:15) [35]


> glTranslatef (0.0, 0.0, -5.0);
>  glRotatef (30.0, 0.0, 0.0, 1.0);
>  glPushMatrix;
>  //нарисовала все, что хотела
>  glPopMatrix;
>

Нет, это в корне не правильно :)
Еще раз: glPushMatrix позволяет сохранить матрицу, путем помещения ее копии в стек, а glPopMatrix ее из стека достает и таким образом восстанавлявает матрицу.
 А операции glTranslatef и glRotatef изменяют матрицу.

Поэтому этот код выглядит так:
 - изменяем матрицу не сохранив ее: glTranslatef (0.0, 0.0, -5.0);
 - еще раз изменяем:  glRotatef (30.0, 0.0, 0.0, 1.0);
 - сохраняем уже измененную матрицу:  glPushMatrix; (зачем ее сохранять то?)
 - рисуем, но не изменяем матрицу:  //нарисовала все, что хотела
 - восстанавливаем матрицу:  glPopMatrix;

А хотелось бы вот так:
 - прежде всего сохранить исходную матрицу:  glPushMatrix;
 - сделать необходимые преобразования: glTranslatef (0.0, 0.0, -5.0);
 - сделать необходимые преобразования: glRotatef (30.0, 0.0, 0.0, 1.0);
 - нарисовать все, что хочу...
 - восстанавить матрицу:  glPopMatrix;


 
Creative   (2006-11-29 17:38) [36]


> Нет, это в корне не правильно :)
> А хотелось бы вот так:


Да, ты совершенно прав. Таким образом нам совершенно не нужна становится LoadIdentity. Спасибо.
Кстати, у меня тут еще один глупый вопрос есть: если я пишу glRotatef (30.0, 1.0, 0.0, 1.0) - я ведь получу поворот на 30 градусов по обеим указанным осям, так? А если я хочу повернуть объект по нескольким осям на разные углы, мне что, дважды или трижды писать glRotatef ?

Кстати, пример твой скомпилился, я поставила твой код и мне удалось нарисовать кусочек сферы :-). Осталось поработать над мелочами, подобрать точные параметры.

Такой вопрос: а тебе удобно писать на VCL?


 
ancara ©   (2006-11-29 18:10) [37]


> я ведь получу поворот на 30 градусов по обеим указанным
> осям, так?

нет, это будет поворот вокруг вектора (1.0, 0.0, 1.0) на 30 градусов. Причем OpenGL этот вектор еще и нормализует...

> А если я хочу повернуть объект по нескольким осям на разные
> углы, мне что, дважды или трижды писать glRotatef ?

Да, дважды или трижды :)

> Такой вопрос: а тебе удобно писать на VCL?

Да не жалуюсь :))

Вот тут еще один вариант main_u.pas, там кусок сферы окрашенный в желтый цвет и точечный источник света.
 В процедуре рендеринга вот этими функциями
 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   @diffuse);
 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   @diffuse);

устанавливаются соответсвующие свойства материала объекта.


 
ancara ©   (2006-11-29 18:18) [38]

вот чорт, опять линк забыл запостить, вот он:
http://www.everfall.com/paste/id.php?sloc9gq8fbfp

P.S. как-то там на эверфоле каменты странно выглядят, или мне так кажется?


 
Creative   (2006-11-30 10:39) [39]

Да, очень страно смотрятся. Вчера как-то лечила, сегодня не могу вспомнить как.

А что делает процедура SwapRGB?


 
ancara ©   (2006-11-30 11:59) [40]

Дело в том, что порядок хранения цветов в bmp-файле и RGB-текстуре различается : в bmp BGR, а в текстуре RGB, вот она и меняет местами крайние байты..


 
Creative   (2006-11-30 12:03) [41]

Пример потрясающий. То, что мне нужно!!!


 
ancara ©   (2006-11-30 12:41) [42]

Кушай, не обляпайся :))


 
Creative   (2006-11-30 12:46) [43]

Уляпаюсь по уши и еще попрошу! :-)

Только у меня чуть позже могут вопросы возникнуть, так что заглядывай, если не трудно.


 
Creative   (2006-11-30 15:13) [44]

Скажи, у тебя в DisplayMouseMove X и Y - это координаты курсора?


 
Creative   (2006-11-30 15:19) [45]

И еще вопрос: в твоем последнем примере у тебя в процедуре DisplayMouseMove что происходит при нажатии Alt+левая мышь?


 
ancara ©   (2006-11-30 16:35) [46]


> в DisplayMouseMove X и Y - это координаты курсора?

так точно


> что происходит при нажатии Alt+левая мышь?

не обращай внимания, это лишнее, я не заметил и забыл удалить. В этом примере раньше на плоскость накладывались две текстуры и одну из них можно было перемещать как раз левой кнопкой с зажатым альтом, а tx и ty как раз были координаты смещения текстуры... короче это не нужно :))


 
Creative   (2006-11-30 16:53) [47]

В общем, я перетолмачила твой пример на АПИ и теперь все очень здорово работает. ТЫ мне очень помог, спасибо.
Будем учиться натягивать на шарик текстуру :-)


 
ancara ©   (2006-11-30 19:18) [48]


> спасибо

На здоровье! :))

> Будем учиться натягивать на шарик текстуру :-)

Нет ничего проще: в BuildList сферы, после вычислений координат вершин,
ну там где glVertex3fv(@Vec2) и glVertex3fv(@Vec1) вставь такой код:

glNormal3fv(@vNorm2);
glTexCoord2f(azi/pi/2, zenNext/pi+0.5);
glVertex3fv(@Vec2);

glNormal3fv(@vNorm1);
glTexCoord2f(azi/pi/2, zen/pi+0.5);
glVertex3fv(@Vec1);

и все, текстурные координаты для вершин заданы, осталось "применить" текстуру к объекту, для этого в рендере, перед вызовом списка glCallList
нужно "забиндить" соответствующую текстуру:
glBindTexture(GL_TEXTURE_2D, Tex_1);
Tex_1 - это идентификатор текстуры, там в процедуре InitGL загружается картинка 1.jpg в качестве текстуры с номером Text_1:
LoadTexture("1.jpg", Tex_1);

Итого, еще раз:
 - в процессе инициализации создается текстура с номером Tex_1 (эта переменная примет некот. значение, мы его у OpenGL попросим);
 - содержимое этой текстуры загружается из файла 1.jpg;
 - в процессе построения диспл. списка сферы для каждой ее вершины указываются текстурные координаты;
 - перед рендерингом сферы, "биндится" (становится текущей) соответствующая текстура;
 - в результате рендеринга получаем сферу с натянутой текстурой;

Вопросы? :)


 
Creative   (2006-12-01 10:38) [49]

Объяснение отличное, пошла делать. Только переведу LoadTexture на понятный язык. :-)

Скажи, а ечли я хочу сделать например глобус - мне нужна карта мира, вырезанная в виду "апельсиновой корки" или как лучше? (я и сама конечно попробую, просто вдруг у тебя опыт есть)


 
ancara ©   (2006-12-01 11:24) [50]

нужна карта мира в виде равноугольной проекции Меркатора, т.е. той, на которой и параллели и меридианы параллельны, а полюсами являются верхняя и нижняя строны прямоугольника. Вобщем, вот такая вот: http://img135.imageshack.us/img135/6897/earthdaynq0.jpg
она там правда не очень высокого разрешения, 1024х1024.


 
Creative   (2006-12-01 11:35) [51]

Спасибо, мне за глаза хватит.
Кстати, если я внутри этой ветки один примитивный вопрос по VCL задам  - не страшно?


 
ancara ©   (2006-12-01 11:49) [52]


> не страшно?

:)) Это очень страшно! Через пол-часа придут люди в погонах и предъявят обвинение в умышленном оффтопе в игровой ветке! Не делай этого! Только не это!


 
Creative   (2006-12-01 11:51) [53]

Ну смотри, сам напросился. :-)
У меня на форме лежат несколько компонентов. Я делаю обработчик onKeyDown (или onKeyPress, как получится) для клавиш vk_Up/DOWN/LEFT/RIGHT. Но при запуске оказывается, что этот обработчик к жизни не вызывается, а при нажатии на стрелки просто по всем компонентам форму бегает фокус. Как правильно с этим бороться? Я точно помню, что как-то можно, но напрочь забыла - как. :-(


 
ancara ©   (2006-12-01 12:07) [54]

Понятно, можно поступить так:
на форму положи компонент TApplicationEvents (вкладка Additional),
в ее обработчике события OnMessage помести такой код:
 if msg.message=WM_KEYDOWN then
 begin
   if msg.wParam=VK_DOWN then //обработать нажатие клавиши "вниз"
   if msg.wParam=VK_UP then //обработать нажатие клавиши "вверх"
//если не хочешь чтоб фокус прыгал по компонентам
//раскоментируй след. строчку
//    handled:=true;
 end;


 
Creative   (2006-12-01 12:23) [55]

Угу, попробую.

У меня еще вопросы по поводу шариков.
У меня есть два разных объекта класса TSphere (написанного по твоему примеру). Соответственно у класса есть метод отрисовки TSphere.Render, выглядящий примерно так:


 glPushMatrix;
 glTranslatef
 glRotatef(Fbeta, 1,0,0);
 glRotatef(Falpha, 0,1,0);

 glCallList(ListID);  // рисование собственно сферы

 glPopMatrix;


В обработчике onMouseMove вот такой код:

S1.FAlpha:=Round(S1.FAlpha+(lpPoint.X-pX)*0.5);
S1.FBeta:=Round(S1.FBeta+(lpPoint.Y-pY)*0.5);

S1.Render;
S2.Render;


Я сильно надеялась, что этот код заставляет повернуться только одну сферу, а вторую остаться на месте (ты мне вроде бы так объяснял как крутить разные объекты?). Но при запуске картинка вообще не шевелится. Делаешь углы глобальными переменными - опять все нормально, но по прежнему крутится вся сцена целиком. Где я наглючила? Разве не должны сферы двигаться раздельно? Я где-то неправильно поняла?


 
Creative   (2006-12-01 12:33) [56]

Стрелки заработали. Ну, ты понял, что я хочу сказать :-)


 
ancara ©   (2006-12-01 12:42) [57]

lpPoint - а что это?

а вот насчет кода в OnMouseMove, я что-то затупил, не пойму что там должно получится...
Просто при изменениях свойств одного из объектов сцены нужно вызывать не его рендерер, а рендерер всей сцены, потому что в OpenGL невозможно перерисовать один объект а остальные нет,  если уж перерисовывать кадр так всю сцену придется рисовать.
  Т.е. в OnMouseMove надо вызвать глобальный рендерер (RenderScene или как там?), а в нем уже:
- подготовка всяких параметров (цвет фона там, cull-инг и пр.)
- настройка света;
- установка положения камеры;
- прочие приблуды;
- перебор всех объектов, подлежащих рендерингу и вызов у них метода Render;
- и наконец SwapBuffers;


 
Creative   (2006-12-01 12:59) [58]


> lpPoint - а что это?


Это переменная типа TPoint, куда складываются координаты курсора. Я пользуюсь GetCursorPos(lpPoint:TPoint).

в OnMouseMove надо вызвать глобальный рендерер (RenderScene или как там?), а в нем уже

а его собственно надо заключать в
PushMatrix;
PopMatrix;
?

cull-инг  - а это что?


 
ancara ©   (2006-12-01 13:31) [59]


> Я пользуюсь GetCursorPos(lpPoint:TPoint).

Дык в процедуру OnMouseMove и так передаются координаты курсора, они хранятся в переменных X и Y :) Ну результат в принципе такой же будет...


> cull-инг  - а это что?

Face culling - это дословно "отбраковка граней", каждый полигон в OpenGL имеет лицевую и обратную стороны, для каждой из них модно установить отдельные свойства  (материал, например). Включив куллинг (glEnable(GL_CULL_FACE)) и указав какие именно грани не рисовать (например обратные: glCullFace(GL_BACK)) мы позволим OpenGL расчитывать только лицевые грани, сэкономив таким образом процессорное время. Это может быть полезно в случае с какими-то замкнутыми фигурами, кубиком напрмер, если мы будем смотреть на него только снаружи и он непрозрачный мы никогда не увидим его обратных граней, т.к. они будут обращены внутрь (об этом надо позаботится отдельно), следовательно их незачем рисовать. Ну куллинг я как пример привел, его не обязательно использовать :)..


> а его собственно надо заключать в
> PushMatrix;
> PopMatrix;

Как это сделано у меня:
в глобальном рендере каждый кадр:

- устанавливаю единичную матрицу:glLoadIdentity;
- поворачиваю и смещаю сист.коорд. согласно положению камеры:
    glRotatef(FCamera.Beta, 1,0,0);
    glRotatef(-FCamera.Alpha, 0,1,0);
    glTranslatef(-FCamera.Position.X,  -FCamera.Position.Y,  -FCamera.Position.Z);

- всякая приблуда: SetupLights; //солнышко светит
- делаю рендеринг всех объектов:   RenderObjects ... (ну это очень упрощенно :)) )
  причем каждый объект перед рендерингом себя:
   - сохраняет матрицу: glPushMatrix;
   - смещает и крутит сист. координат, согласно своей позиции и ориентации;
   - назначает нужный ему материал, ставит куллинг, если надо, включает нужную ему текстуру и т.д., вобщем они у меня все взрослые и самостоятельные...
  - рендерит сам себя: glCallList;
  - возвращает все как было, чтоб следующий не обиделся: glPopMatrix;
  - ну и после того как нарисовали все что хотели : SwapBuffers


 
ancara ©   (2006-12-01 13:33) [60]


> для каждой из них модно установить

для каждой из них можно установить
гыгы, гламурный куллинг :)


 
Creative   (2006-12-01 13:41) [61]

Где-то я глобально запуталась. Вот смотри - всю систему координат мне крутить не надо. Рендеринг объекта я привела в [55], и там по-моему все, как ты и сказал. В глобальном рендере только и остается, что :
 
 glLoadidentity;
 S1.Render;
 S2.Render;

Так?

обработчик onMouseMove изменяет углы поворота и расстояние до экрана одного из объектов. И что получается: если я пытаюсь крутить или двигать первый объект - я вообще не вижу, что с ним происходит, хотя он уже по всем ожиданиям должен из экрана выпрыгнуть. А если второй - да, он крутится, но лежащий под ним первый объект не виден. Почему так?


 
Creative   (2006-12-01 13:42) [62]


> для каждой из них можно установитьгыгы, гламурный куллинг
> :)


да, меня сильно озадачило, думаю...ишь ты, у них там еще мода бывает :-))))


 
ancara ©   (2006-12-01 13:46) [63]

Т.е. первый крутится а второй нет?
Ну тогда давай код рендера и OnMouseMove, будем думать :))


 
Creative   (2006-12-01 14:00) [64]


> Т.е. первый крутится а второй нет?


второй (верхний) крутится, а первого просто не видно, что он там делает.

Рендер объекта:


procedure TSphere.Render;
var
diffuse: array [0..3]of GLfloat;
const
 light_pos: array [0..3]of GLfloat = (5.0, 5.0, 5.0, 0.0);
begin
 diffuse[0] :=FColor[0];
 diffuse[1] :=FColor[1];
 diffuse[2] :=FColor[2];
 diffuse[3] :=1;
 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

 glPushMatrix;
 glTranslatef(0,0,-FDistance);
 glRotatef(Fbeta, 1,0,0);
 glRotatef(Falpha, 0,1,0);

 glLightfv(GL_LIGHT0, GL_POSITION, @light_pos);
 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   @diffuse);
 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   @diffuse);

 glCallList(ListID);

 glPopMatrix;

end;



Рендер сцены:


procedure DrawScene;
begin
 glLoadidentity;
 S1.Render;
 S2.Render;
end;


onMouseMove:


begin
GetCursorPos(lpPoint);
  if mouse[0]  then      
 begin
    S2.FAlpha:=Round(S2.FAlpha+(lpPoint.X-pX)*0.5);
    S2.FBeta:=Round(S2.FBeta+(lpPoint.Y-pY)*0.5);
 end;

  if mouse[1]  then
 begin
    S2.FDistance:=S2.FDistance+(pX-lpPoint.X)*S2.FDistance*0.01;
 end;
 pX:=lpPoint.X;
 pY:=lpPoint.Y;

 DrawScene;

end;


{if mouse[0/1] - это переменная, указывающая на то, какая мышь зажата - левая или правая, не обращай внимания, я этот обработчик сама писала, как умела}

и еще такой вопрос6 я все-таки переделываю твой пример и я заметила такую вещь: если Distance поставить в glTranslatef не вместо оси Z, а вместо X, то объект доедет до середины окна и замрет там, больше его не сдвинуть. Почему так?


 
ancara ©   (2006-12-01 14:38) [65]


> procedure DrawScene;
> begin
>  glLoadidentity;
>  S1.Render;
>  S2.Render;
> end;

И это фсио? А камеру установить? Она по идее будет находится в точке (0, 0, 0) и смотреть вдоль оси Z в положительном направлении, и S1 будет рисоваться там же... получается так.. может она за камерой просто? А вот S2 можно двигать мышкой по оси Z...
попробуй так:

procedure DrawScene;
begin
glLoadidentity;
glTranslatef(0,0,-5); //это мы камеру сдвигаем "назад" на 5
S1.Render;
S2.Render;
end;


> доедет до середины окна и замрет там, больше его не сдвинуть.
>  Почему так?

Потому что у меня FDistance определял расстояние от камеры до ее "цели", точки на кот. она смотрит, оно не должно было быть отрицательным.
И поэтому в моем OnMouseMove было
Distance:=Distance+(pX-X)*Distance*0.01;
т.е. инкремент дистанции уменьшался вместе с самой дистанцией, т.е. чем ближе к нулю, тем медленее она к нему стремится, но видимо из-за недостаточной точности она все же обращалась в ноль и поэтому не отъезжала обратно.


 
Creative   (2006-12-01 14:43) [66]


> И это фсио?

не сочти за труд - говори по-русски :-)

А камеру установить? Она по идее будет находится в точке (0, 0, 0) и смотреть вдоль оси Z в положительном направлении, и S1 будет рисоваться там же... получается так.. может она за камерой просто?

Да нет же. Если заремить отрисовку S2 - то S1 преспокойно будет виден, благо он рисуется на тех же координатах. Я имею в виду, что если я поверну S2 или сдвину его назад - я не увижу S1, лежащий за ним, как будто его и нет вовсе.

glTranslatef(0,0,-5); //это мы камеру сдвигаем "назад" на 5

да от же самое, только издали :-(


 
Creative   (2006-12-01 14:43) [67]


> И это фсио?

не сочти за труд - говори по-русски :-)

А камеру установить? Она по идее будет находится в точке (0, 0, 0) и смотреть вдоль оси Z в положительном направлении, и S1 будет рисоваться там же... получается так.. может она за камерой просто?

Да нет же. Если заремить отрисовку S2 - то S1 преспокойно будет виден, благо он рисуется на тех же координатах. Я имею в виду, что если я поверну S2 или сдвину его назад - я не увижу S1, лежащий за ним, как будто его и нет вовсе.

glTranslatef(0,0,-5); //это мы камеру сдвигаем "назад" на 5

да от же самое, только издали :-(


 
ancara ©   (2006-12-01 14:50) [68]


> сдвину его назад - я не увижу S1, лежащий за ним, как будто
> его и нет вовсе.

А куда ж он девается-то? Может они вместе уезжают?
А попробуй заремить S2 и кнопки понажимать - S1 двигается? Хотя с чего ему двигаться...
А FDistance, FAlpha и FBeta это точно поля класса, может глоб. переменные?


 
Creative   (2006-12-01 14:54) [69]


> А куда ж он девается-то? Может они вместе уезжают?


С какой стати ему уезжать? Его то коодинаты не изменяются.

А попробуй заремить S2 и кнопки понажимать - S1 двигается? Хотя с чего ему двигаться...

Совершенно верно - никуда он не девается.

А FDistance, FAlpha и FBeta это точно поля класса, может глоб. переменные?

Абсолютно точно.


 
ancara ©   (2006-12-01 14:59) [70]

ну не знаю тогда... попробуй дебаггером посмотреть FDistances обеих сфер когда ты их двигаешь...


 
Creative   (2006-12-01 15:00) [71]

До меня кажется начало доходить!!!! Опытным путем я выяснила, что всему виной была строчка
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
стоящая в рендере объекта. По моему она просто аккуратно вытирала первый объект и спокойно на чистом листе рисовала второй.


 
Creative   (2006-12-01 15:01) [72]

просто в твоем примере она стояла в RenderScene. ну я и взяла ее :-)
как дура...


 
ancara ©   (2006-12-01 15:13) [73]


> аккуратно вытирала первый объект

ну это конечно зря :)) ее ж надо было перед началом формирования кадра вызвать один раз, и все :))

> как дура

нуну, не надо так себя :)) самокритика хороша, когда в меру :))


 
Creative   (2006-12-01 15:23) [74]

Как здорово все получилось!
Ну, до следующего косяка :-)


 
ancara ©   (2006-12-01 15:29) [75]


> до следующего косяка :-)

<затягиваясь> Угу, позовешь если что...


 
Creative   (2006-12-01 15:31) [76]


> <затягиваясь>


Я не эту гадость имела в виду :-(
Но позвать - позову


 
Creative   (2006-12-01 15:49) [77]

Еще кстати вопрос:
1. я использую дисплейный список.
2. внутри списка я использую поле объекта F.
3. я создаю два объекта с разными значениями поля - F1 и F2.
4. в конструкторе у меня стоит BuildList (F: real - переменная, которая будет принимать в себя значение поля создаваемого объекта)
5. в рендере дисплейный список вызывается glCallList(ListID).

Проблема вот какая: glCallList(ListID) - это ведь уже стандартная процедура, к ней не убавишь, не прибавишь, и получается, что она вызывает список в том виде, в котором он был заполнен для _последнего объекта_.
Я правильно поняла, что именно поэтому все объекты рисуются одинаково (с точки зрения тех переменных, которые находятся внутри BuildList)?


 
Creative   (2006-12-01 15:50) [78]

Это же глупо - создавать два идентичных дисплейных списка просто потому, что они различаются на одну цифру?


 
Creative   (2006-12-01 15:52) [79]

И еще: мне только что пришло в голову: а почему не написать просто процедуру, например DrawSphere и не вызывать в рендере объекта именно ее, а не Список? В чем принципиальное отличие Списка от процедуры?


 
ancara ©   (2006-12-01 16:15) [80]


> Это же глупо - создавать два идентичных дисплейных списка
> просто потому, что они различаются на одну цифру?

Нет, гораздо глупее пересобирать один и тот же список для разных объектов :)) Для каждого объекта нужно выделить отдельный персональный список :))

> В чем принципиальное отличие Списка от процедуры?

ну если рендер сделать просто в процедуре то, очевидно, там будут содержаться такие команды:
 glVertex(x,y,z);
 glNormal(x,y,z);
 и т.д.,
т.е. мы будем по-очереди, каждую вершину с ее координатами, нормалями, текстрными координатами и пр. загонять в видеокарту.
А она, в свою очередь, при получении очередного полигона, почти мгновенно его расчитает (нарисует) и будет ждать, пока мы подготовим следующий и пропихнем его по медленной AGP (PCI-E) шине.
 И так каждый кадр, даже если ничего не поменялось, мы опять будем гнать те же данные снова и снова, в итоге FPS -> к нулю..
 Когда мы используем список, а точнее его собираем (glNewList и так далее..)
мы проделываем те же операции, т.е. гоним данные по шине в видеокарту, но мы делаем это только один раз (в идеальном случае), а в момент рендеринга мы просто вызываем соответствующий дисплейный список (glCallList), т.е. мы всего лишь просим видеокарту отрисовать некий объект (совокупность полигонов) который уже находится в ней, и это происходит значительно быстрее.


 
Creative   (2006-12-01 16:20) [81]

то есть если у меня две сферы отличаются по одному единственному параметру (например ZenitStop) - мне тем не менее имеет смысл собрать два разных списка их рисования? Я правильно поняла?


 
ancara ©   (2006-12-01 16:28) [82]


> Я правильно поняла?

угу

А VBO еще круче :))
Например: мы пытаемся рисовать какой-то ландшафт сеткой полигонов.
Один раз его загнали в видюху и рисуем оттуда.
Затем, в некий момент нам понадобилось изменить координаты парочки вершин.
Если мы будем использовать дисплейный список, то в этой ситуации мы будем вынуждены пересобрать список заново и целиком его залить в видеокарту.
 VBO же нам позволяет кинуть туда только эти отдельные вершины, в памяти видеокарты они заменять старые и мы снова сможем приступить к рисованию и займет эта процедура считаные такты :))


 
Creative   (2006-12-01 16:31) [83]

Звучит здорово, а что такое VBO?


 
ancara ©   (2006-12-01 16:40) [84]

VBO = Vertex Buffer Object, но это уже из разряда расширений, на первых порах нужно освоить базовые возможности OpenGL, например списки эти опять же, или DrawArray, DrawElement... Главное понимать что происходить после тех или иных манипуляций.


 
Creative   (2006-12-01 16:45) [85]

Ок, отложим на более позднее время.

А насчет списков я подумала, если у меня всего пара объектов рисуется - можно и процедурой обойтись, я ж не игрушку рисую. А вот когда рисования будет действительно много - тогда да.


 
ancara ©   (2006-12-01 16:55) [86]


> если у меня всего пара объектов рисуется - можно и процедурой
> обойтись

Если рассматривать конкретно этот случай можно и так сказать, но тем неменее, использование более экономичных и более производительных средств даже в примитивных задачах поможет выработать "хороший тон" программирования.
 Ведь хороший капитан, например, ведет судно одинаково профессионально, назависимо от того, сколько пассажиров находится на борту, два или пятьсот? :))


 
Creative   (2006-12-01 17:05) [87]

Хм...хорошо сказано, я это учту, спасибо. Как говорится, береги честь смолоду. :-)

Я сейчас вот о чем думаю: изначально у тебя по движению мыши сфера крутится по двум осям. Все было бы хорошо, кабы осей не было три. Я попробовала было посадить вращение на [мышь] + [кнопка, соответствующая оси], но наткнулась на такое затруднение: если я правильно поняла, в процессе вращения система координат сферы перекашивается и поэтому когда ты в следующий раз крутишь ее например по оси Z - ты уже с трудом понимаешь, в какое место попадет твоя сфера. Есть какой-то умный способ реализовать такую вещь?
Я например хочу как-то добиться, либо, чтобы сфера перемещалась таким образом - координаты ее зенита должны в точности следовать перемещениям курсора, либо, чтобы пользователь мог просто ввести например широту и долготу, и сфера сама по ним бы вставала.


 
ancara ©   (2006-12-01 17:25) [88]


> чтобы пользователь мог просто ввести например широту и долготу,
>  и сфера сама по ним бы вставала.

Я понял! Ты работаешь в Google и вы там делаете новую GoogleEarth! Я угадал? :))


> изначально у тебя по движению мыши сфера крутится по двум
> осям

Стоп, стоп, изначально у меня камера перемещалась вокруг центра координат, а объекты были неподвижны. Но учитывая, что у тебя при движении мышки изменяются FAlpha и FBeta и при рендеринге каждая сфера поворачивается на эти свои углы, то получается что крутится сфера, да.


> Все было бы хорошо, кабы осей не было три.

А тебе действительно надо крутить вокрух трех осей? Я в том смысле что двумя  не обойтись? Вроде как можно крутить вокруг оси, проходящей через полюса и  вокруг оси, лежащей в плоскости экватора и проходящей через нулевой меридиан, например.
 Или можно вокруг оси, проходящей через полюса и  вокруг оси, лежащей в плоскости, параллельно плоскости "экрана", и проходящей черех центр сферы, но тогда этот вектор придется постоянно расчитывать, исходя из того, "каким боком", повернут шарик к наблюдателю. Этот вектор будет результатом векторного произведения двух векторов:
V1 - лежит на оси, кот. проходит через полюса;
V2 - лежит на прямой, проходящей через камеру и центр сферы;


 
Creative   (2006-12-01 17:33) [89]

Я понял! Ты работаешь в Google и вы там делаете новую GoogleEarth! Я угадал?

нет, я всего-навсего пишу программку, которая помогла бы мне расставить базу в игрушке UFO: XCOM :-)

А тебе действительно надо крутить вокрух трех осей? Я в том смысле что двумя  не обойтись? Вроде как можно крутить вокруг оси, проходящей через полюса и  вокруг оси, лежащей в плоскости экватора и проходящей через нулевой меридиан, например.

Что мне действительно нужно - так это чтобы зенит полусферы мог встать в абсолютно любой точке сферы. И если я не глючу - кручением по двум осям тут не обойтись. Но это я если я не глючу!


 
DesWind ©   (2006-12-02 00:58) [90]

В Хсом там вродь тока вокруг одной можно, но ее ставят поразному


 
Creative   (2006-12-04 10:07) [91]


> В Хсом там вродь тока вокруг одной можно, но ее ставят поразному


В самом то ХСом - да, но я хочу, чтобы модель можно было посмотреть с любой стороны, потому что кто как, а я задолбалась ПВО ставить.


 
Creative   (2006-12-04 10:09) [92]


to ancara


Я таки поняла что наглючила - поворотом по двум осям можно попасть в любую точку сферы. :-)


 
Creative   (2006-12-04 16:52) [93]

Продолжение вопросов. Если сферу вращать (не важно по скольки осям) - ее система координат кувыракается вместе с ней. То есть стоит нам повернуть ее на А градусов по Х, как наши оси Y и Z уже окажутся черт те где, и когда мы захотим повернуть сферу по Z например, она окажется совсем не там, где мы хотим. Как заставить сферу вращаться в фиксированной системе координат: чтобы если я говорю "поворот по Y на положительный градус" - я ВСЕГДА видела как ее зенит движется вокруг строго вертикальной оси?
Звучит наверное непонятно, но я лучше не могу. :-(


 
ancara ©   (2006-12-04 17:21) [94]


> Звучит наверное непонятно,

Да все прекрасно понятно, там ничего сложного: после того как повернула вокруг оси X на угол А, надо посчитать "вертикальный" вектор и он будет равен:
( 0.0, cos(A), sin(A) ), A надо конечно же сначала привести к радианам. Вот вокруг этого вектора и крути... Но это при условии, что первый поворот был вокруг оси X, если же вокруг Z то вектор будет другим, а если вокруг и X и Z, то тем более :))


 
ancara ©   (2006-12-04 17:24) [95]

А можно еще проще, сначала крути вокруг Y, потом вокруг X


 
Creative   (2006-12-05 10:08) [96]

Не я лучше углы посчитаю, а то гадай еще, где у меня Х теперь находится. Я хочу сделать максимально наглядно.

>Вот вокруг этого вектора и крути...

А как мне выбирать - вокруг которго вектора крутить? Я знаю только glRotate(0, 0.0, 0.0, 0.0, 0.0), но она вроде не располагает к выбору конкретных векторов.


 
ancara ©   (2006-12-05 10:56) [97]


> А как мне выбирать - вокруг которго вектора крутить? Я знаю
> только glRotate(0, 0.0, 0.0, 0.0, 0.0), но она вроде не
> располагает к выбору конкретных векторов.

Кури пост N37.


 
Creative   (2006-12-05 11:10) [98]

>это будет поворот вокруг вектора (1.0, 0.0, 1.0) на 30 градусов. Причем OpenGL этот вектор еще и нормализует

оно?


 
ancara ©   (2006-12-05 11:45) [99]

оно самое:)


 
Creative   (2006-12-05 12:30) [100]

О пользе образования: начала читать статью про кватернионы. Запустила проект и погоняв его, увидела, что все нормально вращается и вокруг двух осей и все предсказуемо и ничего такого страшного рассчитывать кажется не придется. Не понимаю...:-)



Страницы: 1 2 3 вся ветка

Форум: "Игры";
Текущий архив: 2008.01.13;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.77 MB
Время: 0.01 c
2-1197276817
Razrab
2007-12-10 11:53
2008.01.13
Быстрый вывод в текстовый файл?


15-1196978196
Lip
2007-12-07 00:56
2008.01.13
Delphi -> Console Application


15-1197011581
TUser
2007-12-07 10:13
2008.01.13
ICQ-рассылки


2-1197644076
J@ME
2007-12-14 17:54
2008.01.13
Сортировка Бэтчера


15-1194860207
TIF
2007-11-12 12:36
2008.01.13
Vista глюканулась. В чём проблема?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский