Initial commit
This commit is contained in:
commit
4ffab16303
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.*
|
||||||
|
!.gitignore
|
||||||
|
*~
|
443
init.lua
Normal file
443
init.lua
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
-- Function to calculate the positions of nodes inside the box
|
||||||
|
-- box: A table containing yaw angle (in radians), dimensions, and the origin position
|
||||||
|
function get_nodes_in_box(box)
|
||||||
|
local yaw, dimensions, origin = box.yaw, box.dimensions, box.origin
|
||||||
|
local longitudinal, transverse, height = dimensions[1], dimensions[2], dimensions[3]
|
||||||
|
local nodes = {}
|
||||||
|
|
||||||
|
local sin_yaw = math.sin(yaw)
|
||||||
|
local cos_yaw = math.cos(yaw)
|
||||||
|
|
||||||
|
for x = -math.floor(longitudinal / 2), math.floor(longitudinal / 2) do
|
||||||
|
for y = 0, height - 1 do
|
||||||
|
for z = -math.floor(transverse / 2), math.floor(transverse / 2) do
|
||||||
|
local rotated_x = origin.x + (x * cos_yaw - z * sin_yaw)
|
||||||
|
local rotated_y = origin.y + y
|
||||||
|
local rotated_z = origin.z + (x * sin_yaw + z * cos_yaw)
|
||||||
|
|
||||||
|
local node_pos = {x = math.floor(rotated_x), y = math.floor(rotated_y), z = math.floor(rotated_z)}
|
||||||
|
table.insert(nodes, node_pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to get non-walkable nodes in the box
|
||||||
|
-- box: A table containing yaw angle (in radians), dimensions, and the origin position
|
||||||
|
function get_non_walkable_nodes_in_box(box)
|
||||||
|
local all_nodes = get_nodes_in_box(box)
|
||||||
|
local non_walkable_nodes = {}
|
||||||
|
|
||||||
|
for _, node_pos in ipairs(all_nodes) do
|
||||||
|
local node = minetest.get_node(node_pos)
|
||||||
|
local node_def = minetest.registered_nodes[node.name]
|
||||||
|
if node_def and not node_def.walkable or node_def.drawtype ~= "normal" then
|
||||||
|
table.insert(non_walkable_nodes, node_pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return non_walkable_nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_walkable_nodes_in_box(box)
|
||||||
|
local all_nodes = get_nodes_in_box(box)
|
||||||
|
local walkable_nodes = {}
|
||||||
|
|
||||||
|
for _, node_pos in ipairs(all_nodes) do
|
||||||
|
local node = minetest.get_node(node_pos)
|
||||||
|
local node_def = minetest.registered_nodes[node.name]
|
||||||
|
if node_def and node_def.walkable then
|
||||||
|
table.insert(walkable_nodes, node_pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return walkable_nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to create a virtual wall in front of the player and move nodes in front of the wall
|
||||||
|
function create_virtual_wall(player_pos_float, player_yaw, wall_dimensions, move_distance,
|
||||||
|
placement_height, placement_depth)
|
||||||
|
local player_pos = {
|
||||||
|
x = player_pos_float.x + 0.5,
|
||||||
|
y = player_pos_float.y + 0.5,
|
||||||
|
z = player_pos_float.z + 0.5,
|
||||||
|
}
|
||||||
|
|
||||||
|
local wall_pos = {
|
||||||
|
x = player_pos.x + math.cos(player_yaw) * 0,
|
||||||
|
y = player_pos.y,
|
||||||
|
z = player_pos.z + math.sin(player_yaw) * 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
local wall_box = {
|
||||||
|
yaw = player_yaw,
|
||||||
|
dimensions = wall_dimensions,
|
||||||
|
origin = {
|
||||||
|
x = wall_pos.x + math.cos(player_yaw),
|
||||||
|
y = wall_pos.y + 1.5 - 1,
|
||||||
|
z = wall_pos.z + math.sin(player_yaw),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local nodes_in_wall = get_nodes_in_box(wall_box)
|
||||||
|
|
||||||
|
-- Main placement box in front
|
||||||
|
|
||||||
|
local placement_box = {
|
||||||
|
yaw = player_yaw,
|
||||||
|
dimensions = {move_distance, wall_dimensions[2], placement_height - placement_depth},
|
||||||
|
origin = {
|
||||||
|
x = player_pos.x + math.cos(player_yaw) * 5,
|
||||||
|
y = player_pos.y + 1.5 + placement_depth,
|
||||||
|
z = player_pos.z + math.sin(player_yaw) * 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local non_walkable_nodes_in_placement_box = get_non_walkable_nodes_in_box(placement_box)
|
||||||
|
|
||||||
|
table.sort(non_walkable_nodes_in_placement_box, function(a, b) return a.y < b.y end)
|
||||||
|
|
||||||
|
-- Side placement boxes
|
||||||
|
|
||||||
|
local side_box_width = wall_dimensions[2]
|
||||||
|
|
||||||
|
local left_box = {
|
||||||
|
yaw = player_yaw,
|
||||||
|
dimensions = {move_distance, side_box_width, wall_dimensions[3] + 2},
|
||||||
|
origin = {
|
||||||
|
x = player_pos.x + math.cos(player_yaw+math.pi/2) * side_box_width,
|
||||||
|
y = player_pos.y + 0.5 * wall_dimensions[3] - 3,
|
||||||
|
z = player_pos.z + math.sin(player_yaw+math.pi/2) * side_box_width,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local nwn_in_left_box = get_non_walkable_nodes_in_box(left_box)
|
||||||
|
|
||||||
|
local right_box = {
|
||||||
|
yaw = player_yaw,
|
||||||
|
dimensions = {move_distance, side_box_width, wall_dimensions[3] + 2},
|
||||||
|
origin = {
|
||||||
|
x = player_pos.x + math.cos(player_yaw-math.pi/2) * side_box_width,
|
||||||
|
y = player_pos.y + 0.5 * wall_dimensions[3] - 3,
|
||||||
|
z = player_pos.z + math.sin(player_yaw-math.pi/2) * side_box_width,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local nwn_in_right_box = get_non_walkable_nodes_in_box(right_box)
|
||||||
|
|
||||||
|
local nwn_left_right_combined = {}
|
||||||
|
|
||||||
|
for i, node_pos in ipairs(nwn_in_left_box) do
|
||||||
|
table.insert(nwn_left_right_combined, node_pos)
|
||||||
|
end
|
||||||
|
for i, node_pos in ipairs(nwn_in_right_box) do
|
||||||
|
table.insert(nwn_left_right_combined, node_pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(nwn_left_right_combined, function(a, b) return a.y < b.y end)
|
||||||
|
|
||||||
|
for i, node_pos in ipairs(nwn_left_right_combined) do
|
||||||
|
table.insert(non_walkable_nodes_in_placement_box, node_pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
local num_sounds_played = 0
|
||||||
|
|
||||||
|
for i, node_pos in ipairs(nodes_in_wall) do
|
||||||
|
local node = minetest.get_node(node_pos)
|
||||||
|
if node.name ~= "air" then
|
||||||
|
if #non_walkable_nodes_in_placement_box > 0 then
|
||||||
|
minetest.set_node(non_walkable_nodes_in_placement_box[1], node)
|
||||||
|
minetest.remove_node(node_pos)
|
||||||
|
table.remove(non_walkable_nodes_in_placement_box, 1)
|
||||||
|
|
||||||
|
if num_sounds_played < 1 then
|
||||||
|
local node_def = minetest.registered_nodes[node.name]
|
||||||
|
if node_def then
|
||||||
|
local sounds = node_def.sounds
|
||||||
|
if sounds and sounds.dig then
|
||||||
|
num_sounds_played = num_sounds_played + 1
|
||||||
|
minetest.sound_play(sounds.dig, {pos = pos, gain = 0.5})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function target_value(current, target, rate)
|
||||||
|
if current < target - rate then
|
||||||
|
return current + rate
|
||||||
|
elseif current > target + rate then
|
||||||
|
return current - rate
|
||||||
|
else
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local BULLDOZER_SIZE = 3
|
||||||
|
local BULLDOZER_HEIGHT = 1
|
||||||
|
local CLEAR_HEIGHT = 10
|
||||||
|
local PLACEMENT_HEIGHT = 3
|
||||||
|
local PLACEMENT_DEPTH = -5
|
||||||
|
|
||||||
|
-- Register the bulldozer entity
|
||||||
|
minetest.register_entity("bulldozer:bulldozer", {
|
||||||
|
initial_properties = {
|
||||||
|
physical = true,
|
||||||
|
collisionbox = {-1.4, -0.5, -1.4, 1.4, 0.8, 1.4},
|
||||||
|
visual = "mesh",
|
||||||
|
mesh = "bulldozer_bulldozer.obj",
|
||||||
|
textures = {
|
||||||
|
"bulldozer_bulldozer_blade.png",
|
||||||
|
"bulldozer_bulldozer_track.png",
|
||||||
|
"bulldozer_bulldozer_track.png",
|
||||||
|
"bulldozer_bulldozer_body.png",
|
||||||
|
"bulldozer_bulldozer_body.png",
|
||||||
|
"bulldozer_bulldozer_body.png",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
driver = nil,
|
||||||
|
wanted_sound_pitch = 0.7,
|
||||||
|
played_sound_pitch = 0.0,
|
||||||
|
wanted_sound_gain = 0.2,
|
||||||
|
played_sound_gain = 0.0,
|
||||||
|
|
||||||
|
on_rightclick = function(self, clicker)
|
||||||
|
if not clicker or not clicker:is_player() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local player_name = clicker:get_player_name()
|
||||||
|
|
||||||
|
if self.driver and player_name == self.driver:get_player_name() then
|
||||||
|
-- Detach the player
|
||||||
|
self.driver:set_detach()
|
||||||
|
self.driver:set_eye_offset()
|
||||||
|
self.driver = nil
|
||||||
|
self.object:set_properties({
|
||||||
|
physical = true,
|
||||||
|
})
|
||||||
|
if self.sound_handle then minetest.sound_stop(self.sound_handle) end
|
||||||
|
elseif not self.driver then
|
||||||
|
-- Attach the player
|
||||||
|
self.driver = clicker
|
||||||
|
self.driver:set_attach(self.object, "", {x = 0, y = 0, z = 0}, {x = 0, y = -90, z = 0})
|
||||||
|
self.driver:set_eye_offset({x = 0, y = 2, z = 0})
|
||||||
|
self.object:set_properties({
|
||||||
|
physical = false, -- We want to go through nodes
|
||||||
|
})
|
||||||
|
self.wanted_sound_pitch = 0.7
|
||||||
|
self.wanted_sound_gain = 0.2
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_step = function(self, dtime)
|
||||||
|
if not self.driver then
|
||||||
|
self:update_sound()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get player control inputs
|
||||||
|
local ctrl = self.driver:get_player_control()
|
||||||
|
--local yaw = self.driver:get_look_horizontal()
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
|
||||||
|
local object_pos = self.object:get_pos()
|
||||||
|
|
||||||
|
local y_off = -0.5
|
||||||
|
|
||||||
|
local box = {
|
||||||
|
yaw = yaw,
|
||||||
|
dimensions = {(BULLDOZER_SIZE+1), (BULLDOZER_SIZE+1), 1},
|
||||||
|
origin = {
|
||||||
|
x = object_pos.x,
|
||||||
|
y = object_pos.y - y_off + 0.9,
|
||||||
|
z = object_pos.z,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local nwn_tracks = get_walkable_nodes_in_box(box)
|
||||||
|
|
||||||
|
local box = {
|
||||||
|
yaw = yaw,
|
||||||
|
dimensions = {(BULLDOZER_SIZE+1), (BULLDOZER_SIZE+1), 1},
|
||||||
|
origin = {
|
||||||
|
x = object_pos.x,
|
||||||
|
y = object_pos.y - y_off - 0.5,
|
||||||
|
z = object_pos.z,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local nwn_support = get_walkable_nodes_in_box(box)
|
||||||
|
|
||||||
|
local box = {
|
||||||
|
yaw = yaw,
|
||||||
|
dimensions = {(BULLDOZER_SIZE+1), (BULLDOZER_SIZE+1), 1},
|
||||||
|
origin = {
|
||||||
|
x = object_pos.x,
|
||||||
|
y = object_pos.y - y_off - 0.15,
|
||||||
|
z = object_pos.z,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local nwn_close_support = get_walkable_nodes_in_box(box)
|
||||||
|
|
||||||
|
local box = {
|
||||||
|
yaw = yaw,
|
||||||
|
dimensions = {(BULLDOZER_SIZE+1), (BULLDOZER_SIZE+1), 1},
|
||||||
|
origin = {
|
||||||
|
x = object_pos.x,
|
||||||
|
y = object_pos.y - y_off - 0.05,
|
||||||
|
z = object_pos.z,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local nwn_very_close_support = get_walkable_nodes_in_box(box)
|
||||||
|
|
||||||
|
if ctrl.up then
|
||||||
|
local speed = 2.0
|
||||||
|
|
||||||
|
-- Move the bulldozer forward
|
||||||
|
self.object:set_velocity(vector.new(
|
||||||
|
math.cos(yaw+math.pi) * speed,
|
||||||
|
0,
|
||||||
|
math.sin(yaw+math.pi) * speed
|
||||||
|
))
|
||||||
|
|
||||||
|
local object_pos2 = self.object:get_pos()
|
||||||
|
if ctrl.jump then
|
||||||
|
object_pos2.y = object_pos2.y - y_off + 1.1
|
||||||
|
elseif ctrl.sneak then
|
||||||
|
object_pos2.y = object_pos2.y - y_off - 0.9
|
||||||
|
else
|
||||||
|
object_pos2.y = object_pos2.y - y_off - 0.5
|
||||||
|
end
|
||||||
|
local object_yaw = self.object:get_yaw()+math.pi
|
||||||
|
|
||||||
|
local wall_dimensions = {2, (BULLDOZER_SIZE+1), CLEAR_HEIGHT}
|
||||||
|
local move_distance = 5
|
||||||
|
create_virtual_wall(object_pos2, object_yaw, wall_dimensions, move_distance, PLACEMENT_HEIGHT, PLACEMENT_DEPTH)
|
||||||
|
elseif ctrl.down then
|
||||||
|
local speed = 1.5
|
||||||
|
-- Move the bulldozer backward
|
||||||
|
self.object:set_velocity(vector.new(
|
||||||
|
math.cos(yaw) * speed,
|
||||||
|
0,
|
||||||
|
math.sin(yaw) * speed
|
||||||
|
))
|
||||||
|
else
|
||||||
|
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctrl.left then
|
||||||
|
self.object:set_yaw(self.object:get_yaw() + 0.020)
|
||||||
|
elseif ctrl.right then
|
||||||
|
self.object:set_yaw(self.object:get_yaw() - 0.020)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ctrl.jump and ctrl.up and (#nwn_tracks >= 1 or #nwn_very_close_support >= (BULLDOZER_SIZE*BULLDOZER_SIZE/2))) or
|
||||||
|
(ctrl.down and #nwn_tracks >= 1 and not ctrl.sneak) then
|
||||||
|
local rate = 0.01
|
||||||
|
if #nwn_very_close_support >= 9 then
|
||||||
|
rate = 0.03
|
||||||
|
elseif #nwn_very_close_support >= 5 then
|
||||||
|
rate = 0.02
|
||||||
|
end
|
||||||
|
self.object:set_pos({
|
||||||
|
x = self.object:get_pos().x,
|
||||||
|
y = self.object:get_pos().y + rate,
|
||||||
|
z = self.object:get_pos().z
|
||||||
|
})
|
||||||
|
elseif (ctrl.sneak and ctrl.up) then
|
||||||
|
local rate = 0.01
|
||||||
|
if #nwn_support <= 7 then
|
||||||
|
rate = 0.02
|
||||||
|
end
|
||||||
|
self.object:set_pos({
|
||||||
|
x = self.object:get_pos().x,
|
||||||
|
y = self.object:get_pos().y - rate,
|
||||||
|
z = self.object:get_pos().z
|
||||||
|
})
|
||||||
|
else
|
||||||
|
if #nwn_close_support >= (BULLDOZER_SIZE*BULLDOZER_SIZE*0.8) and not ctrl.sneak then
|
||||||
|
local off = -y_off
|
||||||
|
local new_y = math.floor(self.object:get_pos().y + off + 0.5) - off
|
||||||
|
if math.abs(new_y - self.object:get_pos().y) >= 0.2 then
|
||||||
|
self.object:set_pos({
|
||||||
|
x = self.object:get_pos().x,
|
||||||
|
y = new_y,
|
||||||
|
z = self.object:get_pos().z
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ctrl.sneak then
|
||||||
|
if #nwn_support <= (BULLDOZER_SIZE*BULLDOZER_SIZE/3) then
|
||||||
|
self.object:set_pos({
|
||||||
|
x = self.object:get_pos().x,
|
||||||
|
--y = math.floor(self.object:get_pos().y + 0.5 - 1.0),
|
||||||
|
y = self.object:get_pos().y - 0.1,
|
||||||
|
z = self.object:get_pos().z
|
||||||
|
})
|
||||||
|
elseif #nwn_close_support <= (BULLDOZER_SIZE*BULLDOZER_SIZE/3) then
|
||||||
|
self.object:set_pos({
|
||||||
|
x = self.object:get_pos().x,
|
||||||
|
--y = math.floor(self.object:get_pos().y + 0.5 - 1.0),
|
||||||
|
y = self.object:get_pos().y - 0.02,
|
||||||
|
z = self.object:get_pos().z
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctrl.up then
|
||||||
|
self.wanted_sound_pitch = target_value(self.wanted_sound_pitch, 1.3, 0.02)
|
||||||
|
self.wanted_sound_gain = target_value(self.wanted_sound_gain, 0.30, 0.02)
|
||||||
|
elseif ctrl.down or ctrl.left or ctrl.right then
|
||||||
|
self.wanted_sound_pitch = target_value(self.wanted_sound_pitch, 1.1, 0.02)
|
||||||
|
self.wanted_sound_gain = target_value(self.wanted_sound_gain, 0.25, 0.02)
|
||||||
|
else
|
||||||
|
self.wanted_sound_pitch = target_value(self.wanted_sound_pitch, 0.9, 0.02)
|
||||||
|
self.wanted_sound_gain = target_value(self.wanted_sound_gain, 0.20, 0.02)
|
||||||
|
end
|
||||||
|
self:update_sound()
|
||||||
|
end,
|
||||||
|
|
||||||
|
update_sound = function(self)
|
||||||
|
if not self.driver then
|
||||||
|
if self.sound_handle then minetest.sound_stop(self.sound_handle) end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if math.abs(self.wanted_sound_pitch - self.played_sound_pitch) < 0.06 and
|
||||||
|
math.abs(self.wanted_sound_gain - self.played_sound_gain) < 0.03 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if self.sound_handle then minetest.sound_stop(self.sound_handle) end
|
||||||
|
if self.object then
|
||||||
|
self.played_sound_pitch = self.wanted_sound_pitch
|
||||||
|
self.played_sound_gain = self.wanted_sound_gain
|
||||||
|
self.sound_handle = minetest.sound_play({name = "bulldozer_engine"}, {
|
||||||
|
object = self.object, gain = self.wanted_sound_gain,
|
||||||
|
pitch = self.wanted_sound_pitch,
|
||||||
|
max_hear_distance = 45,
|
||||||
|
loop = true,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Register the bulldozer item for spawning the entity
|
||||||
|
minetest.register_craftitem("bulldozer:bulldozer_item", {
|
||||||
|
description = "Bulldozer",
|
||||||
|
inventory_image = "bulldozer_bulldozer_item.png",
|
||||||
|
on_place = function(itemstack, placer, pointed_thing)
|
||||||
|
if pointed_thing.type ~= "node" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local ent = minetest.add_entity(pointed_thing.above, "bulldozer:bulldozer")
|
||||||
|
ent:set_yaw(placer:get_look_horizontal())
|
||||||
|
|
||||||
|
itemstack:take_item()
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
141
media/bulldozer_bulldozer.obj
Normal file
141
media/bulldozer_bulldozer.obj
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# Axis-aligned boxes for Minetest
|
||||||
|
|
||||||
|
mtllib boxes.mtl
|
||||||
|
|
||||||
|
# Blade
|
||||||
|
o Box1
|
||||||
|
g Group1
|
||||||
|
usemtl Texture1
|
||||||
|
v 18.5 0 -15.0
|
||||||
|
v 20.5 0 -15.0
|
||||||
|
v 20.5 0 15.0
|
||||||
|
v 18.5 0 15.0
|
||||||
|
v 18.5 8 -15.0
|
||||||
|
v 20.5 8 -15.0
|
||||||
|
v 20.5 8 15.0
|
||||||
|
v 18.5 8 15.0
|
||||||
|
vt 0 0
|
||||||
|
vt 1 0
|
||||||
|
vt 1 1
|
||||||
|
vt 0 1
|
||||||
|
f 1/1 5/4 6/3 2/2
|
||||||
|
f 2/1 6/4 7/3 3/2
|
||||||
|
f 3/1 7/4 8/3 4/2
|
||||||
|
f 4/1 8/4 5/3 1/2
|
||||||
|
f 1/1 2/2 3/3 4/4
|
||||||
|
f 5/1 8/2 7/3 6/4
|
||||||
|
|
||||||
|
# Left track
|
||||||
|
o Box2
|
||||||
|
g Group2
|
||||||
|
usemtl Texture2
|
||||||
|
v -10.0 0 -14
|
||||||
|
v 17.0 0 -14
|
||||||
|
v 17.0 0 -8
|
||||||
|
v -10.0 0 -8
|
||||||
|
v -10.0 6 -14
|
||||||
|
v 17.0 6 -14
|
||||||
|
v 17.0 6 -8
|
||||||
|
v -10.0 6 -8
|
||||||
|
vt 0 0
|
||||||
|
vt 1 0
|
||||||
|
vt 1 1
|
||||||
|
vt 0 1
|
||||||
|
f 9/1 13/4 14/3 10/2
|
||||||
|
f 10/1 14/4 15/3 11/2
|
||||||
|
f 11/1 15/4 16/3 12/2
|
||||||
|
f 12/1 16/4 13/3 9/2
|
||||||
|
f 9/1 10/2 11/3 12/4
|
||||||
|
f 13/1 16/2 15/3 14/4
|
||||||
|
|
||||||
|
# Right track
|
||||||
|
o Box3
|
||||||
|
g Group3
|
||||||
|
usemtl Texture3
|
||||||
|
v -10.0 0 8
|
||||||
|
v 17.0 0 8
|
||||||
|
v 17.0 0 14
|
||||||
|
v -10.0 0 14
|
||||||
|
v -10.0 6 8
|
||||||
|
v 17.0 6 8
|
||||||
|
v 17.0 6 14
|
||||||
|
v -10.0 6 14
|
||||||
|
vt 0 0
|
||||||
|
vt 1 0
|
||||||
|
vt 1 1
|
||||||
|
vt 0 1
|
||||||
|
f 17/1 21/4 22/3 18/2
|
||||||
|
f 18/1 22/4 23/3 19/2
|
||||||
|
f 19/1 23/4 24/3 20/2
|
||||||
|
f 20/1 24/4 21/3 17/2
|
||||||
|
f 17/1 18/2 19/3 20/4
|
||||||
|
f 21/1 24/2 23/3 22/4
|
||||||
|
|
||||||
|
# Body
|
||||||
|
o Box4
|
||||||
|
g Group4
|
||||||
|
usemtl Texture4
|
||||||
|
v -10.0 1 -7.0
|
||||||
|
v 17.0 1 -7.0
|
||||||
|
v 17.0 1 7.0
|
||||||
|
v -10.0 1 7.0
|
||||||
|
v -10.0 10 -7.0
|
||||||
|
v 17.0 10 -7.0
|
||||||
|
v 17.0 10 7.0
|
||||||
|
v -10.0 10 7.0
|
||||||
|
vt 0 0
|
||||||
|
vt 1 0
|
||||||
|
vt 1 1
|
||||||
|
vt 0 1
|
||||||
|
f 25/1 29/4 30/3 26/2
|
||||||
|
f 26/1 30/4 31/3 27/2
|
||||||
|
f 27/1 31/4 32/3 28/2
|
||||||
|
f 28/1 32/4 29/3 25/2
|
||||||
|
f 25/1 26/2 27/3 28/4
|
||||||
|
f 29/1 32/2 31/3 30/4
|
||||||
|
|
||||||
|
# Left track cover
|
||||||
|
o Box5
|
||||||
|
g Group5
|
||||||
|
usemtl Texture5
|
||||||
|
v -9.0 1 -15
|
||||||
|
v 16.0 1 -15
|
||||||
|
v 16.0 1 -7
|
||||||
|
v -9.0 1 -7
|
||||||
|
v -9.0 5 -15
|
||||||
|
v 16.0 5 -15
|
||||||
|
v 16.0 5 -7
|
||||||
|
v -9.0 5 -7
|
||||||
|
vt 0 0
|
||||||
|
vt 1 0
|
||||||
|
vt 1 1
|
||||||
|
vt 0 1
|
||||||
|
f 33/1 37/4 38/3 34/2
|
||||||
|
f 34/1 38/4 39/3 35/2
|
||||||
|
f 35/1 39/4 40/3 36/2
|
||||||
|
f 36/1 40/4 37/3 33/2
|
||||||
|
f 33/1 34/2 35/3 36/4
|
||||||
|
f 37/1 40/2 39/3 38/4
|
||||||
|
|
||||||
|
# Right track cover
|
||||||
|
o Box6
|
||||||
|
g Group6
|
||||||
|
usemtl Texture6
|
||||||
|
v -9.0 1 7
|
||||||
|
v 16.0 1 7
|
||||||
|
v 16.0 1 15
|
||||||
|
v -9.0 1 15
|
||||||
|
v -9.0 5 7
|
||||||
|
v 16.0 5 7
|
||||||
|
v 16.0 5 15
|
||||||
|
v -9.0 5 15
|
||||||
|
vt 0 0
|
||||||
|
vt 1 0
|
||||||
|
vt 1 1
|
||||||
|
vt 0 1
|
||||||
|
f 41/1 45/4 46/3 42/2
|
||||||
|
f 42/1 46/4 47/3 43/2
|
||||||
|
f 43/1 47/4 48/3 44/2
|
||||||
|
f 44/1 48/4 45/3 41/2
|
||||||
|
f 41/1 42/2 43/3 44/4
|
||||||
|
f 45/1 48/2 47/3 46/4
|
BIN
media/bulldozer_bulldozer_blade.png
Normal file
BIN
media/bulldozer_bulldozer_blade.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.0 KiB |
BIN
media/bulldozer_bulldozer_body.png
Normal file
BIN
media/bulldozer_bulldozer_body.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.0 KiB |
BIN
media/bulldozer_bulldozer_track.png
Normal file
BIN
media/bulldozer_bulldozer_track.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
BIN
media/bulldozer_engine.ogg
Normal file
BIN
media/bulldozer_engine.ogg
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user