Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник по GLScene.doc
Скачиваний:
253
Добавлен:
16.12.2018
Размер:
7.18 Mб
Скачать

Глава 47. Практика. Создание мира

Здесь описывается создание земли, по земле ходит актёр с камерой от первого и третьего лица, вокруг растут деревья, а под деревьями грибы. Ну и ещё сделаем воду.

Начали. Поместим на форму:

GLScene1, GLSceneViewer1, GLCadencer1, GLNavigator1, GLUserInterface1, GLMaterialLibrary1, GLBitmapHDS1, Timer1.

Пользуясь рисунком, создадим необходимые компоненты.

Имя

Свойство

Значение

GLSceneViewer1

Camera

GLCamera2

Align

alClient

GLNavigator1

MovingObject

DummyCube2

UseVirtualUp

True

VirtualUp

X=0, Y=0, Z=1

GLUserInterface1

GLNavigator

GLNavigator1

MouseSpeed

20

GLCadencer1

Scene

GLScene1

GLWaterPlane1

Position

X=-25, Y=-100, Z=-40

Scale

X=2000, Y=2000, Z=2000

Elastic

5

RainForce

80

RainTimeInterval

1

Up

X=0, Y=0, Z=1

GLLightSource1

Position

X=50, Y=50, Z=50

FreeForm1

Position

X=10, Y=10, Z=5

Scale

X=5, Y=5, Z=5

Up

X=0, Y=1, Z=0

DummyCube2

Up

X=0, Y=0, Z=1

GLCamera1

DepthOfView

2000

Up

X=0, Y=1, Z=0

В таблице приведены имена компонентов и свойства, которые нужно у них изменить.

Position

X=0, Y=3, Z=0

GLCamera2

DepthOfView

2000

Position

X=0, Y=2, Z=-5

Up

X=0, Y=0, Z=1

TargetObject

DummyCube2

Actor1

Up

X=1, Y=0, Z=0

Material

Texture-убрать галочку с Disable

Actor2

Material

Texture-убрать галочку с Disable

GLTerrainRenderer1

HeightDataSource

GLBitmapHDS1

Material

Texture-убрать галочку с Disable

Scale

X=10, Y=10, Z=0,5

Up

X=0, Y=1, Z=0

FreeForm2

Scale

X= 0,01, Y= 0,01, Z= 0,01

Up

X=0, Y=1, Z=0

Кое что поясню по поводу создания воды.

Рекомендую поэкспериментировать с цветом у GLWaterPlane1. Зайдите в свойство Material у GLWaterPlane1 поставьте BlendingMode в bmTransparency чтобы изменения цвета отображались на воде. Для придания реалистичности сделайте воду прозрачной, для этого щёлкните на Front, и на вкладке Diffuse изменяйте свойство Alpha. С помощью свойства Elastic меняется эластичность воды, чем больше, тем менее эластична. Свойство RainTimeInterval регулирует, с какой частотой появляются капли на воде. Свойство RainForce регулирует силу волны. Замечание: при изменении какого-либо параметра изменения отображаются на воде только после второй компиляции(по крайней мере у меня так).

Ну что ж начнём кодить. Сначала подключим необходимые модули.

uses jpeg, tga, GLkeyboard, VectorGeometry,GLFile3DS, GLFilemd2;

Далее объявим переменные:

public

interpolated_height : single;

GLTree : TGLTree; // наше дерево

proxy, proxy2 : TGLProxyObject; // прокси объекты дерева и гриба

end;

Следом загружаем всё необходимое, в OnCreate для формы пишем

procedure TForm1.FormCreate(Sender: TObject);

begin

// загружаем модель гриба

FreeForm2.LoadFromFile('media\mushroom.3ds');

// загружаем карту высот для нашей земли

GLBitmapHDS1.Picture.LoadFromFile('media\terrain.bmp');

// загружаем текстуру на землю

GLTerrainRenderer1.Material.Texture.Image.LoadFromFile('media\clover.jpg');

// модель актёра

Actor1.LoadFromFile('media\waste.md2');

// его текстура

Actor1.Material.Texture.Image.LoadFromFile('media\waste.jpg');

// его анимация

Actor1.Animations.LoadFromFile('media\Quake2Animations.aaf');

// делаем его поменьше

Actor1.Scale.SetVector(0.04, 0.04, 0.04, 0);

// грузим нашему актёру пушку

Actor2.LoadFromFile('media\WeaponWaste.md2');

// и текстуру к ней

Actor2.Material.Texture.Image.LoadFromFile('media\WeaponWaste.jpg');

// анимация проигрывается циклически

Actor1.AnimationMode:=aamLoop;

// проигрывать анимацию стоять на месте

Actor1.SwitchToAnimation('stand');

// активируем управление мышкой

GLUserInterface1.MouseLookActivate;

// далее добавляем текстуру для нашего дерева

with GLMaterialLibrary1.AddTextureMaterial('LeafFront','media\maple_multi.tga') do begin

Material.BlendingMode:=bmAlphaTest50;

Material.Texture.TextureMode:=tmModulate; Material.Texture.TextureFormat:=tfRGBA;

end;

with GLMaterialLibrary1.AddTextureMaterial('LeafBack','media\maple_multi.tga') do begin

Material.BlendingMode:=bmAlphaTest50;

Material.Texture.TextureMode:=tmModulate; Material.Texture.TextureFormat:=tfRGBA;

end;

with GLMaterialLibrary1.AddTextureMaterial('Branch','media\zbark_016.jpg') do begin

Material.Texture.TextureMode:=tmModulate;

end;

// добавляем прокси-деревья

AddTree;

// добавляем дерево

NewTree;

// добавляем грибы

AddMuroms; End;

Пора добавить дерево на сцену, для этого создадим процедуру NewTree:

procedure TForm1.NewTree;

begin

// добавляем во FreeForm1 дерево GLTree:=TGLTree(FreeForm1.AddNewChild(TGLTree)); with GLTree do

begin

// присваиваем библиотеку материалов и берём от туда текстуры MaterialLibrary:=GLMaterialLibrary1; LeafMaterialName:='LeafFront'; LeafBackMaterialName:='LeafBack'; BranchMaterialName:='Branch';

// размеры

Depth:=8;

// регулирует насколько дерево заросло листьями

LeafSize:=0.2;

// толщина ствола

BranchRadius:=0.08; BranchNoise:=0.5; Randomize;

Seed:=Round((2*Random-1)*(MaxInt-1));

end;

end;

Но что нам одно дерево, наделаем ему двойников, и оп ять нужно писать новую процедуру

AddTree

procedure TForm1.AddTree;

var i : Integer;

s : TVector;

f : Single;

begin

// создаём 50 клонов одного дерева которое содержится у нас во FreeForm1

for i:=0 to 50 do begin

//Создаем очередной прокси-объект

proxy:=TGLProxyObject(DummyCube1.AddNewChild(TGLProxyObject));

with proxy do begin

// наследуем только структуру объекта

ProxyOptions:=[pooObjects];

//В свойство MasterObject записываем наше дерево образец

MasterObject:=FreeForm1;

// ориентация в пространстве такая же как и у FreeForm1

Direction:=FreeForm1.Direction; Up:=FreeForm1.Up;

// делаем разной величины s:=FreeForm1.Scale.AsVector; f:=(1*Random+1); ScaleVector(s, f); Scale.AsVector:=s;

// позиция по X и Y выбирается случайно для каждого созданного //прокси-объекта

Position.SetPoint(random(990), random(990), interpolated_height);

// случайно поворачиваем на какой-то угол RollAngle:=Random(360); TransformationChanged;

// объясню дальше

with proxy.Position do begin interpolated_height:=GLTerrainRenderer1.InterpolatedHeight(AsVector);

if Z<>interpolated_height then

Z:=interpolated_height; FreeForm1.Position:=proxy.Position; end;

end;

end;

end;

with proxy.Position do begin

interpolated_height:=GLTerrainRenderer1.InterpolatedHeight(AsVector);

if Z<>interpolated_height then

Z:=interpolated_height;

end;

Этот блок нужен для того что бы деревья располагались не абы как а точно по склонам созданной нами земли. Объясняю как он работает, создаётся очередной прокси объект, X и Y выбираются случайно, а Z присваивается значение interpolated_height которое в свою очередь считается так. Сначала определяем высоту земли в точке в которую ставим дерево, потом смотрим если Z больше или меньше этой высоты то присваиваем Z значение высоты. А строчка FreeForm1.Position:=proxy.Position; нужна чтобы наше первое дерево не висело в воздухе.

Всё деревья растут точно по склонам надо теперь чтобы и грибы росли также эта процедура аналогична предыдущей(разве что грибов 100) так что объяснять я не буду:

procedure TForm1.AddMuroms;

var i : Integer; s : TVector; f : Single;

begin

for i:=0 to 100 do begin proxy2:=TGLProxyObject(DummyCube3.AddNewChild(TGLProxyObject));

with proxy2 do

begin ProxyOptions:=[pooObjects]; MasterObject:=FreeForm2;

Direction:=FreeForm2.Direction; Up:=FreeForm2.Up;

s:=FreeForm2.Scale.AsVector; f:=(1*Random+1); ScaleVector(s, f); Scale.AsVector:=s;

Position.SetPoint(random(1000)-(1000/5), random(1000)-(1000/5), interpolated_height);

RollAngle:=Random(360); TransformationChanged;

with proxy2.Position do begin interpolated_height:=GLTerrainRenderer1.InterpolatedHeight(AsVector);

if Z<>interpolated_height+0.2 then Z:=interpolated_height+0.2; FreeForm2.Position:=proxy2.Position; end;

end;

end;

end;

Здесь лишь отмечу следующее

Position.SetPoint(random(1000)-(1000/5), random(1000)-(1000/5), interpolated_height);

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

Ну вот и готово, растительность расставлена по местам осталось научить ходить нашего героя для этого у GLCadencer1 в OnProgress запишем следующее:

procedure TForm1.GLCadencer1Progress(Sender: TObject; const deltaTime, newTime: Double);

var

speed : Single;

moving: string;

begin

// проигрывается анимация стоять на месте

moving:='stand';

// при нажатии на шифт интервал с которой проигрывается анимация //=100 т.е играется быстрей

if IsKeyDown(VK_SHIFT) then begin

Actor1.Interval:=100;

// бежим быстрей

speed:=50*deltaTime

// а иначе анимация замедляется

end else begin Actor1.Interval:=150;

// Бежим медленнее

speed:=10*deltaTime;

end;

// при нажатии на Q переключаем камеру2 на камеру1

if IsKeyDown('q') then begin GLSceneViewer1.Camera:=GLCamera1; end;

// при нажатии на E переключаем камеру1 на камеру2

if IsKeyDown('E') then begin GLSceneViewer1.Camera:=GLCamera2; end;

// при нажатии на W бежим вперёд

if IsKeyDown('w') then begin GLNavigator1.MoveForward(speed);

// играется анимация бежать

moving:='run';

end;

// при нажатии на S бежим назад

if IsKeyDown('s') then begin

GLNavigator1.MoveForward(-speed);

// анимация бежать moving:='run'; end;

// при нажатии на A передвинуться влево

if IsKeyDown('a') then begin

GLNavigator1.StrafeHorizontal(-speed);

moving:='run';

end;

// при нажатии на D бежим вправо

if IsKeyDown('d') then begin GLNavigator1.StrafeHorizontal(speed); moving:='run';

end;

// при нажатии на эскейп завершаем программу

if IsKeyDown(VK_ESCAPE) then Close;

// пояснения далее

with DummyCube2.Position do begin interpolated_height:=GLTerrainRenderer1.InterpolatedHeight(AsVector);

if Z<>interpolated_height then

Z:=interpolated_height+1;

end;

if Actor1.CurrentAnimation<>moving then

// играть анимацию которая находиться в Moving

Actor1.SwitchToAnimation(moving);

// синхронизируемся с первым актёром

Actor2.Synchronize(Actor1); GLUserInterface1.MouseUpdate; GLUserInterface1.MouseLook; end;

Этот блок

with DummyCube2.Position do begin interpolated_height:=GLTerrainRenderer1.InterpolatedHeight(AsVector);

if Z<>interpolated_height then

Z:=interpolated_height+1;

end;

служит для того чтобы наш герой не ломился сквозь холмы, а честно ходил по ним. Работает это так же как и в случае с proxy за одним лишь исключением что здесь мы прибавляем 1 если позиция по Z думмикуба <> interpolated_height . Делается это для того чтобы наш герой не стоял по пояс в земле. Добавим ещё кое что, а именно регулирование расстояния до актёра в виде от третьего лица колесиком мыши. Пишем в FormMouseWheel у Form1:

procedure TForm1.FormMouseWheel(Sender: TObject; Shift: TShiftState;

WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);

begin

GLCamera2.AdjustDistanceToTarget(Power(1.1, WheelDelta/120));

end;

Ну и последний штрих в виде подсчёта фпс. В OnTimer у Timer1 пишем:

procedure TForm1.Timer1Timer(Sender: TObject);

begin

Caption:=Format('%.2f FPS', [GLSceneViewer1.FramesPerSecond]); GLSceneViewer1.ResetPerformanceMonitor;

end;

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