Refactor spatial, add documentation

This commit is contained in:
Dane Johnson 2021-01-26 23:28:20 -06:00
parent 62059079cd
commit d6db1c16c7
9 changed files with 293 additions and 73 deletions

View File

@ -1,11 +1,35 @@
/**
@file
@author Dane Johnson <dane@danejohnson.org>
@section LICENSE
Couch Copyright (C) 2021 Dane Johnson
This program comes with ABSOLUTELY NO WARRANTY; without event the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for details at
https://www.gnu.org/licenses/gpl-3.0.html
This is free software, and you are welcome to redistribute it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
@section DESCRIPTION
This file defines the cameras that can be used to render the scene
*/
#ifndef CAMERA_H #ifndef CAMERA_H
#define CAMERA_H #define CAMERA_H
#include "Transform.h" #include "Spatial.h"
class Camera { /**
The common 3D camera
*/
class Camera : public Spatial {
public: public:
Transform transform;
Camera(); Camera();
void MakeCurrent(); void MakeCurrent();
static Camera *GetCurrentCamera(); static Camera *GetCurrentCamera();

View File

@ -1,8 +1,7 @@
/** /*
@file Dane Johnson <dane@danejohnson.org>
@author Dane Johnson <dane@danejohnson.org>
@section LICENSE LICENSE
Couch Copyright (C) 2021 Dane Johnson Couch Copyright (C) 2021 Dane Johnson
@ -16,7 +15,7 @@
by the Free Software Foundation; either version 3 of the License, by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version. or (at your option) any later version.
@section DESCRIPTION DESCRIPTION
Node is the parent class for all classes that would be in the scene Node is the parent class for all classes that would be in the scene
tree. The root of the scene tree is always a node. tree. The root of the scene tree is always a node.

View File

@ -56,23 +56,26 @@ RigidbodyMotionState::RigidbodyMotionState(Rigidbody *rigidbody) {
} }
void RigidbodyMotionState::getWorldTransform(btTransform &worldTrans) const { void RigidbodyMotionState::getWorldTransform(btTransform &worldTrans) const {
Transform transform = rigidbody->GetTransform();
worldTrans.setOrigin(btVector3( worldTrans.setOrigin(btVector3(
rigidbody->transform.position.x, transform.position.x,
rigidbody->transform.position.y, transform.position.y,
rigidbody->transform.position.z)); transform.position.z));
btQuaternion quat; btQuaternion quat;
quat.setEuler(rigidbody->transform.rotation.z, quat.setEuler(transform.rotation.z,
rigidbody->transform.rotation.y, transform.rotation.y,
rigidbody->transform.rotation.x); transform.rotation.x);
worldTrans.setRotation(quat); worldTrans.setRotation(quat);
} }
void RigidbodyMotionState::setWorldTransform(const btTransform &worldTrans) { void RigidbodyMotionState::setWorldTransform(const btTransform &worldTrans) {
rigidbody->transform.position = Vector3(worldTrans.getOrigin().getX(), Transform transform = rigidbody->GetTransform();
transform.position = Vector3(worldTrans.getOrigin().getX(),
worldTrans.getOrigin().getY(), worldTrans.getOrigin().getY(),
worldTrans.getOrigin().getZ()); worldTrans.getOrigin().getZ());
worldTrans.getRotation().getEulerZYX(rigidbody->transform.rotation.z, worldTrans.getRotation().getEulerZYX(transform.rotation.z,
rigidbody->transform.rotation.y, transform.rotation.y,
rigidbody->transform.rotation.x); transform.rotation.x);
rigidbody->SetTransform(transform);
} }

View File

@ -1,11 +1,71 @@
/*
Dane Johnson <dane@danejohnson.org>
LICENSE
Couch Copyright (C) 2021 Dane Johnson
This program comes with ABSOLUTELY NO WARRANTY; without event the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for details at
https://www.gnu.org/licenses/gpl-3.0.html
This is free software, and you are welcome to redistribute it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
DESCRIPTION
A spatial is a node with a transform property, i.e. position, scale or rotation.
They can be instanced on the scene tree as an anchor for some other nodes.
*/
#include "Spatial.h" #include "Spatial.h"
Name Spatial::GetType() const {return "Spatial";} Name Spatial::GetType() const {return "Spatial";}
Transform Spatial::GetTransform() {
return transform;
}
void Spatial::SetTransform(Transform transform) {
this->transform = transform;
}
Spatial *Spatial::Create() { Spatial *Spatial::Create() {
return new Spatial; return new Spatial;
} }
void Spatial::Translate(Vector3 offset) {
Transform t = this->GetTransform();
t.position += offset;
this->SetTransform(t);
}
void Spatial::RotateX(cfloat phi) {
Transform t = this->GetTransform();
t.rotation.x += phi;
this->SetTransform(t);
}
void Spatial::RotateY(cfloat phi) {
Transform t = this->GetTransform();
t.rotation.y += phi;
this->SetTransform(t);
}
void Spatial::RotateZ(cfloat phi) {
Transform t = this->GetTransform();
t.rotation.z += phi;
this->SetTransform(t);
}
void Spatial::UniformScale(cfloat scale) {
Transform t = this->GetTransform();
t.scale *= scale;
this->SetTransform(t);
}
Spatial *Spatial::Duplicate() { Spatial *Spatial::Duplicate() {
Spatial *spatial = static_cast<Spatial*>(Node::Duplicate()); Spatial *spatial = static_cast<Spatial*>(Node::Duplicate());
spatial->transform = transform; spatial->transform = transform;

View File

@ -1,17 +1,83 @@
/**
@file
@author Dane Johnson <dane@danejohnson.org>
@section LICENSE
Couch Copyright (C) 2021 Dane Johnson
This program comes with ABSOLUTELY NO WARRANTY; without event the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for details at
https://www.gnu.org/licenses/gpl-3.0.html
This is free software, and you are welcome to redistribute it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
@section DESCRIPTION
A spatial is a node with a transform property, i.e. position, scale or rotation.
They can be instanced on the scene tree as an anchor for some other nodes.
*/
#ifndef SPATIAL_H #ifndef SPATIAL_H
#define SPATIAL_H #define SPATIAL_H
#include "types.h"
#include "Node.h" #include "Node.h"
#include "Transform.h" #include "Transform.h"
/**
Spatial nodes have a transform property. They can be subclassed or instanced
as an anchor for their children.
*/
class Spatial : public Node { class Spatial : public Node {
public: public:
Transform transform;
virtual bool IsTransformable() const { return true;}
virtual Name GetType() const; virtual Name GetType() const;
/**
Gets the transform property of this spatial
@return The transform
*/
Transform GetTransform();
/**
Sets the transform property of this spatial.
@param transform The transform property
*/
void SetTransform(Transform transform);
/**
Directly translates the spatial by offset
@param offset The offset of the transform operation
*/
void Translate(Vector3 offset);
/**
Rotates the Camera phi radians about the X axis
@param phi The amount to rotate in radians
*/
void RotateX(cfloat phi);
/**
Rotates the Camera phi radians about the Y axis
@param phi The amount to rotate in radians
*/
void RotateY(cfloat phi);
/**
Rotates the Camera phi radians about the Z axis
@param phi The amount to rotate in radians
*/
void RotateZ(cfloat phi);
/**
Scales the spatial by scale uniformly
@param scale The amount to scale by.
*/
void UniformScale(cfloat scale);
virtual Spatial *Create(); virtual Spatial *Create();
virtual Spatial *Duplicate(); virtual Spatial *Duplicate();
virtual Spatial *Instance(); virtual Spatial *Instance();
private:
Transform transform;
}; };
#endif /* SPATIAL_H */ #endif /* SPATIAL_H */

View File

@ -1,4 +1,26 @@
/*
Dane Johnson <dane@danejohnson.org>
LICENSE
Couch Copyright (C) 2021 Dane Johnson
This program comes with ABSOLUTELY NO WARRANTY; without event the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for details at
https://www.gnu.org/licenses/gpl-3.0.html
This is free software, and you are welcome to redistribute it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
DESCRIPTION
A transform represents various aspects of 3d space.
*/
#include "Transform.h" #include "Transform.h"
#include <glm/gtc/matrix_transform.hpp>
Transform::Transform() { Transform::Transform() {
position = Vector3(0.0f); position = Vector3(0.0f);
@ -19,21 +41,6 @@ Transform::Transform(Vector3 position, Vector3 rotation, Vector3 scale) {
} }
void Transform::Translate(cfloat x, cfloat y, cfloat z) {
position = position + Vector3(x, y, z);
}
Matrix Transform::RotationMatrix() {
Matrix mat(1.0f);
mat = glm::rotate(mat, this->rotation.z, Vector3(0.0f, 0.0f, 1.0f));
mat = glm::rotate(mat, this->rotation.y, Vector3(0.0f, 1.0f, 0.0f));
mat = glm::rotate(mat, this->rotation.x, Vector3(1.0f, 0.0f, 0.0f));
return mat;
}
Vector3 Transform::Forward() { Vector3 Transform::Forward() {
return glm::vec3(RotationMatrix() * glm::vec4(0.0f, 0.0f, -1.0f, 1.0f)); return glm::vec3(RotationMatrix() * glm::vec4(0.0f, 0.0f, -1.0f, 1.0f));
} }
@ -45,3 +52,13 @@ Vector3 Transform::Up() {
Vector3 Transform::Right() { Vector3 Transform::Right() {
return glm::vec3(RotationMatrix() * glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); return glm::vec3(RotationMatrix() * glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
} }
Matrix Transform::RotationMatrix() {
Matrix mat(1.0f);
mat = glm::rotate(mat, this->rotation.z, Vector3(0.0f, 0.0f, 1.0f));
mat = glm::rotate(mat, this->rotation.y, Vector3(0.0f, 1.0f, 0.0f));
mat = glm::rotate(mat, this->rotation.x, Vector3(1.0f, 0.0f, 0.0f));
return mat;
}

View File

@ -1,21 +1,65 @@
/**
@file
@author Dane Johnson <dane@danejohnson.org>
@section LICENSE
Couch Copyright (C) 2021 Dane Johnson
This program comes with ABSOLUTELY NO WARRANTY; without event the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for details at
https://www.gnu.org/licenses/gpl-3.0.html
This is free software, and you are welcome to redistribute it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
@section DESCRIPTION
A transform represents various aspects of 3d space.
*/
#ifndef TRANSFORM_H #ifndef TRANSFORM_H
#define TRANSFORM_H #define TRANSFORM_H
#include <glm/gtc/matrix_transform.hpp>
#include "types.h" #include "types.h"
/**
A Transform represents a position, rotation, and scale in 3D space.
*/
struct Transform { struct Transform {
Transform(); Transform();
Transform(Vector3 position, Vector3 rotation); Transform(Vector3 position, Vector3 rotation);
Transform(Vector3 position, Vector3 rotation, Vector3 scale); Transform(Vector3 position, Vector3 rotation, Vector3 scale);
/**
The position, according to the left-hand rule
*/
Vector3 position; Vector3 position;
/**
The rotation, in Euler angles
*/
Vector3 rotation; Vector3 rotation;
/**
Scaling along the specified axis
*/
Vector3 scale; Vector3 scale;
void Translate(cfloat x, cfloat y, cfloat z); /**
Returns a vector that is -Z, rotated by @ref rotation.
*/
Vector3 Forward(); Vector3 Forward();
/**
Returns a vector that is +X, rotated by @ref rotation.
*/
Vector3 Right(); Vector3 Right();
/**
Returns a vector that is +Y, rotated by @ref rotation.
*/
Vector3 Up(); Vector3 Up();
/**
Returns a matrix that, when multiplied by a @ref Vector3
gives a Vector rotated by @ref rotation
*/
Matrix RotationMatrix(); Matrix RotationMatrix();
}; };

View File

@ -43,11 +43,12 @@ Node *root;
void render(Node *curr, Shader *shader, Matrix model) { void render(Node *curr, Shader *shader, Matrix model) {
Spatial *spatial = dynamic_cast<Spatial*>(curr); Spatial *spatial = dynamic_cast<Spatial*>(curr);
if (spatial) { if (spatial) {
model = glm::rotate(model, spatial->transform.rotation.x, Vector3(1.0f, 0.0f, 0.0f)); Transform transform = spatial->GetTransform();
model = glm::rotate(model, spatial->transform.rotation.y, Vector3(0.0f, 1.0f, 0.0f)); model = glm::rotate(model, transform.rotation.x, Vector3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, spatial->transform.rotation.z, Vector3(0.0f, 0.0f, 1.0f)); model = glm::rotate(model, transform.rotation.y, Vector3(0.0f, 1.0f, 0.0f));
model = glm::translate(model, spatial->transform.position); model = glm::rotate(model, transform.rotation.z, Vector3(0.0f, 0.0f, 1.0f));
model = glm::scale(model, spatial->transform.scale); model = glm::translate(model, transform.position);
model = glm::scale(model, transform.scale);
shader->UpdateModel(model); shader->UpdateModel(model);
shader->UpdateNormal(glm::mat3(glm::transpose(glm::inverse(model)))); shader->UpdateNormal(glm::mat3(glm::transpose(glm::inverse(model))));
} }
@ -126,10 +127,11 @@ int main() {
shader->UpdateProjection(projection); shader->UpdateProjection(projection);
Matrix view(1.0f); Matrix view(1.0f);
Camera *camera = Camera::GetCurrentCamera(); Camera *camera = Camera::GetCurrentCamera();
view = glm::rotate(view, -camera->transform.rotation.x, Vector3(1.0f, 0.0f, 0.0f)); Transform camera_transform = camera->GetTransform();
view = glm::rotate(view, -camera->transform.rotation.y, Vector3(0.0f, 1.0f, 0.0f)); view = glm::rotate(view, -camera_transform.rotation.x, Vector3(1.0f, 0.0f, 0.0f));
view = glm::rotate(view, -camera->transform.rotation.z, Vector3(0.0f, 0.0f, 1.0f)); view = glm::rotate(view, -camera_transform.rotation.y, Vector3(0.0f, 1.0f, 0.0f));
view = glm::translate(view, -camera->transform.position); view = glm::rotate(view, -camera_transform.rotation.z, Vector3(0.0f, 0.0f, 1.0f));
view = glm::translate(view, -camera_transform.position);
shader->UpdateView(view); shader->UpdateView(view);
// Find the lights // Find the lights

View File

@ -27,10 +27,11 @@ local light
function init() function init()
local material local material
local transform
camera = couch.Camera() camera = couch.Camera()
camera:MakeCurrent() camera:MakeCurrent()
camera.transform:Translate(0.0, 0.0, 10.0) camera:Translate(couch.Vector3(0.0, 0.0, 10.0))
local light = couch.DirectionalLight() local light = couch.DirectionalLight()
light.direction = couch.Vector3(0.0, -1.0, -1.0) light.direction = couch.Vector3(0.0, -1.0, -1.0)
@ -38,7 +39,6 @@ function init()
light.ambient = 0.2 light.ambient = 0.2
light.diffuse = 1.0 light.diffuse = 1.0
light.specular = 0.1 light.specular = 0.1
print(couch.Node.GetRoot().GetChildren)
couch.Node.GetRoot():AddChild(light:Instance()) couch.Node.GetRoot():AddChild(light:Instance())
local skybox = couch.Skybox.FromFiles( local skybox = couch.Skybox.FromFiles(
@ -58,7 +58,7 @@ function init()
material.diffuse = BLUE material.diffuse = BLUE
physics_ball_mesh:SetMaterial(0, material) physics_ball_mesh:SetMaterial(0, material)
physics_ball_prefab:AddChild(physics_ball_mesh); physics_ball_prefab:AddChild(physics_ball_mesh);
physics_ball_prefab.transform.position = couch.Vector3(0.0, 30.0, -10.0) physics_ball_prefab:Translate(couch.Vector3(0.0, 30.0, -10.0))
physics_ball = physics_ball_prefab:Instance() physics_ball = physics_ball_prefab:Instance()
couch.Node.GetRoot():AddChild(physics_ball) couch.Node.GetRoot():AddChild(physics_ball)
@ -75,7 +75,7 @@ function init()
character_body:SetCollisionShape(couch.CapsuleCollisionShape(1.0, 1.0)) character_body:SetCollisionShape(couch.CapsuleCollisionShape(1.0, 1.0))
character_body:SetCharacter(true) character_body:SetCharacter(true)
character_body:AddChild(character_prefab) character_body:AddChild(character_prefab)
character_body.transform.position = couch.Vector3(0.0, 3.0, 0.0) character_body:Translate(couch.Vector3(0.0, 3.0, 0.0))
character = character_body:Instance() character = character_body:Instance()
couch.Node.GetRoot():AddChild(character) couch.Node.GetRoot():AddChild(character)
@ -85,8 +85,8 @@ function init()
material.diffuse = RED material.diffuse = RED
cube_prefab:SetMaterial(0, material) cube_prefab:SetMaterial(0, material)
local orbiter = couch.Mesh.FromFile("ball.obj") local orbiter = couch.Mesh.FromFile("ball.obj")
orbiter.transform.scale = orbiter.transform.scale * 0.25; orbiter:UniformScale(0.25);
orbiter.transform:Translate(1.0, 0.0, 0.0) orbiter:Translate(couch.Vector3(1.0, 0.0, 0.0))
cube_prefab:AddChild(orbiter) cube_prefab:AddChild(orbiter)
cube = cube_prefab:Instance() cube = cube_prefab:Instance()
couch.Node.GetRoot():AddChild(cube) couch.Node.GetRoot():AddChild(cube)
@ -97,12 +97,12 @@ function init()
ball = ball_prefab:Instance() ball = ball_prefab:Instance()
couch.Node.GetRoot():AddChild(ball) couch.Node.GetRoot():AddChild(ball)
ball.transform:Translate(0.0, 3.0, 0.0) ball:Translate(couch.Vector3(0.0, 3.0, 0.0))
local trough_prefab = couch.TexturedMesh("trough.obj", "wood_lowres.png") local trough_prefab = couch.TexturedMesh("trough.obj", "wood_lowres.png")
trough = trough_prefab:Instance() trough = trough_prefab:Instance()
couch.Node.GetRoot():AddChild(trough) couch.Node.GetRoot():AddChild(trough)
trough.transform:Translate(10.0, 0.0, 0.0) trough:Translate(couch.Vector3(10.0, 0.0, 0.0))
local scaffold_prefab = couch.TexturedMesh("scaffold.obj", "grate_floor_lowres.png", "railing.png") local scaffold_prefab = couch.TexturedMesh("scaffold.obj", "grate_floor_lowres.png", "railing.png")
local scaffold = scaffold_prefab:Instance() local scaffold = scaffold_prefab:Instance()
@ -116,7 +116,7 @@ function init()
material.alphaScissor = 0.1 material.alphaScissor = 0.1
scaffold:SetMaterial(1, material) scaffold:SetMaterial(1, material)
couch.Node.GetRoot():AddChild(scaffold) couch.Node.GetRoot():AddChild(scaffold)
scaffold.transform:Translate(-3.0, 3.0, 0.0) scaffold:Translate(couch.Vector3(-3.0, 3.0, 0.0))
local barn_prefab = couch.TexturedMesh("barn.obj", "paintedwood.jpg", "barnroof_lowres.png", "wood_lowres.png") local barn_prefab = couch.TexturedMesh("barn.obj", "paintedwood.jpg", "barnroof_lowres.png", "wood_lowres.png")
local barn = barn_prefab:Instance() local barn = barn_prefab:Instance()
@ -127,35 +127,37 @@ function init()
material.cullBack = false material.cullBack = false
barn:SetMaterial(1, material) barn:SetMaterial(1, material)
couch.Node.GetRoot():AddChild(barn) couch.Node.GetRoot():AddChild(barn)
barn.transform:Translate(-15.0, 0.0, 0.0) barn:Translate(couch.Vector3(-15.0, 0.0, 0.0))
end end
function update(delta) function update(delta)
local move_vec = couch.Vector3() local move_vec = couch.Vector3()
move_vec = camera.transform.position + camera.transform:Forward() * delta * vz * SPEED local camera_transform = camera:GetTransform()
move_vec = move_vec + camera.transform:Right() * delta * vx * SPEED move_vec = camera_transform.position + camera_transform:Forward() * delta * vz * SPEED
move_vec = move_vec + camera.transform:Up() * delta * vy * SPEED move_vec = move_vec + camera_transform:Right() * delta * vx * SPEED
camera.transform.position = move_vec move_vec = move_vec + camera_transform:Up() * delta * vy * SPEED
camera_transform.position = move_vec
camera.transform.rotation.y = camera.transform.rotation.y - cam_rot_x * delta camera_transform.rotation.y = camera_transform.rotation.y - cam_rot_x * delta
cam_rot_x = 0.0 cam_rot_x = 0.0
camera.transform.rotation.x = camera.transform.rotation.x - cam_rot_y * delta camera_transform.rotation.x = camera_transform.rotation.x - cam_rot_y * delta
camera.transform.rotation.x = min(max(camera.transform.rotation.x, -3.14 / 2.0), 3.14 / 2.0) camera_transform.rotation.x = min(max(camera_transform.rotation.x, -3.14 / 2.0), 3.14 / 2.0)
cam_rot_y = 0.0 cam_rot_y = 0.0
local loc = ball.transform.position camera:SetTransform(camera_transform)
local loc = ball:GetTransform().position
if loc.y > 4.0 then if loc.y > 4.0 then
ballvy = -1.0 ballvy = -1.0
elseif loc.y < 2.0 then elseif loc.y < 2.0 then
ballvy = 1.0 ballvy = 1.0
end end
ball.transform.position.y = ball.transform.position.y + ballvy * delta ball:Translate(couch.Vector3(ballvy * delta, 0.0, 0.0))
cube.transform.rotation.y = cube.transform.rotation.y + 2.0 * delta; cube:RotateY(2.0 * delta)
cube.transform.rotation.z = cube.transform.rotation.z + 1.0 * delta; cube:RotateZ(1.0 * delta)
character:ApplyForce(character_move_vec * 10.0) character:ApplyForce(character_move_vec * 10.0)
print(character_move_vec.z)
end end
function action_dir(key, action, pos, neg, curr) function action_dir(key, action, pos, neg, curr)
@ -189,8 +191,11 @@ end
function make_ground() function make_ground()
local ground_prefab = couch.TexturedMesh("ground.obj", "grass_lowres.png") local ground_prefab = couch.TexturedMesh("ground.obj", "grass_lowres.png")
ground_prefab.transform.position = couch.Vector3(0.0, -2.0, 0.0)
ground_prefab.transform.scale = couch.Vector3(3.0, 1.0, 3.0) local ground_prefab_transform = ground_prefab:GetTransform()
ground_prefab_transform.position = couch.Vector3(0.0, -2.0, 0.0)
ground_prefab_transform.scale = couch.Vector3(3.0, 1.0, 3.0)
ground_prefab:SetTransform(ground_prefab_transform)
ground = couch.Spatial():Instance() ground = couch.Spatial():Instance()
couch.Node.GetRoot():AddChild(ground) couch.Node.GetRoot():AddChild(ground)
@ -199,13 +204,13 @@ function make_ground()
local ground_shape_prefab = couch.Rigidbody() local ground_shape_prefab = couch.Rigidbody()
ground_shape_prefab.mass = 0.0 ground_shape_prefab.mass = 0.0
ground_shape_prefab:SetCollisionShape(couch.BoxCollisionShape(180.0, 1.0, 180.0)) ground_shape_prefab:SetCollisionShape(couch.BoxCollisionShape(180.0, 1.0, 180.0))
ground_shape_prefab.transform:Translate(0.0, -2.5, 0.0) ground_shape_prefab:Translate(couch.Vector3(0.0, -2.5, 0.0))
ground:AddChild(ground_shape_prefab:Instance()) ground:AddChild(ground_shape_prefab:Instance())
for x = -20, 20, 1 do for x = -20, 20, 1 do
for z = -20, 20, 1 do for z = -20, 20, 1 do
local piece = ground_prefab:Instance() local piece = ground_prefab:Instance()
piece.transform.position = couch.Vector3(6.0 * x, -2.0, 6.0 * z) piece:Translate(couch.Vector3(6.0 * x, -2.0, 6.0 * z))
ground:AddChild(piece) ground:AddChild(piece)
end end
end end