Created
July 11, 2013 03:34
-
-
Save CapsAdmin/5972340 to your computer and use it in GitHub Desktop.
some sort of 3d world
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
screens = screens or {} | |
screens.active_screens = screens.active_screens or {} | |
screens.lock_player = false | |
screens.mouse_delta = Vector(0, 0, 0) | |
screens.focused_screen = NULL | |
do -- meta | |
local SCREEN = {} | |
SCREEN.__index = SCREEN | |
function SCREEN:Remove() | |
self:KillFocus() | |
for _, ent in pairs(self.entities) do | |
SafeRemoveEntity(ent) | |
end | |
function self:IsValid() | |
return false | |
end | |
screens.active_screens[self.screen_id] = nil | |
setmetatable(self, getmetatable(NULL)) | |
end | |
function SCREEN:IsValid() | |
return true | |
end | |
AccessorFunc(SCREEN, "terrain_mat", "TerrainMaterial") | |
AccessorFunc(SCREEN, "rt_tex", "RTTexture") | |
AccessorFunc(SCREEN, "fps", "FPS", FORCE_NUMBER) | |
AccessorFunc(SCREEN, "width", "Width") | |
AccessorFunc(SCREEN, "height", "Height") | |
AccessorFunc(SCREEN, "cam_pos", "CameraPos") | |
AccessorFunc(SCREEN, "cam_ang", "CameraAngles") | |
AccessorFunc(SCREEN, "cam_fov", "CameraFOV") | |
function SCREEN:IsPaused() | |
return self.paused | |
end | |
function SCREEN:Pause() | |
self.paused = true | |
end | |
function SCREEN:Resume() | |
self.paused = false | |
end | |
do -- input | |
function SCREEN:RequestFocus() | |
screens.EnableKeyMonitor(true) | |
screens.focused_screen = self | |
end | |
function SCREEN:KillFocus() | |
if screens.focused_screen == self then | |
screens.EnableKeyMonitor(false) | |
screens.focused_screen = NULL | |
end | |
end | |
function SCREEN:IsFocused() | |
return screens.focused_screen == self | |
end | |
function SCREEN:GetMouseDelta() | |
return screens.mouse_delta | |
end | |
function SCREEN:OnInput(key, press) | |
end | |
end | |
do -- entities | |
function SCREEN:CreateEntity(mdl) | |
local ent = ClientsideModel(mdl) | |
ent:SetNoDraw(true) | |
table.insert(self.entities, ent) | |
return ent | |
end | |
function SCREEN:DrawEntities() | |
render.SuppressEngineLighting(true) | |
render.SetBlend(1) | |
render.SetColorModulation(1,1,1) | |
render.SetAmbientLight(0,0,0) | |
render.ResetModelLighting(0.2,0.2,0.2) | |
render.SetModelLighting(3, 1,1,1) -- global_light.r / 255, global_light.g / 255, global_light.b / 255) | |
for _, ent in pairs(self.entities) do | |
ent:DrawModel() | |
end | |
render.SuppressEngineLighting(false) | |
end | |
end | |
if pac then | |
function SCREEN:GetTextureFromURL(url, size, callback) | |
pac.urltex.GetMaterialFromURL(url, function(mat, tex) | |
callback(mat, tex) | |
end, false, nil, size, false) | |
end | |
end | |
do -- terrain | |
local W, H = ScrW(), ScrH() | |
local function start_height(tex) | |
local mat = CreateMaterial("screens_heightmap_" .. os.clock(), "UnlitGeneric", {["$basetexture"] = "dev/hemisphere_height.vtf"}) | |
if tex then | |
mat:SetTexture("$basetexture", tex) | |
end | |
cam.Start2D() | |
surface.SetMaterial(mat) | |
surface.SetDrawColor(255, 255, 255, 255) | |
-- this kinda sucks but using a heightmap resolution higher than screen resolution will cause undesired results.. | |
surface.DrawTexturedRect(0, 0, W, H) | |
render.CapturePixels() | |
end | |
local function get_height(x, y) | |
local r,g,b = render.ReadPixel(math.ceil(x*W), math.ceil(y*H)) | |
return ((r+g+b) / 3) / 255 | |
end | |
local function end_height() | |
cam.End2D() | |
end | |
function SCREEN:CreateTerrainFromHeightmap(tex, size, res, height) | |
local mesh = Mesh() | |
start_height(tex) | |
size = size or 32768 | |
res = res or 64 | |
height = height or 8192 | |
local data = {} | |
local _size = size / res | |
for y = 0, res do | |
for x = 0, res do | |
local z1 = get_height(x/res, (y+1)/res) * height -- bottom left | |
local z2 = get_height(x/res, y/res) * height -- top left | |
local z3 = get_height((x+1)/res, y/res) * height -- top right | |
local z4 = get_height((x+1)/res, (y+1)/res) * height -- bottom right | |
table.insert(data, {pos = Vector(x * _size + _size, y * _size, z3)}) | |
table.insert(data, {pos = Vector(x * _size, y * _size, z2)}) | |
table.insert(data, {pos = Vector(x * _size, y * _size + _size, z1)}) | |
table.insert(data, {pos = Vector(x * _size, y * _size + _size, z1)}) | |
table.insert(data, {pos = Vector(x * _size + _size, y * _size + _size, z4)}) | |
table.insert(data, {pos = Vector(x * _size + _size, y * _size, z3)}) | |
end | |
end | |
end_height() | |
local height_cache = {} | |
for _, vertex in pairs(data) do | |
-- vertex.normal = VectorRand() | |
vertex.v = vertex.pos.x / size | |
vertex.u = vertex.pos.y / size | |
end | |
start_height(tex) | |
for x = 0, W do | |
for y = 0, H do | |
local r,g,b = render.ReadPixel(x, y) | |
local z = ((r+g+b) / 3) / 255 | |
height_cache[x] = height_cache[x] or {} | |
height_cache[x][y] = height_cache[x][y] or z * height | |
end | |
end | |
end_height() | |
self.height_cache = height_cache | |
mesh:BuildFromTriangles(data) | |
SafeRemoveEntity(self.terrain_ent) | |
local ent = self:CreateEntity("error.mdl") | |
self.terrain_mat = Material("models/wireframe") | |
self.terrain_size = size | |
ent.RenderOverride = function(ent) | |
render.MaterialOverride(self.terrain_mat) | |
ent:SetModelScale(0, 0) | |
ent:DrawModel() | |
local mat = Matrix() | |
mat:SetAngles(ent:GetAngles()) | |
mat:SetTranslation(ent:GetPos()) | |
cam.PushModelMatrix(mat) | |
mesh:Draw() | |
cam.PopModelMatrix() | |
render.MaterialOverride() | |
end | |
end | |
function SCREEN:GetTerrainHeight(pos) | |
if self.height_cache then | |
local x = math.floor((pos.x / self.terrain_size) * W) | |
local y = math.floor((pos.y / self.terrain_size) * H) | |
return self.height_cache[x] and self.height_cache[x][y] or 0 | |
end | |
return 0 | |
end | |
end | |
function SCREEN:SetFog(start, _end, max_density, r,g,b) | |
if start and _end and max_density and r and g and b then | |
self.fog_params = | |
{ | |
start = start, | |
_end = _end, | |
max_density = max_density, | |
color = {r,g,b}, | |
} | |
else | |
self.fog_params = nil | |
end | |
end | |
function SCREEN:CreateWorldScreen() | |
SafeRemoveEntity(self.world_screen) | |
local ent = ClientsideModel("models/hunter/plates/plate1x1.mdl") | |
local mat = Matrix() | |
mat:Scale(Vector(self.height / self.width, 1, 1)) | |
ent:EnableMatrix("RenderMultiply", mat) | |
ent.RenderOverride = function(s) | |
if not self:IsValid() then | |
timer.Simple(0, function() | |
s:Remove() | |
end) | |
end | |
render.SetBlend(1) | |
render.SetColorModulation(1, 1, 1) | |
render.PushFilterMag(TEXFILTER.POINT) | |
render.PushFilterMin(TEXFILTER.POINT) | |
render.MaterialOverride(self.screen_mat) | |
s:DrawModel() | |
render.MaterialOverride() | |
render.PopFilterMag() | |
render.PopFilterMin() | |
end | |
self.world_screen = ent | |
return ent | |
end | |
-- events | |
function SCREEN:UpdateFreeroamCamera() | |
local speed = 100 | |
local delta = screens.mouse_delta * 0.1 | |
local cam_pos = self.cam_pos | |
local cam_ang = self.cam_ang | |
cam_ang.p = cam_ang.p + delta.y | |
cam_ang.y = cam_ang.y - delta.x | |
cam_ang.p = math.clamp(cam_ang.p, -90, 90) | |
if input.IsKeyDown(KEY_LSHIFT) then | |
speed = speed * 4 | |
elseif input.IsKeyDown(KEY_LCONTROL) then | |
speed = speed / 4 | |
end | |
if input.IsKeyDown(KEY_SPACE) then | |
cam_pos = cam_pos - Vector(0, 0, -speed) | |
end | |
local offset = cam_ang:Forward() * speed | |
if input.IsKeyDown(KEY_W) then | |
cam_pos = cam_pos + offset | |
elseif input.IsKeyDown(KEY_S) then | |
cam_pos = cam_pos - offset | |
end | |
offset = cam_ang:Right() * speed | |
offset.z = -offset.z | |
if input.IsKeyDown(KEY_D) then | |
cam_pos = cam_pos + offset | |
elseif input.IsKeyDown(KEY_A) then | |
cam_pos = cam_pos - offset | |
end | |
self.cam_pos = cam_pos | |
self.cam_ang = cam_ang | |
end | |
function SCREEN:UpdateWalkCamera() | |
self.cam_vel = self.cam_vel or Vector(0,0,0) | |
local speed = 10 | |
local delta = screens.mouse_delta * 0.1 | |
local cam_pos = self.cam_pos | |
local cam_ang = self.cam_ang | |
cam_ang.p = cam_ang.p + delta.y | |
cam_ang.y = cam_ang.y - delta.x | |
cam_ang.p = math.clamp(cam_ang.p, -90, 90) | |
if input.IsKeyDown(KEY_LSHIFT) then | |
speed = speed * 4 | |
elseif input.IsKeyDown(KEY_LCONTROL) then | |
speed = speed / 4 | |
end | |
if input.IsKeyDown(KEY_SPACE) then | |
if not self.jumped then | |
self.cam_vel.z = self.cam_vel.z + 500 | |
self.jumped = true | |
end | |
else | |
self.jumped = false | |
end | |
local offset = cam_ang:Forward() * speed | |
if input.IsKeyDown(KEY_W) then | |
cam_pos = cam_pos + offset | |
elseif input.IsKeyDown(KEY_S) then | |
cam_pos = cam_pos - offset | |
end | |
offset = cam_ang:Right() * speed | |
offset.z = -offset.z | |
if input.IsKeyDown(KEY_D) then | |
cam_pos = cam_pos + offset | |
elseif input.IsKeyDown(KEY_A) then | |
cam_pos = cam_pos - offset | |
end | |
cam_pos.z = math.max(cam_pos.z - 100, self:GetTerrainHeight(cam_pos) + 72) | |
self.cam_vel = (self.cam_vel + (cam_pos - self.cam_pos)) * 0.35 | |
self.cam_pos = cam_pos + self.cam_vel | |
self.cam_ang = cam_ang | |
end | |
function SCREEN:UpdateCamera() | |
if not self:IsFocused() then return end | |
self:UpdateWalkCamera() | |
end | |
function SCREEN:Think() | |
self:UpdateCamera() | |
end | |
local skybox_ent = ClientsideModel("models/XQM/Rails/gumball_1.mdl") | |
skybox_ent:SetMaterial("models/debug/debugwhite") | |
function SCREEN:DrawSkyBox() | |
render.SetColorModulation(0.4,0.7,1) | |
render.SetBlend(1) | |
render.SuppressEngineLighting(true) | |
skybox_ent:SetPos(self.cam_pos) | |
skybox_ent:SetModelScale(-1, 0) | |
skybox_ent:DrawModel() | |
render.SuppressEngineLighting(false) | |
render.ClearDepth() | |
end | |
local water_mat = CreateMaterial("screens_water_"..RealTime(), "Refract", | |
{ | |
["$model"] = 1, | |
["$nocull"] = 1, | |
["$translucent"] = 1, | |
["$bluramount"] = 50, | |
["$refractamount"] = 0.1, | |
["$refracttint"] = "0.5 0.5 1", | |
["$dudvmap"] = "water/dx80_tfwater001_dudv", | |
["$normalmap"] = "water/tfwater001_normal", | |
Proxies = | |
{ | |
AnimatedTexture = | |
{ | |
animatedtexturevar = "$normalmap", | |
animatedtextureframenumvar = "$bumpframe", | |
animatedtextureframerate = 30, | |
}, | |
}, | |
}) | |
local water_mesh = Mesh() | |
local data = {} | |
local size = 32768 / 128*4 | |
for y = 0, 128 do | |
for x = 0, 128 do | |
table.insert(data, {pos = Vector(x * size + size, y * size)}) | |
table.insert(data, {pos = Vector(x * size, y * size)}) | |
table.insert(data, {pos = Vector(x * size, y * size + size)}) | |
table.insert(data, {pos = Vector(x * size, y * size + size)}) | |
table.insert(data, {pos = Vector(x * size + size, y * size + size)}) | |
table.insert(data, {pos = Vector(x * size + size, y * size)}) | |
end | |
end | |
local up = Vector(0,0,1) | |
for k,v in pairs(data) do | |
v.normal = up | |
v.v = v.pos.x / size | |
v.u = v.pos.y / size | |
end | |
water_mesh:BuildFromTriangles(data) | |
function SCREEN:DrawWater() | |
render.UpdateRefractTexture() | |
render.SetBlend(1) | |
render.SuppressEngineLighting(true) | |
local mat = Matrix() | |
mat:Translate(Vector(-32768*0.5,-32768*0.5,1000)) | |
cam.PushModelMatrix(mat) | |
render.SetMaterial(water_mat) | |
water_mesh:Draw() | |
cam.PopModelMatrix() | |
render.SuppressEngineLighting(false) | |
end | |
function SCREEN:Draw3D() | |
self:DrawEntities() | |
end | |
function SCREEN:Draw2D() | |
end | |
function SCREEN:DrawPostProcess() | |
if self.cam_pos.z < 1000 then | |
surface.SetDrawColor(20,50,100,100) | |
surface.DrawRect(0,0,self.width,self.height) | |
end | |
end | |
function SCREEN:Update() | |
self:Think() | |
local old_rt, old_w, old_h = render.GetRenderTarget(), ScrW(), ScrH() | |
render.SetRenderTarget(self.rt_tex) | |
render.SetViewPort(0, 0, self.width, self.height) | |
render.Clear(0,0,0,0, true) | |
cam.Start3D(self.cam_pos, self.cam_ang, self.cam_fov, 0, 0, self.width, self.height) | |
self:DrawSkyBox() | |
local params = self.fog_params | |
if params then | |
render.FogMode(1) | |
render.FogStart(params.start) | |
render.FogEnd(params._end) | |
render.FogMaxDensity(params.max_density) | |
render.FogColor(unpack(params.color)) | |
end | |
self:Draw3D() | |
self:DrawWater() | |
cam.End3D() | |
cam.Start2D() | |
self:Draw2D() | |
self:DrawPostProcess() | |
cam.End2D() | |
render.SetRenderTarget(old_rt) | |
render.SetViewPort(0, 0, old_w, old_h) | |
end | |
screens.ScreenMeta = SCREEN | |
end | |
function screens.GetAll() | |
return screens.active_screens | |
end | |
function screens.Remove(id) | |
local self = screens.active_screens[id] or NULL | |
if self:IsValid() then | |
self:Remove() | |
end | |
end | |
function screens.Create(id, w, h, fps) | |
w = w or 320 | |
h = h or 240 | |
fps = fps or 10 | |
screens.Remove(id) | |
local self = setmetatable({}, screens.ScreenMeta) | |
self.screen_id = id | |
self.width = w | |
self.height = h | |
self.fps = fps | |
self.cam_pos = Vector(0, 0, 0) | |
self.cam_ang = Angle(0, 0, 0) | |
self.cam_fov = 90 | |
self.rt_tex = GetRenderTarget(id, w, h, true) | |
self.screen_mat = CreateMaterial(id, "UnlitGeneric") | |
self.screen_mat:SetTexture("$basetexture", self.rt_tex) | |
self.entities = {} | |
screens.active_screens[id] = self | |
return self | |
end | |
function screens.EnableKeyMonitor(b) | |
if b then | |
screens.lock_player = LocalPlayer():EyeAngles() | |
else | |
screens.lock_player = false | |
end | |
end | |
do -- hooks | |
hook.Add("Think", "screens_update", function() | |
local time = RealTime() | |
for _, screen in pairs(screens.active_screens) do | |
if not screen.paused and (not screen.last_update or screen.last_update < time) then | |
screen:Update() | |
screen.last_update = time + (1/screen.fps) | |
end | |
end | |
end) | |
hook.Add("CreateMove", "screens_camera", function(ucmd) | |
if screens.lock_player then | |
ucmd:SetForwardMove(0) | |
ucmd:SetSideMove(0) | |
ucmd:SetViewAngles(screens.lock_player) | |
ucmd:SetButtons(0) | |
screens.mouse_delta.x = ucmd:GetMouseX() | |
screens.mouse_delta.y = ucmd:GetMouseY() | |
end | |
end) | |
hook.Add("KeyPress", "screens", function(ply, key) | |
local screen = screens.focused_screen | |
if screen:IsValid() then | |
screen:OnInput(key, true) | |
end | |
end) | |
hook.Add("KeyRelease", "screens", function(ply, key) | |
local screen = screens.focused_screen | |
if screen:IsValid() then | |
screen:OnInput(key, false) | |
end | |
end) | |
end | |
do -- test | |
local screen = screens.Create("forest") | |
local ent = screen:CreateWorldScreen() | |
ent:SetPos(LocalPlayer():EyePos() + LocalPlayer():GetAimVector() * 30) | |
ent:SetAngles(LocalPlayer():EyeAngles() + Angle(90,0,0)) | |
screen:SetFog(0, 18000, 0.15, 80, 170, 255) | |
screen:RequestFocus() | |
local trees = { | |
"models/props_foliage/tree_pine01.mdl", | |
"models/props_foliage/tree_pine01_8cluster.mdl", | |
"models/props_foliage/tree_pine01_4cluster.mdl", | |
"models/props_foliage/tree_pine_huge.mdl", | |
"models/props_foliage/tree_pine_small.mdl", | |
} | |
screen:GetTextureFromURL("http://dl.dropboxusercontent.com/u/244444/heightmap1.jpg", 2048, function(mat, tex) | |
screen:CreateTerrainFromHeightmap(tex) | |
for i = 1, 1000 do | |
local pos = Vector(math.random(), math.random(), 0) * 32768 | |
pos.z = screen:GetTerrainHeight(pos) | |
if pos.z > 1000 and pos.z < 3000 then | |
local ent = screen:CreateEntity(table.Random(trees)) | |
ent:SetModelScale(math.Rand(0.5, 2), 0) | |
ent:SetPos(pos) | |
end | |
end | |
end) | |
screen:GetTextureFromURL("http://dl.dropboxusercontent.com/u/244444/HeightMap_1BaseTexture.jpg", 2048, function(mat) | |
screen:SetTerrainMaterial(mat) | |
end) | |
function screen:Draw2D() | |
local t = RealTime() | |
local w, h = screen:GetWidth(), screen:GetHeight() | |
surface.SetFont("DermaDefault") | |
surface.SetTextColor(255, 255, 255, 255) | |
local pos = self:GetCameraPos() | |
surface.SetTextPos(5, 5) | |
surface.DrawText(tostring(pos)) | |
local height = self:GetTerrainHeight(pos) | |
surface.SetTextPos(5, 20) | |
surface.DrawText(height) | |
end | |
LOL_SCREEN = screen | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment