Merge branch 'master' of ssh://git.openclonk.org/openclonk

objectmenu
Matthias Rottländer 2016-01-20 00:25:00 +01:00
commit 3810937867
96 changed files with 1706 additions and 974 deletions

View File

@ -11,7 +11,7 @@
# To redistribute this file separately, substitute the full license texts
# for the above references.
cmake_minimum_required (VERSION 2.8.10)
cmake_minimum_required (VERSION 3.0.2)
project (openclonk CXX C)
# CMP0054: Only interpret if() arguments as variables or keywords when unquoted
@ -55,9 +55,6 @@ separate_arguments(OC_EXE_LINKER_FLAGS_DEBUG)
CHECK_CXX_COMPILER_FLAG("-std=gnu++14" USE_GCC_STD_14)
if(USE_GCC_STD_14)
if(OC_CXX_FLAGS MATCHES \\-std=gnu\\+\\+0x)
list(REMOVE_ITEM OC_CXX_FLAGS "-std=gnu++0x")
endif()
list(APPEND OC_CXX_FLAGS "-std=gnu++14")
endif()
@ -1142,7 +1139,7 @@ endif()
if(GTK3_FOUND AND GTK3_gtksourceview_FOUND)
add_executable(mape ${MAPE_BASE_SOURCES} ${MAPE_SOURCES})
set_property(TARGET mape APPEND PROPERTY COMPILE_FLAGS ${GTK3_COMPILE_DEFINITIONS})
target_compile_options(mape PRIVATE ${GTK3_COMPILE_DEFINITIONS} ${GTK3_gtksourceview_COMPILE_DEFINITIONS})
target_include_directories(mape PRIVATE ${GTK3_INCLUDE_DIRS} ${GTK3_gtksourceview_INCLUDE_DIRS})
target_link_libraries(mape
${GTK3_LIBRARIES}
@ -1172,18 +1169,8 @@ if(WIN32)
target_link_libraries(c4script ws2_32)
endif()
if(NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.10")
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG>)
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
else()
# CMake started supporting generator expressions in 2.8.10. Earlier
# versions pass most of this through unmodified, which confuses make.
# Once we stop supporting old versions of CMake, this case can be removed.
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG _DEBUG)
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_RELEASE NDEBUG)
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_RELWITHDEBINFO NDEBUG)
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_MINSIZEREL NDEBUG)
endif()
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG>)
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
# This expands some variables in Info.plist as a side-effect. XCode might then
# expand a second time, using the same syntax. Try not to get confused by this!
@ -1493,9 +1480,4 @@ set(CPACK_PACKAGE_VERSION_MINOR "${C4XVER2}")
set(CPACK_PACKAGE_FILE_NAME "openclonk-${C4XVER1}.${C4XVER2}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "openclonk-src-${C4XVER1}.${C4XVER2}")
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
# Somebody who uses Debian/Ubuntu should set this
#set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libgcc1, libx11, libXrandr, libXpm, libGLEW, libGL, libpng, libSDL-1.2, libSDL_mixer-1.2, gtk2, libjpeg, zlib")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_REQUIRES "libc6, libgcc1, libx11, libXrandr, libXpm, libGLEW, libGL, libpng, libSDL-1.2, libSDL_mixer-1.2, gtk2, libjpeg, zlib")
include(CPack)

View File

@ -27,10 +27,10 @@ Martin Strohmeier (K-Pone)
Tobias Zwick (Newton)
<Thanks to our package maintainers>
Benedict Etzel (B_E), Phillip Kern (pkern), Kevin Zheng and more
Benedict Etzel (B_E), Philipp Kern (pkern), Kevin Zheng and more
<Special Thanks to Contributors>
Martin Adam (Win), Florian Graier (Nachtfalter), Mark Haßelbusch (Marky), Merten Ehmig (pluto), Benjamin Herr (Loriel), Armin Schäfer, Pyrit, Philip Holzmann (Batman), Alexander Semeniuk (AlteredARMOR), Andriel, Peewee, Oliver Schneider (ker), Fabian Pietsch, Manuel Rieke (MrBeast), Felix Riese (Fungiform), Carl-Philip Hänsch (Carli), Sebastian Rühl, Gurkenglas and many more:
Luchs, Asmageddon, mizipzor, Tim Blume, Apfelclonk, Sven-Hendrik Haase, Lauri Niskanen (Ape), Daniel Theuke (ST-DDT), Russell, Stan, TomyLobo, Clonkine, Koronis, Johannes Nixdorf (mixi), grgecko, Dominik Bayerl, Misty de Meo, Lorenz Schwittmann, hasufell, Jan Heberer, dylanstrategie, Checkmaty, Faby
Also, big thanks to Matthes Bender and all those who contributed to previous Clonk titles for the passion they put into the game and for agreeing to make Clonk open source.
Also, big thanks to Matthes Bender and all those who contributed to previous Clonk titles for the passion they put into the game and for agreeing to make Clonk open source.

View File

@ -1,10 +1,13 @@
/**
/*
Hot Ice
Ice islands above a lava lake
@authors Sven2
*/
static g_player_spawn_positions;
static g_map_width;
// Called be the engine: draw the complete map here.
public func InitializeMap(proplist map)
{
@ -12,35 +15,77 @@ public func InitializeMap(proplist map)
// Map type 1: Only many small islands
var t = SCENPAR_MapType;
var w = map.Wdt, h=map.Hgt;
g_map_width = w;
// Bottom lava lake
map->Draw("^DuroLava", nil, [0,h*4/5,w,h/5]);
// Big island
if (t == 0)
{
var island = { Algo=MAPALGO_Polygon, X=[0,w,w*6/8,w*2/8], Y=[h*4/10,h*4/10,h*7/10,h*7/10] };
island = { Algo=MAPALGO_Turbulence, Op=island, Amplitude=[0, 8] };
map->Draw("^Ice-ice2", island, [w/10,h*13/20,w*8/10,h*3/20]);
}
// Small islands
var n_islands = [12,37][t];
while(n_islands--)
{
var y = h*2/10 + Random(h*(3+t*2)/10);
var x = w*1/10 + Random(w*8/10);
var szx = t*Random(3);
var szy = 1+t*Random(Random(2));
map->Draw("^Ice-ice2", nil, [x-szx,y,1+2*szx,szy]);
}
// Alternate texctures
var icealt_tex = { Algo=MAPALGO_RndChecker, Wdt=2, Hgt=3 };
icealt_tex = { Algo=MAPALGO_Turbulence, Op=icealt_tex };
icealt_tex = { Algo=MAPALGO_And, Op=[Duplicate("Ice"), icealt_tex]};
map->Draw("^Ice-ice3", icealt_tex);
if (t == 0) DrawBigIslandMap(map);
if (t == 1) DrawSmallIslandsMap(map);
// Alternate texctures
var icealt_tex = { Algo=MAPALGO_RndChecker, Wdt=2, Hgt=3 };
icealt_tex = { Algo=MAPALGO_Turbulence, Op=icealt_tex };
icealt_tex = { Algo=MAPALGO_And, Op=[Duplicate("Ice"), icealt_tex]};
map->Draw("^Ice-ice", icealt_tex);
// Return true to tell the engine a map has been successfully created.
return true;
}
func DrawBigIslandMap(proplist map)
{
var w = map.Wdt, h=map.Hgt;
// Draw one big island as the ground and some smaller islands floating above
// Big
var island = { Algo=MAPALGO_Polygon, X=[0,w,w*6/8,w*2/8], Y=[h*4/10,h*4/10,h*7/10,h*7/10] };
island = { Algo=MAPALGO_Turbulence, Op=island, Amplitude=[0, 8] };
map->Draw("^Ice-ice2", island, [w/10,h*13/20,w*8/10,h*3/20]);
// Make sure one row of inner island is drawn because it's used for player spawns
map->Draw("^Ice-ice2", nil, [w*3/10,h*13/20,w*4/10+1,1]);
// Smaller floating
var n_islands = 12;
while(n_islands--)
{
var x = w*1/10 + Random(w*8/10);
var y = h*2/10 + Random(h*3/10);
map->Draw("^Ice-ice2", nil, [x,y,1,1]);
}
// Player spawns simply in middle of big island
var plrcnt = GetStartupPlayerCount();
g_player_spawn_positions = CreateArray(plrcnt);
for (var i = 0; i < plrcnt; ++i)
{
g_player_spawn_positions[i] = [w*3/10 + i*w*4/10/(plrcnt-1), h*13/20-1];
}
return true;
}
func DrawSmallIslandsMap(proplist map)
{
var w = map.Wdt, h=map.Hgt, x, y, szx, szy;
// Islands in center of map
var n_islands = 35;
while(n_islands--)
{
y = h*3/10 + Random(h*5/10 - 3);
var xrange = w * (y)/(h*9/10);
x = w/2 - xrange/2 + Random(xrange);
szx = Random(3);
szy = 1;
if (y > h/2) szy += Random(2); // lower islands sometimes taller
if (Abs(x-w/2) < w/10) szx += Random(3); // central islands sometimes wider
map->Draw("^Ice-ice2", nil, [x-szx,y,1+2*szx,szy]);
}
// Starting islands for player spawns
var spawn_island_count = GetStartupPlayerCount();
g_player_spawn_positions = CreateArray(spawn_island_count);
for (var i = 0; i < spawn_island_count; ++i)
{
var x = w*2/10 + i * (w*6/10) / (spawn_island_count - 1);
var y = Max(1, h/10) + Abs(x-w/2) * 3*h/10/w;
map->Draw("^Ice-ice2", nil, [x,y,1,1]);
g_player_spawn_positions[i] = [x, y-1];
}
return true;
}

View File

@ -5,9 +5,10 @@ func Initialize()
// Materials: Chests
var i,pos;
var ls_wdt = LandscapeWidth(), ls_hgt = LandscapeHeight();
var top_area_hgt = ls_hgt*[50,80][SCENPAR_MapType]/100;
var chest_area_y = ls_hgt*[0,30][SCENPAR_MapType]/100;
var chest_area_hgt = ls_hgt/2;
for (i=0; i<6; ++i)
if (pos=FindLocation(Loc_InRect(0,0,ls_wdt,top_area_hgt-100), Loc_Wall(CNAT_Bottom))) // Loc_Wall adds us 100 pixels...
if (pos=FindLocation(Loc_InRect(0,chest_area_y,ls_wdt,chest_area_hgt-100), Loc_Wall(CNAT_Bottom))) // Loc_Wall adds us 100 pixels...
{
var chest = CreateObjectAbove(Chest,pos.x,pos.y);
if (chest)
@ -24,27 +25,40 @@ func Initialize()
}
// Materials: Firestones
for (i=0; i<30; ++i)
if (pos=FindLocation(Loc_InRect(0,0,ls_wdt,top_area_hgt), Loc_Solid()))
if (pos=FindLocation(Loc_InRect(0,chest_area_y,ls_wdt,chest_area_hgt), Loc_Solid()))
if (IsFirestoneSpot(pos.x,pos.y))
CreateObjectAbove(Firestone,pos.x,pos.y-1);
// Some firestones in lower half. For ap type 1, more firestones in lower than upper half.
// Some firestones and bombs in lower half. For ap type 1, more firestones in lower than upper half.
for (i=0; i<30; ++i)
if (pos=FindLocation(Loc_InRect(0,ls_hgt/2,ls_wdt,ls_hgt/3), Loc_Solid()))
if (IsFirestoneSpot(pos.x,pos.y))
CreateObjectAbove(Firestone,pos.x,pos.y-1);
CreateObjectAbove([Firestone,IronBomb][Random(Random(3))],pos.x,pos.y-1);
return true;
}
static g_player_spawn_positions, g_map_width, g_player_spawn_index;
func InitializePlayer(int plr)
{
// everything visible
SetFoW(false, plr);
// player positioning. In lower area for both maps becuase starting high is an an advantage.
// Player positioning.
var ls_wdt = LandscapeWidth(), ls_hgt = LandscapeHeight();
var crew = GetCrew(plr);
var start_pos = FindLocation(Loc_InRect(ls_wdt/5,ls_hgt/2,ls_wdt*3/5,ls_hgt/3), Loc_Wall(CNAT_Bottom), Loc_Func(Scenario.IsStartSpot));
if (!start_pos) start_pos = FindLocation(Loc_InRect(ls_wdt/10,0,ls_wdt*8/10,ls_hgt*4/5), Loc_Wall(CNAT_Bottom), Loc_Func(Scenario.IsStartSpot));
if (!start_pos) start_pos = {x=Random(ls_wdt*6/10)+ls_wdt*2/10, y=ls_hgt*58/100};
var crew = GetCrew(plr), start_pos;
// Position by map type?
if (g_player_spawn_positions && g_player_spawn_index < GetLength(g_player_spawn_positions))
{
start_pos = g_player_spawn_positions[g_player_spawn_index++];
var map_zoom = ls_wdt / g_map_width;
start_pos = {x=start_pos[0]*map_zoom+map_zoom/2, y=start_pos[1]*map_zoom};
}
else
{
// Start positions not defined or exhausted: Spawn in lower area for both maps becuase starting high is an an advantage.
start_pos = FindLocation(Loc_InRect(ls_wdt/5,ls_hgt/2,ls_wdt*3/5,ls_hgt/3), Loc_Wall(CNAT_Bottom), Loc_Func(Scenario.IsStartSpot));
if (!start_pos) start_pos = FindLocation(Loc_InRect(ls_wdt/10,0,ls_wdt*8/10,ls_hgt*4/5), Loc_Wall(CNAT_Bottom), Loc_Func(Scenario.IsStartSpot));
if (!start_pos) start_pos = {x=Random(ls_wdt*6/10)+ls_wdt*2/10, y=ls_hgt*58/100};
}
crew->SetPosition(start_pos.x, start_pos.y-10);
// initial material
crew->CreateContents(Shovel);

View File

@ -386,9 +386,8 @@ func Statue_Death()
while (i--) EliminatePlayer(GetPlayerByIndex(i, C4PT_User));
// Statue down :(
CastObjects(Nugget, 5, 10);
Explode(10);
ScheduleCall(nil, Global.GameOver, 50, 1);
return true;
return Explode(10);
}
/* Developer commands */

View File

@ -76,14 +76,14 @@ slice(texture+5)
// Query light texture
vec2 lightDirCoord = lightCoord;
vec4 lightPx = texture2D(lightTex, lightDirCoord);
vec4 lightPx = texture(lightTex, lightDirCoord);
float lightBright = maxLightBrightness * max(0.0, (lightPx.a-lightDarknessLevel)/(1.0-lightDarknessLevel));
vec3 lightDir = normalize(vec3(vec2(1.0, 1.0) - lightPx.yz * 3.0, lightDepth));
// Query light color texture (part of the light texture)
vec2 lightColorCoord = lightCoord - vec2(0.0, 0.5); // subtract offset for the color texture
vec3 lightColor = texture2D(lightTex, lightColorCoord.st).rgb;
vec3 lightColor = texture(lightTex, lightColorCoord.st).rgb;
// Normalise light colour
#ifdef LIGHT_DEBUG_COLOR
@ -93,7 +93,7 @@ slice(texture+5)
// Ambient light
// Edxtra .xy since some old intel drivers return a vec3
float ambient = texture2D(ambientTex, (ambientTransform * vec3(gl_FragCoord.xy, 1.0)).xy).r;
float ambient = texture(ambientTex, (ambientTransform * vec3(gl_FragCoord.xy, 1.0)).xy).r;
ambient *= ambientBrightness;
#ifdef OC_SKY
ambient = 0.999; // TODO: = 1.0 causes bugs?
@ -144,7 +144,7 @@ slice(color+5)
// "spotty" and allow the material to self-illuminate. The light
// brightness overrules everything though (= FoW is last factor).
vec3 spotLight = pow(vec3(light,light,light), matSpot);
color.rgb = lightBright * color.rgb * (matEmit + lightColorNorm * spotLight);
fragColor.rgb = lightBright * fragColor.rgb * (matEmit + lightColorNorm * spotLight);
#ifdef OC_LANDSCAPE
vec3 spotLight2 = pow(vec3(light2,light2,light2), matSpot2);
color2.rgb = lightBright * color2.rgb * (matEmit2 + lightColorNorm * spotLight2);
@ -159,18 +159,18 @@ slice(finish+5)
float lightYDir = lightPx.b - 1.0/3.0;
float lightXDir = lightPx.g - 1.0/3.0;
float lightStrength = lightPx.a;
color =
fragColor =
vec4(lightStrength * vec3(1.0-1.5*(max(0.0, lightYDir) + max(0.0,lightXDir)),
1.0-1.5*(max(0.0, lightYDir) + max(0.0,-lightXDir)),
1.0-1.5*max(0.0, -lightYDir)),
1.0);
#else
color = vec4(0.0, 0.0, 0.0, 0.0); // invisible
fragColor = vec4(0.0, 0.0, 0.0, 0.0); // invisible
#endif
#endif
}
slice(finish+10) {
color = vec4(pow(color.rgb, gamma), color.a);
fragColor = vec4(pow(fragColor.rgb, gamma), fragColor.a);
}

View File

@ -1,14 +1,14 @@
// Interpolated texture coordinates
varying vec2 landscapeTexCoord;
in vec2 landscapeTexCoord;
#ifdef OC_DYNAMIC_LIGHT
varying vec2 lightTexCoord;
in vec2 lightTexCoord;
#endif
// Input textures
uniform sampler2D landscapeTex[2];
uniform sampler2D scalerTex;
uniform sampler3D materialTex;
uniform sampler2DArray materialTex;
// Resolution of the landscape texture
uniform vec2 resolution;
@ -20,6 +20,8 @@ uniform sampler1D matMapTex;
uniform float materialDepth;
uniform vec2 materialSize;
out vec4 fragColor;
// Expected parameters for the scaler
const vec2 scalerStepX = vec2(1.0 / 8.0, 0.0);
const vec2 scalerStepY = vec2(0.0, 1.0 / 32.0);
@ -28,7 +30,7 @@ const vec2 scalerPixel = vec2(scalerStepX.x, scalerStepY.y) / 3.0;
vec4 queryMatMap(int pix)
{
return texture1D(matMapTex, float(pix) / 2.0 / 256.0 + 0.5 / 2.0 / 256.0);
return texture(matMapTex, float(pix) / 2.0 / 256.0 + 0.5 / 2.0 / 256.0);
}
slice(init)
@ -67,8 +69,8 @@ slice(coordinate)
slice(texture)
{
// our pixel color (without/with interpolation)
vec4 landscapePx = texture2D(landscapeTex[0], centerCoo);
vec4 realLandscapePx = texture2D(landscapeTex[0], texCoo);
vec4 landscapePx = texture(landscapeTex[0], centerCoo);
vec4 realLandscapePx = texture(landscapeTex[0], texCoo);
// find scaler coordinate
vec2 scalerCoo = scalerOffset + mod(pixelCoo, vec2(1.0, 1.0)) * scalerPixel;
@ -77,12 +79,12 @@ slice(texture)
scalerCoo.y += float(iScaler / 8) / 32.0;
// query scaler texture
vec4 scalerPx = texture2D(scalerTex, scalerCoo);
vec4 scalerPx = texture(scalerTex, scalerCoo);
// Get "second" landscape pixel
vec2 centerCoo2 = centerCoo + fullStep * floor(vec2(-0.5, -0.5) +
scalerPx.gb * 255.0 / 64.0);
vec4 landscapePx2 = texture2D(landscapeTex[0], centerCoo2);
vec4 landscapePx2 = texture(landscapeTex[0], centerCoo2);
}
@ -95,30 +97,31 @@ slice(texture+4)
slice(material)
{
// Get material properties from material map
int matMapIx = f2i(landscapePx.r);
vec4 matMap = queryMatMap(2*matMapIx);
vec4 matMapX = queryMatMap(2*matMapIx+1);
float materialIx = float(f2i(matMap.a)) / 256.0 + 0.5 / materialDepth;
float materialIx = f2i(matMap.a) / 256.0 * materialDepth;
vec3 matEmit = matMap.rgb;
vec3 matSpot = matMapX.rgb * 255.9f / 16.0f;
float matAngle = matMapX.a;
// Query material texture pixels
vec4 materialPx = texture3D(materialTex, vec3(materialCoo, materialIx));
vec4 normalPx = texture3D(materialTex, vec3(materialCoo, materialIx+0.5));
vec4 materialPx = texture(materialTex, vec3(materialCoo, materialIx));
vec4 normalPx = texture(materialTex, vec3(materialCoo, materialIx+0.5 * materialDepth));
// Same for second pixel
int matMapIx2 = f2i(landscapePx2.r);
vec4 matMap2 = queryMatMap(2*matMapIx2);
vec4 matMapX2 = queryMatMap(2*matMapIx2+1);
float materialIx2 = float(f2i(matMap2.a)) / 256.0 + 0.5 / materialDepth;
float materialIx2 = f2i(matMap2.a) / 256.0 * materialDepth;
vec3 matEmit2 = matMap2.rgb;
vec3 matSpot2 = matMapX2.rgb * 255.9f / 16.0f;
float matAngle2 = matMapX2.a;
// Query material texture pixels
vec4 materialPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2));
vec4 normalPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2+0.5));
vec4 materialPx2 = texture(materialTex, vec3(materialCoo, materialIx2));
vec4 normalPx2 = texture(materialTex, vec3(materialCoo, materialIx2+0.5 * materialDepth));
}
slice(normal)
@ -144,12 +147,11 @@ slice(normal)
}
slice(color) {
#define color gl_FragColor
color = materialPx;
fragColor = materialPx;
vec4 color2 = materialPx2;
}
slice(color+10) {
// Mix second color into main color according to scaler
color = mix(color2, color, scalerPx.r);
fragColor = mix(color2, fragColor, scalerPx.r);
}

View File

@ -15,13 +15,13 @@
// Default Vertex Shader for the landscape.
attribute vec2 oc_Position;
attribute vec2 oc_LandscapeTexCoord;
attribute vec2 oc_LightTexCoord;
in vec2 oc_Position;
in vec2 oc_LandscapeTexCoord;
in vec2 oc_LightTexCoord;
varying vec2 landscapeTexCoord;
out vec2 landscapeTexCoord;
#ifdef OC_DYNAMIC_LIGHT
varying vec2 lightTexCoord;
out vec2 lightTexCoord;
#endif
uniform mat4 projectionMatrix;

View File

@ -36,12 +36,12 @@
#define MAX_BONE_COUNT 80
attribute vec3 oc_Position;
attribute vec3 oc_Normal;
attribute vec2 oc_TexCoord;
in vec3 oc_Position;
in vec3 oc_Normal;
in vec2 oc_TexCoord;
varying vec3 vtxNormal;
varying vec2 texcoord;
out vec3 vtxNormal;
out vec2 texcoord;
uniform mat4 projectionMatrix;
uniform mat4 modelviewMatrix;
@ -58,12 +58,12 @@ uniform mat3x4 bones[MAX_BONE_COUNT];
// respectively. (Or we could split it even further.)
#define BONE_COUNT 8
attribute vec4 oc_BoneIndices0;
attribute vec4 oc_BoneWeights0;
in vec4 oc_BoneIndices0;
in vec4 oc_BoneWeights0;
#if BONE_COUNT > 4
attribute vec4 oc_BoneIndices1;
attribute vec4 oc_BoneWeights1;
in vec4 oc_BoneIndices1;
in vec4 oc_BoneWeights1;
#endif
#ifndef OC_WA_LOW_MAX_VERTEX_UNIFORM_COMPONENTS

View File

@ -1,6 +1,13 @@
uniform mat3x2 lightTransform;
#ifdef OC_MESH
//uniform vec4 materialAmbient;
uniform vec4 materialDiffuse;
uniform vec4 materialEmission;
uniform vec4 materialSpecular;
#endif
#ifdef OC_WITH_NORMALMAP
uniform mat3 normalMatrix;
uniform sampler2D normalTex;
@ -14,50 +21,55 @@ uniform sampler2D overlayTex;
#endif
#ifdef OC_MESH
varying vec3 vtxNormal;
varying vec2 texcoord;
in vec3 vtxNormal;
in vec2 texcoord;
#endif
#ifndef OC_MESH
varying vec4 vtxColor;
in vec4 vtxColor;
#ifdef OC_HAVE_BASE
uniform sampler2D baseTex;
varying vec2 texcoord;
in vec2 texcoord;
#endif
#endif
out vec4 fragColor;
slice(material)
{
// Default material properties. TODO: Populate them?
vec3 matEmit = vec3(0.0,0.0,0.0);
vec3 matSpot = vec3(1.0,1.0,1.0);
// Default material properties.
#ifdef OC_MESH
vec3 matEmit = vec3(0.0, 0.0, 0.0);//materialEmission.rgb;
vec3 matSpot = vec3(1.0, 1.0, 1.0);//materialSpecular.rgb;
#else
vec3 matEmit = vec3(0.0, 0.0, 0.0);
vec3 matSpot = vec3(1.0, 1.0, 1.0);
#endif
float matAngle = 1.0;
}
slice(texture)
{
#define color gl_FragColor
#ifdef OC_MESH
// TODO: Add emission part of the material. Note we cannot just
// add this to the color, but it would need to be handled separately,
// such that it is independent from the incident light direction.
color = gl_FrontMaterial.diffuse;
fragColor = materialDiffuse;
#else
#ifdef OC_HAVE_BASE
// Texturing: Use color from texture, modulated with vertex color
color = vtxColor * texture2D(baseTex, texcoord);
fragColor = vtxColor * texture(baseTex, texcoord);
#ifdef OC_HAVE_OVERLAY
// Get overlay color from overlay texture
vec4 overlay = vtxColor * overlayClr * texture2D(overlayTex, texcoord);
vec4 overlay = vtxColor * overlayClr * texture(overlayTex, texcoord);
// Mix overlay with texture
float alpha0 = 1.0 - (1.0 - color.a) * (1.0 - overlay.a);
color = vec4(mix(color.rgb, overlay.rgb, overlay.a / alpha0), alpha0);
float alpha0 = 1.0 - (1.0 - fragColor.a) * (1.0 - overlay.a);
fragColor = vec4(mix(fragColor.rgb, overlay.rgb, overlay.a / alpha0), alpha0);
#endif
#else
// No texturing: Just use color assigned to vertex
color = vtxColor;
fragColor = vtxColor;
#endif
#endif
}
@ -74,7 +86,7 @@ slice(texture+4)
slice(normal)
{
#ifdef OC_WITH_NORMALMAP
vec4 normalPx = texture2D(normalTex, texcoord);
vec4 normalPx = texture(normalTex, texcoord);
vec3 normalPxDir = 2.0 * (normalPx.xyz - vec3(0.5, 0.5, 0.5));
vec3 normal = normalize(normalMatrix * normalPxDir);
#else
@ -98,8 +110,8 @@ slice(color)
// out = (color, clrmod, 1) * (A,B,C,D,E,F,0,0,G) * (color, clrmod, 1)
#ifdef OC_CLRMOD_MOD2
color = vec4(clamp(color.rgb + clrMod.rgb - 0.5, 0.0, 1.0), color.a * clrMod.a);
fragColor = vec4(clamp(fragColor.rgb + clrMod.rgb - 0.5, 0.0, 1.0), fragColor.a * clrMod.a);
#else
color = color * clrMod;
fragColor = fragColor * clrMod;
#endif
}

View File

@ -18,17 +18,17 @@
uniform mat4 projectionMatrix;
uniform mat4 modelviewMatrix;
attribute vec2 oc_Position;
attribute vec4 oc_Color;
in vec2 oc_Position;
in vec4 oc_Color;
#ifdef OC_HAVE_BASE
attribute vec2 oc_TexCoord;
in vec2 oc_TexCoord;
#endif
varying vec4 vtxColor;
out vec4 vtxColor;
#ifdef OC_HAVE_BASE
varying vec2 texcoord;
out vec2 texcoord;
#endif
slice(position)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -28,10 +28,8 @@ func InitializeObjects()
var Tree_Coniferous2003 = CreateObjectAbove(Tree_Coniferous2, 347, 565);
var Tree_Coniferous2004 = CreateObjectAbove(Tree_Coniferous2, 422, 558);
CreateObjectAbove(Tree_Coniferous2, 1329, 384);
CreateObjectAbove(Tree_Coniferous2, 1364, 364);
CreateObjectAbove(Tree_Coniferous2, 1389, 327);
CreateObjectAbove(Tree_Coniferous2, 1295, 398);
CreateObjectAbove(Tree_Coniferous2, 1404, 390);
CreateObjectAbove(Tree_Coniferous2, 1290, 403);
CreateObject(Fern, 1331, 701);
CreateObject(Fern, 1468, 661);
@ -143,7 +141,7 @@ func InitializeObjects()
CreateObject(Loam, 1360, 781);
CreateObject(Loam, 1519, 721);
CreateObject(Loam, 1348, 718);
CreateObject(Loam, 1379, 349);
CreateObject(Loam, 1358, 388);
CreateObject(Loam, 559, 1120);
CreateObject(Loam, 505, 850);
CreateObject(Loam, 517, 858);

View File

@ -64,7 +64,7 @@ func EnsureTrees(proplist area)
return true;
}
global func FxIntWaterfallTimer(object obj, int eff)
global func FxIntWaterfallTimer(object obj, proplist eff)
{
InsertMaterial(Material("Water"), 1560,840);
ExtractLiquid(1314,901);

View File

@ -259,8 +259,8 @@ func Intro_Stop()
if (plane)
{
plane->FaceLeft();
plane->SetR(-130);
plane->SetPosition(1387, 238);
plane->SetR(-90);
plane->SetPosition(1387, 345);
}
return true;
}

View File

@ -7,27 +7,27 @@ Sky=Clouds2
[Player1]
Wealth=25
Crew=Clonk=1
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;Chest=1;WoodenBridge=1;
BaseMaterial=Bread=25;
BaseProduction=Bread=25;
[Player2]
Wealth=25
Crew=Clonk=1
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;Chest=1;WoodenBridge=1;
BaseMaterial=Bread=25;
BaseProduction=Bread=25;
[Player3]
Wealth=25
Crew=Clonk=1
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;Chest=1;WoodenBridge=1;
BaseMaterial=Bread=25;
BaseProduction=Bread=25;
[Player4]
Wealth=25
Crew=Clonk=1
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;Chest=1;WoodenBridge=1;
BaseMaterial=Bread=25;
BaseProduction=Bread=25;

View File

@ -159,6 +159,7 @@ func Intro_11()
{
crew->SetPosition(g_tuesday_pos[0],-100);
crew->SetXDir(-10); crew->SetYDir(-30);
crew->SetAction("Tumble");
if (!index) SetPlrView(plr, crew);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -1,6 +1,6 @@
/* Automatically created objects file */
static g_flagpole, npc_dagobert, npc_tarzan, g_golden_shovel, g_golden_idol, g_last_stone_door;
static g_flagpole, g_golden_idol, npc_dagobert, npc_tarzan, g_golden_shovel, g_last_stone_door;
func InitializeObjects()
{
@ -11,14 +11,20 @@ func InitializeObjects()
CreateObjectAbove(Grass, 1564, 493);
CreateObjectAbove(Grass, 1537, 525);
CreateObjectAbove(Grass, 1585, 486);
CreateObject(Grass, 1739, 429);
CreateObjectAbove(Grass, 1739, 430);
var Torch001 = CreateObjectAbove(Torch, 1869, 1454);
var Torch001 = CreateObjectAbove(Torch, 1201, 1549);
Torch001->AttachToWall(true);
var Torch002 = CreateObjectAbove(Torch, 562, 1126);
var Torch002 = CreateObjectAbove(Torch, 1109, 1146);
Torch002->AttachToWall(true);
var Torch003 = CreateObjectAbove(Torch, 923, 1144);
Torch003->AttachToWall(true);
var Torch004 = CreateObjectAbove(Torch, 1869, 1454);
Torch004->AttachToWall(true);
var Torch005 = CreateObjectAbove(Torch, 562, 1126);
Torch005->AttachToWall(true);
var Chest001 = CreateObjectAbove(Chest, 1002, 313);
var Chest001 = CreateObject(Chest, 1002, 302);
Chest001.Plane = 50;
var Column001 = CreateObjectAbove(Column, 779, 591);
@ -104,10 +110,10 @@ func InitializeObjects()
var Lichen001 = CreateObjectAbove(Lichen, 2694, 706);
Lichen001->SetAction("Grown");
CreateObjectAbove(BigRock, 1301, 500);
CreateObject(BigRock, 1301, 497);
CreateObjectAbove(BigRock, 1207, 282);
CreateObject(BigRock, 1291, 260);
var Amethyst001 = CreateObjectAbove(Amethyst, 803, 583);
var Amethyst001 = CreateObject(Amethyst, 803, 579);
Amethyst001.Plane = 190;
var Chest002 = CreateObjectAbove(Chest, 515, 967);
@ -139,85 +145,12 @@ func InitializeObjects()
var Chest019 = CreateObjectAbove(Chest, 730, 135);
var Chest020 = CreateObjectAbove(Chest, 1626, 1591);
var StoneDoor001 = CreateObject(StoneDoor, 940, 1132);
StoneDoor001->SetComDir(COMD_Down);
StoneDoor001->MakeInvincible();
var StoneDoor002 = CreateObject(StoneDoor, 1084, 1132);
StoneDoor002->SetComDir(COMD_Down);
StoneDoor002->MakeInvincible();
var StoneDoor003 = CreateObject(StoneDoor, 564, 436);
StoneDoor003->SetComDir(COMD_Down);
StoneDoor003->MakeInvincible();
var StoneDoor004 = CreateObject(StoneDoor, 843, 716);
StoneDoor004->SetComDir(COMD_Down);
StoneDoor004->MakeInvincible();
var StoneDoor005 = CreateObject(StoneDoor, 1058, 700);
StoneDoor005->SetComDir(COMD_Down);
StoneDoor005->MakeInvincible();
var StoneDoor006 = CreateObject(StoneDoor, 1092, 1028);
StoneDoor006->SetComDir(COMD_Down);
StoneDoor006->MakeInvincible();
var StoneDoor007 = CreateObject(StoneDoor, 1892, 932);
StoneDoor007->SetComDir(COMD_Down);
StoneDoor007->MakeInvincible();
var StoneDoor008 = CreateObject(StoneDoor, 813, 716);
StoneDoor008->SetComDir(COMD_Down);
StoneDoor008->MakeInvincible();
g_last_stone_door = CreateObject(StoneDoor, 781, 716);
g_last_stone_door.StaticSaveVar = "g_last_stone_door";
g_last_stone_door->SetComDir(COMD_Down);
g_last_stone_door->SetClrModulation(0xffa0a0a0);
var StoneDoor010 = CreateObject(StoneDoor, 692, 748);
StoneDoor010->SetComDir(COMD_Down);
StoneDoor010->MakeInvincible();
var StoneDoor011 = CreateObject(StoneDoor, 684, 332);
StoneDoor011->SetComDir(COMD_Down);
StoneDoor011->MakeInvincible();
var SpinWheel001 = CreateObjectAbove(SpinWheel, 589, 457);
SpinWheel001->SetMeshMaterial("SpinWheelGearRed", 0);
SpinWheel001->SetStoneDoor(StoneDoor001);
var SpinWheel002 = CreateObjectAbove(SpinWheel, 611, 456);
SpinWheel002->SetMeshMaterial("SpinWheelGearBlue", 0);
SpinWheel002->SetStoneDoor(StoneDoor002);
var SpinWheel003 = CreateObjectAbove(SpinWheel, 619, 410);
SpinWheel003->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel003->SetStoneDoor(StoneDoor003);
var SpinWheel004 = CreateObjectAbove(SpinWheel, 1223, 1553);
SpinWheel004->SetStoneDoor(StoneDoor005);
var SpinWheel005 = CreateObjectAbove(SpinWheel, 1117, 1048);
SpinWheel005->SetStoneDoor(StoneDoor006);
var SpinWheel006 = CreateObjectAbove(SpinWheel, 2761, 690);
SpinWheel006->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel006->SetStoneDoor(StoneDoor008);
var SpinWheel007 = CreateObjectAbove(SpinWheel, 1850, 1463);
SpinWheel007->SetMeshMaterial("SpinWheelGearRed", 0);
SpinWheel007->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel007->SetStoneDoor(StoneDoor004);
var SpinWheel008 = CreateObjectAbove(SpinWheel, 2793, 1521);
SpinWheel008->SetMeshMaterial("SpinWheelGearRed", 0);
SpinWheel008->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel008->SetStoneDoor(StoneDoor007);
var SpinWheel009 = CreateObjectAbove(SpinWheel, 830, 735);
SpinWheel009->SetStoneDoor(StoneDoor010);
var SpinWheel010 = CreateObjectAbove(SpinWheel, 703, 352);
SpinWheel010->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel010->SetStoneDoor(StoneDoor011);
CreateObjectAbove(Pump, 1027, 1152);
CreateObjectAbove(Sawmill, 1259, 1047);
var ToolsWorkshop001 = CreateObjectAbove(ToolsWorkshop, 1169, 903);
var Column002 = CreateObject(Column, 779, 488);
Column002->SetR(180);
Column002->SetClrModulation(0xffffd0d0);
Column002->SetMeshMaterial("AncientColumn", 0);
var Column003 = CreateObject(Column, 1419, 217);
Column003->SetMeshMaterial("AncientColumn", 0);
CreateObject(Column, 1386, 616);
CreateObjectAbove(Ruin_Windmill, 1678, 375);
CreateObjectAbove(Ruin_WoodenCabin, 1199, 1046);
@ -225,17 +158,18 @@ func InitializeObjects()
var Idol001 = CreateObjectAbove(Idol, 1045, 721);
Idol001->SetMeshMaterial("IdolGrayColor", 0);
g_flagpole = CreateObjectAbove(Flagpole, 210, 1185);
g_flagpole = CreateObject(Flagpole, 210, 1151);
g_flagpole.StaticSaveVar = "g_flagpole";
g_flagpole->SetName("Respawn");
g_flagpole->SetNeutral(true);
var LotsOfCoins001 = CreateObject(LotsOfCoins, 805, 583);
LotsOfCoins001.Plane = 200;
g_golden_idol = CreateObject(Idol, 824, 568);
g_golden_idol.StaticSaveVar = "g_golden_idol";
g_golden_idol->SetR(-4);
g_golden_idol.Plane = 220;
g_golden_idol.StaticSaveVar = "g_golden_idol";
var Lorry002 = CreateObjectAbove(Lorry, 200, 1183);
var Lorry001 = CreateObjectAbove(Lorry, 708, 1407);
@ -291,7 +225,7 @@ func InitializeObjects()
Clonk007->SetColor(0x802000);
Clonk007->SetName("Jane");
Clonk007->SetSkin(1);
npc_tarzan = CreateObjectAbove(Clonk, 754, 879);
npc_tarzan = CreateObjectAbove(Clonk, 750, 859);
npc_tarzan->SetXDir(3);
npc_tarzan->SetYDir(27);
npc_tarzan->SetColor(0x402000);
@ -345,10 +279,8 @@ func InitializeObjects()
Bone004->SetYDir(8);
var Bone005 = CreateObject(Bone, 479, 964);
Bone005->SetR(-51);
Bone005->SetYDir(8);
var Bone006 = CreateObject(Bone, 464, 965);
Bone006->SetR(-51);
Bone006->SetYDir(8);
Lorry002->CreateContents(Loam, 4);
CreateObject(Loam, 153, 1232);
@ -401,24 +333,24 @@ func InitializeObjects()
ToolsWorkshop001->CreateContents(GoldBar);
CreateObject(GoldBar, 72, 1463);
CreateObject(GoldBar, 2746, 736);
CreateObject(GoldBar, 2507, 1262);
CreateObjectAbove(GoldBar, 2507, 1262);
Chest020->CreateContents(GoldBar);
var GoldBar003 = CreateObject(GoldBar, 972, 1277);
GoldBar003->SetR(55);
CreateObject(Ruby, 864, 581);
CreateObject(Ruby, 806, 583);
CreateObjectAbove(Ruby, 849, 582);
CreateObject(Ruby, 849, 578);
CreateObject(Ruby, 856, 584);
var Amethyst002 = CreateObject(Amethyst, 793, 584);
Amethyst002->SetR(22);
CreateObject(Amethyst, 885, 557);
CreateObject(Amethyst, 828, 581);
CreateObjectAbove(Amethyst, 828, 585);
Lorry002->CreateContents(Dynamite, 2);
Chest006->CreateContents(Dynamite, 3);
CreateObjectAbove(Dynamite, 1046, 722);
CreateObjectAbove(Dynamite, 790, 736);
Chest005->CreateContents(Dynamite, 2);
Chest004->CreateContents(Dynamite, 2);
Chest002->CreateContents(Dynamite);
@ -426,9 +358,14 @@ func InitializeObjects()
Chest014->CreateContents(Dynamite, 3);
var Bow001 = Clonk001->CreateContents(Bow);
Chest003->CreateContents(Bow);
Bow001->CreateContents(Arrow);
Clonk001->CreateContents(Arrow);
var Arrow001 = CreateObject(Arrow, 313, 431);
Arrow001->SetR(90);
var Arrow002 = CreateObject(Arrow, 313, 431);
Arrow002->SetR(90);
var Arrow003 = CreateObject(Arrow, 313, 431);
Arrow003->SetR(90);
var Arrow004 = CreateObject(Arrow, 313, 431);
@ -443,13 +380,19 @@ func InitializeObjects()
Arrow008->SetR(90);
var Arrow009 = CreateObject(Arrow, 313, 431);
Arrow009->SetR(90);
var Arrow010 = CreateObject(Arrow, 313, 431);
Arrow010->SetR(90);
var Arrow011 = CreateObject(Arrow, 313, 431);
Arrow011->SetR(90);
Chest003->CreateContents(Arrow);
Chest021->CreateContents(Bread, 2);
Lorry001->CreateContents(Bread, 3);
Chest004->CreateContents(Bread);
Chest017->CreateContents(Bread);
Chest009->CreateContents(Bread);
ToolsWorkshop001->CreateContents(Bread);
Chest011->CreateContents(Bread);
Chest012->CreateContents(Bread);
Chest018->CreateContents(Bread);
Chest007->CreateContents(Bread);
Chest002->CreateContents(Bread);
Chest021->CreateContents(DynamiteBox, 2);
Chest017->CreateContents(DynamiteBox, 2);
@ -462,6 +405,7 @@ func InitializeObjects()
Chest006->CreateContents(Sword);
Clonk004->CreateContents(Sword, 2);
Lorry001->CreateContents(Sword);
Chest003->CreateContents(Sword);
CreateObjectAbove(Seaweed, 2494, 1263);
CreateObjectAbove(Seaweed, 2508, 1263);
@ -482,16 +426,16 @@ func InitializeObjects()
CreateObjectAbove(Seaweed, 2395, 1239);
CreateObjectAbove(Seaweed, 2396, 1239);
CreateObjectAbove(Seaweed, 2332, 1145);
CreateObjectAbove(Seaweed, 2407, 1246);
CreateObject(Seaweed, 2407, 1239);
CreateObjectAbove(Mushroom, 1580, 759);
CreateObjectAbove(Mushroom, 1613, 775);
CreateObjectAbove(Mushroom, 1525, 846);
CreateObjectAbove(Mushroom, 1612, 862);
CreateObjectAbove(Mushroom, 1580, 758);
CreateObjectAbove(Mushroom, 1613, 776);
CreateObjectAbove(Mushroom, 1525, 847);
CreateObjectAbove(Mushroom, 1612, 864);
CreateObjectAbove(Mushroom, 1321, 895);
CreateObjectAbove(Mushroom, 1315, 894);
CreateObjectAbove(Mushroom, 1343, 903);
CreateObjectAbove(Mushroom, 1347, 901);
CreateObjectAbove(Mushroom, 1315, 895);
CreateObjectAbove(Mushroom, 1343, 902);
CreateObjectAbove(Mushroom, 1347, 903);
CreateObjectAbove(Balloon, 491, 383);
@ -552,5 +496,78 @@ func InitializeObjects()
CreateObject(Firestone, 2161, 942);
CreateObject(Firestone, 2073, 861);
CreateObject(Firestone, 2064, 851);
var StoneDoor001 = CreateObject(StoneDoor, 940, 1132);
StoneDoor001->SetComDir(COMD_Down);
StoneDoor001->MakeInvincible();
var StoneDoor002 = CreateObject(StoneDoor, 1084, 1132);
StoneDoor002->SetComDir(COMD_Down);
StoneDoor002->MakeInvincible();
var StoneDoor003 = CreateObject(StoneDoor, 564, 436);
StoneDoor003->SetComDir(COMD_Down);
StoneDoor003->MakeInvincible();
var StoneDoor004 = CreateObject(StoneDoor, 843, 716);
StoneDoor004->SetComDir(COMD_Down);
StoneDoor004->MakeInvincible();
var StoneDoor005 = CreateObject(StoneDoor, 1058, 700);
StoneDoor005->SetComDir(COMD_Down);
StoneDoor005->MakeInvincible();
var StoneDoor006 = CreateObject(StoneDoor, 1092, 1028);
StoneDoor006->SetComDir(COMD_Down);
StoneDoor006->MakeInvincible();
var StoneDoor007 = CreateObject(StoneDoor, 1892, 932);
StoneDoor007->SetComDir(COMD_Down);
StoneDoor007->MakeInvincible();
var StoneDoor008 = CreateObject(StoneDoor, 813, 716);
StoneDoor008->SetComDir(COMD_Down);
StoneDoor008->MakeInvincible();
g_last_stone_door = CreateObject(StoneDoor, 781, 716);
g_last_stone_door->SetComDir(COMD_Down);
g_last_stone_door->SetClrModulation(0xffa0a0a0);
g_last_stone_door.StaticSaveVar = "g_last_stone_door";
var StoneDoor009 = CreateObject(StoneDoor, 692, 748);
StoneDoor009->SetComDir(COMD_Down);
StoneDoor009->MakeInvincible();
var StoneDoor010 = CreateObject(StoneDoor, 684, 332);
StoneDoor010->SetComDir(COMD_Down);
StoneDoor010->MakeInvincible();
var SpinWheel001 = CreateObjectAbove(SpinWheel, 589, 457);
SpinWheel001->SetMeshMaterial("SpinWheelGearRed", 0);
SpinWheel001->SetStoneDoor(StoneDoor001);
var SpinWheel002 = CreateObjectAbove(SpinWheel, 611, 456);
SpinWheel002->SetMeshMaterial("SpinWheelGearBlue", 0);
SpinWheel002->SetStoneDoor(StoneDoor002);
var SpinWheel003 = CreateObjectAbove(SpinWheel, 619, 410);
SpinWheel003->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel003->SetStoneDoor(StoneDoor003);
var SpinWheel004 = CreateObject(SpinWheel, 1223, 1545);
SpinWheel004->SetStoneDoor(StoneDoor005);
var SpinWheel005 = CreateObjectAbove(SpinWheel, 1117, 1048);
SpinWheel005->SetStoneDoor(StoneDoor006);
var SpinWheel006 = CreateObjectAbove(SpinWheel, 2761, 690);
SpinWheel006->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel006->SetStoneDoor(StoneDoor008);
var SpinWheel007 = CreateObjectAbove(SpinWheel, 1850, 1463);
SpinWheel007->SetMeshMaterial("SpinWheelGearRed", 0);
SpinWheel007->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel007->SetStoneDoor(StoneDoor004);
var SpinWheel008 = CreateObjectAbove(SpinWheel, 2793, 1521);
SpinWheel008->SetMeshMaterial("SpinWheelGearRed", 0);
SpinWheel008->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel008->SetStoneDoor(StoneDoor007);
var SpinWheel009 = CreateObjectAbove(SpinWheel, 830, 735);
SpinWheel009->SetStoneDoor(StoneDoor009);
var SpinWheel010 = CreateObjectAbove(SpinWheel, 703, 352);
SpinWheel010->SetMeshMaterial("SpinWheelBaseAlt", 1);
SpinWheel010->SetStoneDoor(StoneDoor010);
var Column002 = CreateObject(Column, 779, 488);
Column002->SetR(180);
Column002->SetClrModulation(0xffffd0d0);
Column002->SetMeshMaterial("AncientColumn", 0);
var Column003 = CreateObject(Column, 1419, 217);
Column003->SetMeshMaterial("AncientColumn", 0);
CreateObject(Column, 1386, 616);
return true;
}

View File

@ -21,13 +21,13 @@ func Intro_Init(object flagpole)
// Pyrit the pilot
this.pilot = npc_pyrit = CreateObjectAbove(Clonk, 100, 100, NO_OWNER);
this.pilot->MakeInvincible();
this.pilot->SetSkin(2);
this.pilot->Enter(this.plane);
this.pilot->SetAction("Walk");
this.pilot->SetName("Pyrit");
this.pilot->SetColor(0xff0000);
this.pilot->SetDir(DIR_Left);
this.pilot->SetObjectLayer(this.pilot);
this.pilot->SetAlternativeSkin("MaleBrownHair");
// Pyit has a red hat!
this.pilot->AttachMesh(Hat, "skeleton_head", "main", Trans_Translate(5500, 0, 0));

View File

@ -30,19 +30,19 @@
protected func Construction()
{
_inherited(...);
SetAction("Walk");
SetDir(Random(2));
// Broadcast for rules
GameCallEx("OnClonkCreation", this);
SetSkin(0);
AddEffect("IntTurn", this, 1, 1, this);
AddEffect("IntEyes", this, 1, 35+Random(4), this);
AttachBackpack();
iHandMesh = [0,0];
SetSkin(0);
SetAction("Walk");
SetDir(Random(2));
// Broadcast for rules
GameCallEx("OnClonkCreation", this);
}
/* When adding to the crew of a player */
@ -455,28 +455,36 @@ static const CARRY_Spear = 6;
static const CARRY_Musket = 7;
static const CARRY_Grappler = 8;
func HasHandAction(sec, just_wear)
func HasHandAction(sec, just_wear, bool force_landscape_letgo)
{
// Check if the clonk is currently able to use hands
// sec: Needs both hands (e.g. CarryHeavy?)
// just_wear: ???
// force_landscape_letgo: Also allow from actions where hands are currently grabbing the landscape (scale, hangle)
if(sec && fBothHanded)
return false;
if(just_wear)
{
if( HasActionProcedure() && !fHandAction ) // For wear purpose fHandAction==-1 also blocks
if( HasActionProcedure(force_landscape_letgo) && !fHandAction ) // For wear purpose fHandAction==-1 also blocks
return true;
}
else
{
if( HasActionProcedure() && (!fHandAction || fHandAction == -1) )
if( HasActionProcedure(force_landscape_letgo) && (!fHandAction || fHandAction == -1) )
return true;
}
return false;
}
func HasActionProcedure()
func HasActionProcedure(bool force_landscape_letgo)
{
// Check if the clonk is currently in an action where he could use his hands
// if force_landscape_letgo is true, also allow during scale/hangle assuming the clonk will let go
var action = GetAction();
if (action == "Walk" || action == "Jump" || action == "WallJump" || action == "Kneel" || action == "Ride" || action == "BridgeStand")
return true;
if (force_landscape_letgo) if (action == "Scale" || action == "Hangle")
return true;
return false;
}
@ -653,7 +661,12 @@ func SetSkin(int new_skin)
RemoveBackpack(); //add a backpack
AttachBackpack();
SetAction("Jump"); //refreshes animation
//refreshes animation (whatever that means?)
// Go back to original action afterwards and hope
// that noone calls SetSkin during more compex activities
var prev_action = GetAction();
SetAction("Jump");
SetAction(prev_action);
return skin;
}

View File

@ -220,8 +220,9 @@ private func Construction()
text =
{
Target = this,
Top = "-0.45em",
Style = GUI_TextHCenter,
Top = "-0.5em",
Bottom = "0.5em",
Style = GUI_TextHCenter | GUI_TextVCenter,
Text = "",
Priority = 9
}
@ -231,7 +232,6 @@ private func Construction()
Target = this,
Player = NO_OWNER,
ID = crew_next_id + 3,
Style = GUI_NoCrop,
Top = "100%+0.6em",
Bottom = "100%+0.85em",
BackgroundColor = RGB(40, 40, 40),
@ -826,8 +826,9 @@ private func AddCrewBar(int foreground, int background)
text =
{
Target = this,
Top = "-0.45em",
Style = GUI_TextHCenter,
Top = "-0.5em",
Bottom = "0.5em",
Style = GUI_TextHCenter | GUI_TextVCenter,
Text = "",
Priority = 5
}

View File

@ -159,8 +159,8 @@ func FxIntCheckObjectsTimer(target, effect fx)
var new_objects = FindObjects(Find_AtRect(target->GetX() - 5, target->GetY() - 10, 10, 20), container_restriction, Find_Layer(target->GetObjectLayer()),
// Find all containers and objects with a custom menu.
Find_Or(Find_Func("IsContainer"), Find_Func("HasInteractionMenu")),
// Do not show objects with an extra slot though - even if they are containers. They count as items here.
Find_Not(Find_And(Find_Category(C4D_Object), Find_Func("HasExtraSlots"))),
// Do not show objects with an extra slot though - even if they are containers. They count as items here and can be accessed via the surroundings tab.
Find_Not(Find_And(Find_Property("Collectible"), Find_Func("HasExtraSlot"))),
// Show only objects that the player can see.
Find_Func("CheckVisibility", GetOwner()),
// Normally sorted by z-order. But some objects may have a lower priority.
@ -531,7 +531,7 @@ public func OnMoveAllToClicked(int menu_id)
for (obj in contents)
{
// Sanity, can actually happen if an item merges with others during the transfer etc.
if (!obj) continue;
if (!obj || !target) continue;
var collected = target->Collect(obj, true);
if (collected)
@ -750,7 +750,7 @@ func CreateMainMenu(object obj, int slot)
{
Priority = 1,
Style = GUI_TextVCenter | GUI_TextHCenter,
Bottom = "+0.75em",
Bottom = "+1em",
Text = menu.title,
BackgroundColor = 0xa0000000,
//Decoration = menu.decoration

View File

@ -193,8 +193,7 @@ public func StopDialogue()
// clear remembered positions
dlg_last_opt_sel = nil;
// put on wait for a while; then reenable
SetDialogueStatus(DLG_Status_Wait);
ScheduleCall(this, this.SetDialogueStatus, 30, 1, DLG_Status_Stop);
SetDialogueStatus(DLG_Status_Stop);
return true;
}
@ -245,7 +244,8 @@ public func Interact(object clonk)
if (dlg_status == DLG_Status_Stop)
{
clonk->CloseMenu();
dlg_status = DLG_Status_Active;
dlg_status = DLG_Status_Wait;
ScheduleCall(this, this.SetDialogueStatus, 30, 0, DLG_Status_Active);
// Do a call on a closed dialogue as well.
var fn_closed = Format("~Dlg_%s_Closed", dlg_name);
if (!Call(fn_closed, clonk, dlg_target))

View File

@ -21,7 +21,7 @@ public func GetCarryTransform()
public func RejectUse(object clonk)
{
return !clonk->HasHandAction();
return !clonk->HasHandAction(false, false, true);
}
public func ControlUse(object clonk, int iX, int iY)
@ -154,3 +154,4 @@ local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Collectible = true;
local ForceFreeHands = true;

View File

@ -40,7 +40,8 @@ static const Pickaxe_SwingTime = 40;
public func RejectUse(object clonk)
{
return clonk->GetProcedure() != "WALK";
var proc = clonk->GetProcedure();
return proc != "WALK" && proc != "SCALE";
}
func ControlUseStart(object clonk, int ix, int iy)
@ -52,7 +53,10 @@ func ControlUseStart(object clonk, int ix, int iy)
clonk->SetHandAction(1);
clonk->UpdateAttach();
clonk->PlayAnimation("StrikePickaxe", CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, clonk->GetAnimationLength("StrikePickaxe"), Pickaxe_SwingTime, ANIM_Loop), Anim_Const(1000));
AddEffect("IntPickaxe", clonk, 1, 1, this);
var fx = AddEffect("IntPickaxe", clonk, 1, 1, this);
if (!fx) return false;
fx.x = ix;
fx.y = iy;
return true;
}
@ -66,13 +70,12 @@ func ControlUseHolding(object clonk, int new_x, int new_y)
clonk->PauseUse(this);
return true;
}
x = new_x; y = new_y;
var fx = GetEffect("IntPickaxe", clonk);
if (!fx) return clonk->CancelUse();
fx.x = new_x; fx.y = new_y;
return true;
}
local x, y;
func ControlUseStop(object clonk, int ix, int iy)
{
Reset(clonk);
@ -164,28 +167,16 @@ public func DigOutObject(object obj)
clonk->~DigOutObject(obj);
}
public func FxIntPickaxeStart(object clonk, proplist effect, int temp)
{
if (temp)
return FX_OK;
// Ensure ActMap is local and writable
if (clonk.ActMap == clonk.Prototype.ActMap) clonk.ActMap = new clonk.ActMap {};
// Disable scaling during usage.
effect.actmap_scale = clonk.ActMap.Scale;
clonk.ActMap.Scale = nil;
return FX_OK;
}
public func FxIntPickaxeTimer(object clonk, proplist effect, int time)
{
++swingtime;
if(swingtime >= Pickaxe_SwingTime) // Waits three seconds for animation to run (we could have a clonk swing his pick 3 times)
{
DoSwing(clonk,x,y);
DoSwing(clonk,effect.x,effect.y);
swingtime = 0;
}
var angle = Angle(0,0,x,y);
var angle = Angle(0,0,effect.x,effect.y);
var speed = 50;
var iPosition = swingtime*180/Pickaxe_SwingTime;
@ -196,15 +187,6 @@ public func FxIntPickaxeTimer(object clonk, proplist effect, int time)
clonk->SetYDir(Cos(angle,-speed),100);
}
public func FxIntPickaxeStop(object clonk, proplist effect, int reason, bool temp)
{
if (temp)
return FX_OK;
// Reset the clonk scaling entry in its ActMap.
clonk.ActMap.Scale = effect.actmap_scale;
return FX_OK;
}
protected func ControlUseCancel(object clonk, int ix, int iy)
{
Reset(clonk);
@ -246,3 +228,4 @@ local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local MaxPickDensity = 70; // can't pick granite
local ForceFreeHands = true;

View File

@ -38,6 +38,8 @@ public func ControlUseStart(object clonk, int x, int y)
DigFree(clonk->GetX(), clonk->GetY(), 10);
}
ControlUseHolding(clonk, x, y); // initial coordinate setup
return true;
}

View File

@ -60,7 +60,7 @@ public func GetAnimationSet() { return animation_set; }
public func RejectUse(object clonk)
{
// if the clonk doesn't have an action where he can use it's hands do nothing
return !clonk->HasHandAction();
return !clonk->HasHandAction(false, false, true);
}
public func ControlUseStart(object clonk, int x, int y)
@ -233,4 +233,5 @@ local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Collectible = 1;
local BlastIncinerate = 30;
local ContactIncinerate = 5;
local ContactIncinerate = 5;
local ForceFreeHands = true;

View File

@ -67,7 +67,7 @@ local fAiming;
public func RejectUse(object clonk)
{
return !CanStrikeWithWeapon(clonk) || !clonk->HasHandAction();
return !CanStrikeWithWeapon(clonk) || !clonk->HasHandAction(false, false, true);
}
public func ControlUseStart(object clonk, int x, int y)
@ -99,6 +99,12 @@ public func ControlUseStop(object clonk, ix, iy)
return true;
}
public func ControlUseCancel(object clonk, ix, iy)
{
clonk->StopAim();
return true;
}
// Callback from the clonk, when he actually has stopped aiming
public func FinishedAiming(object clonk, int angle)
{
@ -245,3 +251,4 @@ local Collectible = 1;
local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local ForceFreeHands = true;

View File

@ -65,7 +65,7 @@ protected func HoldingEnabled() { return true; }
public func RejectUse(object clonk)
{
return !clonk->HasHandAction();
return !clonk->HasHandAction(false, false, true);
}
func ControlUseStart(object clonk, int x, int y)
@ -201,4 +201,5 @@ func Definition(def)
local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Collectible = 1;
local Collectible = 1;
local ForceFreeHands = true;

View File

@ -28,7 +28,7 @@ func Fuse(bool explode_on_hit)
AddEffect("FuseBurn", this, 1,1, this);
}
func FxFuseBurnTimer(object bomb, int num, int timer)
func FxFuseBurnTimer(object bomb, proplist effect, int timer)
{
var i = 3;
var x = +Sin(GetR(), i);

View File

@ -16,7 +16,7 @@ public func Launch(int shooter)
AddEffect("HitCheck", this, 1,1, nil, nil);
}
protected func FxFadeTimer(object target, int num, int timer)
protected func FxFadeTimer(object target, proplist effect, int timer)
{
if(timer > FlightTime()) RemoveObject();
}

View File

@ -42,7 +42,7 @@ public func GetCarryTransform() { if(aiming == 1) return Trans_Rotate(180, 0, 0,
public func RejectUse(object clonk)
{
return !clonk->HasHandAction();
return !clonk->HasHandAction(false, false, true);
}
public func ControlUseStart(object clonk, int x, int y)
@ -229,3 +229,4 @@ func Definition(def) {
local Collectible = 1;
local Name = "$Name$";
local Description = "$Description$";
local ForceFreeHands = true;

View File

@ -62,7 +62,7 @@ protected func HoldingEnabled() { return true; }
func RejectUse(object clonk)
{
return !clonk->HasHandAction();
return !clonk->HasHandAction(false, false, true);
}
func ControlUseStart(object clonk, int x, int y)
@ -192,3 +192,4 @@ local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Collectible = 1;
local ForceFreeHands = true;

View File

@ -616,6 +616,9 @@ func StartUseControl(int ctrl, int x, int y, object obj)
// but still catch command
return true;
}
// Disable climb/hangle actions for the duration of this use
if (obj.ForceFreeHands && !GetEffect("IntControlFreeHands", this)) AddEffect("IntControlFreeHands", this, 130, 0, this);
obj->SetController(GetController());
this.control.current_object = obj;
@ -665,6 +668,9 @@ func StartUseDelayedControl(int ctrl, object obj)
return true;
}
// Disable climb/hangle actions for the duration of this use
if (obj.ForceFreeHands && !GetEffect("IntControlFreeHands", this)) AddEffect("IntControlFreeHands", this, 130, 0, this);
this.control.current_object = obj;
this.control.using_type = DetermineUsageType(obj);
this.control.alt = ctrl != CON_UseDelayed;
@ -718,6 +724,8 @@ func StopUseControl(int x, int y, object obj, bool cancel)
if (removal_helper.CommandTarget != this) continue;
break;
} while (true);
RemoveEffect("IntControlFreeHands", this); // make sure we can climb again
this.control.current_object = nil;
this.control.using_type = nil;
@ -918,6 +926,31 @@ func ControlMovement2Script(int ctrl, int x, int y, int strength, bool repeat, b
}
// Effect to free/unfree hands by disabling/enabling scale and hangle procedures
public func FxIntControlFreeHandsStart(object target, proplist fx, int temp)
{
// Process on non-temp as well in case scale/handle effects need to stack
// Stop current action
var proc = GetProcedure();
if (proc == "SCALE" || proc == "HANGLE") SetAction("Walk");
// Make sure ActMap is writable
if (this.ActMap == this.Prototype.ActMap) this.ActMap = new this.ActMap{};
// Kill scale/hangle effects
fx.act_scale = this.ActMap.Scale;
this.ActMap.Scale = nil;
fx.act_hangle = this.ActMap.Hangle;
this.ActMap.Hangle = nil;
return FX_OK;
}
public func FxIntControlFreeHandsStop(object target, proplist fx, int reason, bool temp)
{
// Restore scale/hangle effects (engine will handle re-grabbing walls if needed)
if (fx.act_scale) this.ActMap.Scale = fx.act_scale;
if (fx.act_hangle) this.ActMap.Hangle = fx.act_hangle;
return FX_OK;
}
// returns true if the clonk is able to enter a building (procedurewise)
public func CanEnter()
{
@ -1176,11 +1209,6 @@ public func ControlJump()
return false;
}
func FxIsWallKickStart(object target, int num, bool temp)
{
return 1;
}
// Interaction with clonks is special:
// * The clonk opening the menu should always have higher priority so the clonk is predictably selected on the left side even if standing behind e.g. a crate
// * Other clonks should be behind because interaction with them is rare but having your fellow players stand in front of a building is very common

View File

@ -122,7 +122,14 @@ private func TransferInventory(object from, object to)
while (i--)
if (contents = from->Contents(i))
if (contents->~IsDroppedOnDeath(from))
{
contents->Exit();
}
else
{
// The new clonk doesn't burn. To be consistent, also extinguish contents
contents->Extinguish();
}
return to->GrabContents(from);
}

View File

@ -1,5 +1,16 @@
/*-- Buy at flagpole --*/
public func Construction(...)
{
// This rule enables wealth display because it's annoying to not
// know your wealth in multiplayer when many people buy/sell at
// the flagpole)
// Doesn't remove the display on destruction because there's no
// refcounting for this command.
GUI_Controller->ShowWealth();
return _inherited(...);
}
protected func Activate(int iByPlayer)
{
MessageWindow(GetProperty("Description"), iByPlayer);

View File

@ -3,17 +3,17 @@ uniform sampler2D normalTex;
uniform mat3 normalMatrix;
#ifndef OPENCLONK
varying vec2 texcoord;
in vec2 texcoord;
out vec4 fragColor;
#define slice(x)
#define color gl_FragColor
void main()
{
color = vec4(1.0, 1.0, 1.0, 1.0);
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
#endif
slice(texture+1)
{
color = color * texture2D(basemap, texcoord);
fragColor = fragColor * texture(basemap, texcoord);
#ifndef OPENCLONK
// TODO: Could apply some default lighting here, for viewing the mesh in
@ -27,7 +27,7 @@ slice(normal+1)
// from ObjectShader.glsl. It's probably optimized out,
// but a more elegant solution would be nice. Also maybe
// a function in UtilShader.glsl to reduce code duplication.
vec4 normalPx = texture2D(normalTex, texcoord);
vec4 normalPx = texture(normalTex, texcoord);
vec3 normalPxDir = 2.0 * (normalPx.xyz - vec3(0.5, 0.5, 0.5));
normal = normalize(normalMatrix * normalPxDir);
}

View File

@ -1,9 +1,9 @@
attribute vec3 oc_Position;
attribute vec3 oc_Normal;
attribute vec2 oc_TexCoord;
in vec3 oc_Position;
in vec3 oc_Normal;
in vec2 oc_TexCoord;
varying vec3 vtxNormal;
varying vec2 texcoord;
out vec3 vtxNormal;
out vec2 texcoord;
uniform mat4 projectionMatrix;
uniform mat4 modelviewMatrix;

View File

@ -23,43 +23,36 @@ func SetPaintCol(int idx)
}
// Impact sound
func Hit()
public func Hit()
{
Sound("Hits::GeneralHit?");
}
// Item activation
func ControlUseStart(object clonk, int x, int y)
public func ControlUseStart(object clonk, int x, int y)
{
if (Distance(0,0,x,y) > max_dist) return true;
x += GetX(); y += GetY();
last_x = x; last_y = y;
last_ldx=last_ldy=0;
var r = Random(90), wdt = 2;
var ldx = Sin(r, wdt), ldy = Cos(r, wdt);
DrawMaterialQuad(paint_col, x-ldx,y-ldy, x-ldy,y+ldx, x+ldx,y+ldy, x+ldy,y-ldx, DMQ_Bridge);
SetAction("Spraying");
return true;
return ControlUseHolding(clonk, x, y);
}
func HoldingEnabled() { return true; }
public func HoldingEnabled() { return true; }
func ControlUseHolding(object clonk, int new_x, int new_y)
{
new_x += GetX(); new_y += GetY();
if (new_x==last_x && new_y == last_y) return true;
if (Distance(GetX(),GetY(),new_x,new_y) > max_dist)
public func ControlUseHolding(object clonk, int new_x, int new_y)
{
// Out of reach? Stop spraying.
if (Distance(0,0,new_x,new_y) > max_dist)
{
last_x = new_x;
last_y = new_y;
SetAction("Idle");
return true;
}
if (GetAction() != "Spraying") SetAction("Spraying");
// Work in global coordinates
new_x += GetX(); new_y += GetY();
// (re-)start spraying
if (GetAction() != "Spraying") StartSpraying(clonk, new_x, new_y);
// Spray paint if position moved
if (new_x==last_x && new_y == last_y) return true;
var wdt = 2;
var dx=new_x-last_x, dy=new_y-last_y;
var d = Distance(dx,dy);
@ -83,6 +76,19 @@ public func ControlUseCancel(object clonk, int x, int y)
return true;
}
private func StartSpraying(object clonk, int x, int y)
{
// Go into spray mode and place an initial blob
last_x = x; last_y = y;
last_ldx=last_ldy=0;
var r = Random(90), wdt = 2;
var ldx = Sin(r, wdt), ldy = Cos(r, wdt);
DrawMaterialQuad(paint_col, x-ldx,y-ldy, x-ldy,y+ldx, x+ldx,y+ldy, x+ldy,y-ldx, DMQ_Bridge);
SetAction("Spraying");
return true;
}
local ActMap = {
Spraying = {
Prototype = Action,

View File

@ -2,12 +2,12 @@
Override screenshot functionality
--*/
global func PlayerControl(int plr, int ctrl)
global func PlayerControl(int plr, int ctrl, ...)
{
if (ctrl == CON_TryScreenshot)
{
CustomMessage(Format("$MsgCheater$", GetTaggedPlayerName(plr)));
Sound("Error", true);
Sound("UI::Error", true);
//var crew = GetCursor(plr); - used for cheating
//if (crew) crew->Punch(crew, 50);
return true;

View File

@ -0,0 +1,29 @@
// Makes sure the guide message is hidden when starting a dialogue and shown again when closing.
#appendto Dialogue
public func Interact(object clonk)
{
if (!dlg_interact || !dlg_name)
return inherited(clonk, ...);
var guide = FindObject(Find_ID(TutorialGuide), Find_Owner(clonk->GetOwner()));
if (!guide)
return inherited(clonk, ...);
if (dlg_status == DLG_Status_Stop)
{
if (this.guide_was_shown)
{
this.guide_was_shown = false;
guide->ShowGuide();
}
}
else if (dlg_status != DLG_Status_Remove && dlg_status != DLG_Status_Wait)
{
if (!guide->IsHidden())
{
this.guide_was_shown = true;
guide->HideGuide();
}
}
return inherited(clonk, ...);
}

View File

@ -146,14 +146,10 @@ private func InitializeMenu()
var prop_text =
{
Left = Format("0%%%s", ToEmString(10 * menu_height + text_margin)),
Right = Format("100%%%s", ToEmString(- 5 * menu_height - text_margin)),
// Wrap the text again to scroll only one window instead of also scrolling e.g. the portrait.
text =
{
Target = this,
ID = 2,
Text = nil,
}
// 'Right' will be set on update
Target = this,
ID = 2,
Text = nil,
};
prop_next =
{
@ -222,8 +218,17 @@ private func ShowGuideMenu(int index)
private func UpdateGuideMenu(string guide_message, bool has_next, bool has_prev, bool has_close)
{
// Update the text message entry.
prop_menu.text.text.Text = guide_message;
GuiUpdateText(guide_message, id_menu, prop_menu.text.text.ID, this);
prop_menu.text.Text = guide_message;
// Don't usually leave a margin for the text - just when actually showing buttons.
var is_showing_buttons = has_next || has_close || has_prev;
var text_right_side = "100%";
if (is_showing_buttons)
{
text_right_side = Format("100%-2.9em");
}
GuiUpdate({Right = text_right_side, Text = guide_message}, id_menu, prop_menu.text.ID, this);
// Update the next/close button.
if (has_next || has_close)

View File

@ -62,6 +62,7 @@ private func InitVillageEntrance()
var site = CreateObjectAbove(ConstructionSite, 264, 386);
site.MeshTransformation = Trans_Mul(Trans_Rotate(RandomX(-30, 30), 0, 1, 0), Trans_Rotate(RandomX(-10, 10), 1, 0, 0));
site->Set(Sawmill);
site->MakeUncancellable();
site->CreateContents(Wood, 1);
site->CreateContents(Rock, 1);
@ -202,7 +203,7 @@ private func InitAI()
barrel->SetFilled("Water", 300);
npc_fireman->SetObjectLayer(npc_fireman);
npc_fireman->SetDir(DIR_Left);
npc_fireman->SetDialogue("Fireman", true);
npc_fireman->SetDialogue("Fireman", false);
npc_fireman->SetAlternativeSkin("MaleDarkHair");
// A builder which tells you where to place the flagpole.
@ -211,7 +212,7 @@ private func InitAI()
npc_builder->CreateContents(Hammer);
npc_builder->SetObjectLayer(npc_builder);
npc_builder->SetDir(DIR_Left);
npc_builder->SetDialogue("Builder", true);
npc_builder->SetDialogue("Builder", false);
npc_builder->SetAlternativeSkin("Carpenter");
// A farmer near the grain field.
@ -413,7 +414,9 @@ global func FxTutorialSawmillFinishedTimer(object target, proplist effect)
// Notify lumberjack the sawmill is done.
var dialogue_lumberjack = Dialogue->FindByName("Lumberjack");
if (dialogue_lumberjack)
dialogue_lumberjack->SetDialogueProgress(5, nil, true);
dialogue_lumberjack->SetDialogueProgress(5, nil, false);
Dialogue->FindByName("Fireman")->AddAttention();
Dialogue->FindByName("Builder")->AddAttention();
return FX_Execute_Kill;
}
return FX_OK;
@ -456,6 +459,10 @@ global func FxTutorialPlacedFlagpoleTimer(object target, proplist effect)
guide->ShowGuideMessage();
var new_effect = AddEffect("TutorialTalkedToLumberjack2", nil, 100, 5);
new_effect.plr = effect.plr;
// Notify lumberjack that player talked to other npc's.
var dialogue_lumberjack = Dialogue->FindByName("Lumberjack");
if (dialogue_lumberjack)
dialogue_lumberjack->SetDialogueProgress(6, nil, true);
return FX_Execute_Kill;
}
return FX_OK;

View File

@ -35,27 +35,35 @@ public func Dlg_Lumberjack_4(object clonk)
public func Dlg_Lumberjack_5(object clonk)
{
MessageBox("$DlgLumberjackWellDone$", clonk, dlg_target);
MessageBox("$DlgLumberjackTalkToOthers$", clonk, dlg_target);
StopDialogue();
SetDialogueProgress(5);
return true;
}
public func Dlg_Lumberjack_6(object clonk)
{
MessageBox("$DlgLumberjackFavor$", clonk, clonk);
MessageBox("$DlgLumberjackWellDone$", clonk, dlg_target);
return true;
}
public func Dlg_Lumberjack_7(object clonk)
{
MessageBox("$DlgLumberjackMines$", clonk, dlg_target);
MessageBox("$DlgLumberjackFavor$", clonk, clonk);
return true;
}
public func Dlg_Lumberjack_8(object clonk)
{
MessageBox("$DlgLumberjackMines$", clonk, dlg_target);
return true;
}
public func Dlg_Lumberjack_9(object clonk)
{
MessageBox("$DlgLumberjackLook$", clonk, clonk);
StopDialogue();
SetDialogueProgress(5);
SetDialogueProgress(6);
return true;
}

View File

@ -3,6 +3,7 @@ DlgLumberjackHello=Hallo, ich bin %s, die Holzfällerin des Dorfes.
DlgLumberjackReply=Ich heiße %s. Du schaust traurig aus.
DlgLumberjackSawmill=Ja, das stimmt. Mein Sägewerk wurde zerstört und ich finde keine Steine, um es wieder aufzubauen. Kannst du mir helfen?
DlgLumberjackRock=Ja, ich werde dir Steine finden.
DlgLumberjackTalkToOthers=Danke, vielleicht kannst du jetzt einem der anderen Dorfbewohner helfen.
DlgLumberjackWellDone=Danke vielmals, dass du das Sägewerk aufgebaut hast. Kann ich irgendwas für dich tun?
DlgLumberjackFavor=Tatsächlich kannst du das, ja. Ich brauche Holz, du hast nicht zufällig eine Axt?
DlgLumberjackMines=Nein, leider nicht. Ich habe meine Axt in der Mine fallen gelassen, als ich mich während des Angriffes versteckt habe.

View File

@ -3,6 +3,7 @@ DlgLumberjackHello=Hello I am %s, the lumberjack of this small village.
DlgLumberjackReply=My name is %s, you seem sad.
DlgLumberjackSawmill=Yes, indeed. My sawmill got destroyed and I can't find any rock to rebuild it. Can you help me?
DlgLumberjackRock=Yes, I'll find you some rock.
DlgLumberjackTalkToOthers=Thank you, maybe you can help one of the other villagers now.
DlgLumberjackWellDone=Thanks a lot, for constructing the sawmill. Can I do anything for you?
DlgLumberjackFavor=Actually you can, I need wood, do you happen to have an axe?
DlgLumberjackMines=No, I don't, but I have dropped my axe in the mines when I was hiding there during the attack.

View File

@ -1 +1 @@
Sky.jpg adapted from http://commons.wikimedia.org/wiki/File:Berge_mountains.JPG (Steinsplitter CC BY-SA 3.0)
Sky.jpg adapted from http://commons.wikimedia.org/wiki/File:Berge_mountains.JPG (Steinsplitter CC BY 3.0)

View File

@ -1,5 +1,5 @@
Clonkomotive
A once flourishing train route through Clonkomotive Canyon has been disrupted by heavy rockfall. The rockfall destroyed the necessary bridges to connect the cliffs in between the canyons, thereby disconnecting two villages on the route. You are left with the locomotive in one of the villages and now you need to rebuild the route to the other village. Luckily Nature has settled and the rockfall has become less severe.
Einst florierte die Eisenbahnstrecke durch den Clonkomotive Canyon, doch hat ein schwerer Steinschlag die Strecke zerstört. Die Brücken über die Schluchten wurden sämtlich vernichtet und die beiden Dörfer an den Enden des Canyons sind nun ohne Verbindung. Du findest dich in einem der Dörfer wieder und stehst vor der Aufgabe, die Strecke wieder herzurichten. Glücklicherweise hat sich der Steinschlag etwas beruhigt und ein Aufbau der Strecke ist überhaupt machbar.
Goal: Transport the locomotive to the village on the far right.
Ziel: Bringe die Lokomotive zum Dorf auf der rechten Seite.

View File

@ -1,2 +1,2 @@
Name=HeavyCrumb
Description=Hurts Clonks and buildings!
Name=Felsbrocken
Description=Verletzt Clonks und beschädigt Gebäude.

View File

@ -1,2 +1,2 @@
Name=HeavyCrumb
Description=Hurts Clonks and buildings!
Name=Boulder
Description=Hurts Clonks and damages buildings.

View File

@ -1,4 +1,4 @@
# Intro sequence messages
MsgDriveTrain=We only have to drive this train to the village on the other side of these canyons.
MsgOutOfFuel=Are we out of fuel again?!
MsgBridgesGone=Yes, and it seems all the bridges over the canyons have been destroyed as well.
MsgDriveTrain=Dieser Zug muss zum Dorf auf der anderen Seite des Canyons.
MsgOutOfFuel=Ist der Treibstoff etwa wieder alle?
MsgBridgesGone=Ja, aber die Brücken über die Schluchten sind sowieso zerstört worden.

View File

@ -1282,8 +1282,10 @@ bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtra
{
x = rKeyExtraData.game_x; y = rKeyExtraData.game_y;
}
C4Value vx = (x == C4KeyEventData::KeyPos_None) ? C4VNull : C4VInt(x);
C4Value vy = (y == C4KeyEventData::KeyPos_None) ? C4VNull : C4VInt(y);
// exec control function
C4AulParSet Pars(C4VInt(iPlr), C4VInt(iControl), C4VPropList(C4Id2Def(idControlExtraData)), C4VInt(x), C4VInt(y), C4VInt(rKeyExtraData.iStrength), C4VBool(fRepeated), C4VBool(fUp));
C4AulParSet Pars(C4VInt(iPlr), C4VInt(iControl), C4VPropList(C4Id2Def(idControlExtraData)), vx, vy, C4VInt(rKeyExtraData.iStrength), C4VBool(fRepeated), C4VBool(fUp));
return ::ScriptEngine.GetPropList()->Call(PSF_PlayerControl, &Pars).getBool();
}

View File

@ -552,7 +552,7 @@ void C4ConsoleGUI::DisplayInfoText(InfoTextType type, StdStrBuf& text)
{
case CONSOLE_Cursor:
gtk_statusbar_pop(GTK_STATUSBAR(state->statusBar), 0);
gtk_statusbar_push(GTK_STATUSBAR(state->statusBar), 0, text.getData());
if (text.getData()) gtk_statusbar_push(GTK_STATUSBAR(state->statusBar), 0, text.getData());
return;
case CONSOLE_FrameCounter:
label = state->lblFrame;

View File

@ -135,6 +135,9 @@ public:
void Win32KeepDialogsFloating(HWND hwnd = 0);
virtual bool Win32DialogMessageHandling(MSG *msg);
void UpdateMenuText(HMENU hMenu);
friend INT_PTR CALLBACK PropertyDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam);
friend INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam);
#endif
};

View File

@ -78,6 +78,16 @@ public:
int MenuIndexViewport;
int MenuIndexNet;
int MenuIndexHelp;
int property_dlg_inputarea_height;
int property_dlg_margin;
int property_dlg_okbutton_width;
HWND console_handle;
int console_default_width, console_default_height; // default (and minimum) console window size
int console_margin; // margins between controls and from window borders
int console_wide_margin; // larger margins around some console buttons
int console_button_height; // height of buttons and the three control rows in the console
int console_ok_button_width; // width of OK button to enter script commands (everyone just presses enter anyway...)
int console_status_width; // width of frame counter and time/FPS display status boxes
State(C4ConsoleGUI *console)
{
@ -97,6 +107,17 @@ public:
MenuIndexViewport = 2;
MenuIndexNet = -1;
MenuIndexHelp = 3;
property_dlg_inputarea_height = 0;
property_dlg_margin = 0;
property_dlg_okbutton_width = 0;
console_handle = NULL;
console_default_width = 0;
console_default_height = 0;
console_margin = 0;
console_wide_margin = 0;
console_button_height = 0;
console_ok_button_width = 0;
console_status_width = 0;
}
~State()
@ -155,6 +176,159 @@ public:
hSubMenu = GetSubMenu(hMenu,MenuIndexHelp);
SetMenuItemText(hSubMenu,IDM_HELP_ABOUT,LoadResStr("IDS_MENU_ABOUT"));
}
void PropertyDlgInitLayout()
{
// Find out desired sizes and margins of elements used in property dialogue.
// Just remember initial layout.
// This is easier than getting all values from Windows metrics definitions.
RECT client_rc = { 0,0,252,101 }, button_rc = { 207,182,254,202 };
::GetClientRect(hPropertyDlg, &client_rc);
HWND button = ::GetDlgItem(hPropertyDlg, IDOK);
::GetWindowRect(button, &button_rc);
property_dlg_inputarea_height = button_rc.bottom - button_rc.top;
property_dlg_margin = 1; // hardcoded. The elements are actually placed quite poorly in the .rc, cannot derive from it
property_dlg_okbutton_width = button_rc.right - button_rc.left;
}
void PropertyDlgUpdateSize()
{
// Positions unknown?
if (!property_dlg_inputarea_height) return;
// Reposition all child elements after size of property dialogue has changed
RECT rc = { 0,0,0,0 };
if (!::GetClientRect(hPropertyDlg, &rc)) return;
int y0 = rc.bottom - property_dlg_margin - property_dlg_inputarea_height;
// Output text box
::SetWindowPos(::GetDlgItem(hPropertyDlg, IDC_EDITOUTPUT), NULL,
property_dlg_margin,
property_dlg_margin,
rc.right - 2* property_dlg_margin,
y0 - 2* property_dlg_margin,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Input ComboBox
::SetWindowPos(::GetDlgItem(hPropertyDlg, IDC_COMBOINPUT), NULL,
property_dlg_margin,
y0,
rc.right - property_dlg_okbutton_width - 3*property_dlg_margin,
property_dlg_inputarea_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// OK button
::SetWindowPos(::GetDlgItem(hPropertyDlg, IDOK), NULL,
rc.right - property_dlg_margin - property_dlg_okbutton_width,
y0,
property_dlg_okbutton_width,
property_dlg_inputarea_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
}
void ConsoleInitLayout()
{
// Find out desired sizes and margins of elements used in console dialogue.
// Just remember initial layout.
// This is easier than getting all values from Windows metrics definitions.
RECT console_rc = { 0,0,356,252 };
::GetWindowRect(console_handle, &console_rc);
console_default_width = console_rc.right - console_rc.left;
console_default_height = console_rc.bottom - console_rc.top;
console_margin = 1; // hardcoded margins
console_wide_margin = 3;
RECT button_rc = { 288,180,350,200 };
::GetWindowRect(::GetDlgItem(console_handle, IDOK), &button_rc);
console_button_height = button_rc.bottom - button_rc.top;
console_ok_button_width = button_rc.right - button_rc.left;
RECT status_rc = { 222,205,350,223 };
::GetWindowRect(::GetDlgItem(console_handle, IDC_STATICTIME), &status_rc);
console_status_width = status_rc.right - status_rc.left;
}
void ConsoleUpdateSize()
{
// Positions unknown?
if (!console_default_width) return;
// Reposition all child elements after size of console dialogue has changed
RECT rc = { 0,0,0,0 };
if (!::GetClientRect(console_handle, &rc)) return;
int y0 = rc.bottom - console_margin * 3 - console_button_height * 3;
int y1 = rc.bottom - console_margin * 2 - console_button_height * 2;
int y2 = rc.bottom - console_margin * 1 - console_button_height * 1;
int x0 = rc.right - console_margin - console_button_height;
// Output text box
::SetWindowPos(::GetDlgItem(console_handle, IDC_EDITOUTPUT), NULL,
console_margin,
0,
x0 - console_margin - console_wide_margin,
y0 - console_margin,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Input ComboBox
::SetWindowPos(::GetDlgItem(console_handle, IDC_COMBOINPUT), NULL,
console_margin,
y0,
rc.right - console_ok_button_width - console_margin * 3,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Input OK button
::SetWindowPos(::GetDlgItem(console_handle, IDOK), NULL,
rc.right - console_margin - console_ok_button_width,
y0,
console_ok_button_width,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Frame status bar
::SetWindowPos(::GetDlgItem(console_handle, IDC_STATICFRAME), NULL,
console_margin,
y1,
console_status_width,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Play button
::SetWindowPos(::GetDlgItem(console_handle, IDC_BUTTONPLAY), NULL,
console_margin + console_status_width + console_wide_margin,
y1,
console_button_height,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Halt button
::SetWindowPos(::GetDlgItem(console_handle, IDC_BUTTONHALT), NULL,
console_margin + console_status_width + console_wide_margin * 2 + console_button_height,
y1,
console_button_height,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Time/FPS status bar
::SetWindowPos(::GetDlgItem(console_handle, IDC_STATICTIME), NULL,
rc.right - console_margin - console_status_width,
y1,
console_status_width,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Main status bar
::SetWindowPos(::GetDlgItem(console_handle, IDC_STATICCURSOR), NULL,
console_margin,
y2,
rc.right - 2* console_margin,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
// Tool buttons
::SetWindowPos(::GetDlgItem(console_handle, IDC_BUTTONMODEPLAY), NULL,
x0,
console_margin,
console_button_height,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
::SetWindowPos(::GetDlgItem(console_handle, IDC_BUTTONMODEEDIT), NULL,
x0,
console_margin * 2 + console_button_height,
console_button_height,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
::SetWindowPos(::GetDlgItem(console_handle, IDC_BUTTONMODEDRAW), NULL,
x0,
console_margin * 3 + console_button_height * 2,
console_button_height,
console_button_height,
SWP_NOOWNERZORDER | SWP_NOZORDER);
}
};
void C4ConsoleGUI::UpdateMenuText(HMENU hMenu) { state->UpdateMenuText(*this, hMenu); }
@ -176,7 +350,7 @@ INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPara
return true;
//------------------------------------------------------------------------------------------------------------
case WM_DESTROY:
StoreWindowPosition(hDlg, "Main", Config.GetSubkeyPath("Console"), false);
StoreWindowPosition(hDlg, "Main", Config.GetSubkeyPath("Console"), true);
Application.Quit();
return true;
//------------------------------------------------------------------------------------------------------------
@ -191,7 +365,7 @@ INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPara
//------------------------------------------------------------------------------------------------------------
case WM_INITDIALOG:
Console.Active = true;
SendMessage(hDlg,DM_SETDEFID,(WPARAM)IDOK,(LPARAM)0);
SendMessage(hDlg, DM_SETDEFID, (WPARAM)IDOK, (LPARAM)0);
Console.UpdateMenuText(GetMenu(hDlg));
return true;
//------------------------------------------------------------------------------------------------------------
@ -203,7 +377,7 @@ INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPara
case IDOK:
// IDC_COMBOINPUT to Console.In()
wchar_t buffer[16000];
GetDlgItemTextW(hDlg,IDC_COMBOINPUT,buffer,16000);
GetDlgItemTextW(hDlg, IDC_COMBOINPUT, buffer, 16000);
if (buffer[0])
{
StdStrBuf in_char(buffer);
@ -256,13 +430,13 @@ INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPara
case IDM_VIEWPORT_NEW: Console.ViewportNew(); return true;
}
// New player viewport
if (Inside((int) LOWORD(wParam),IDM_VIEWPORT_NEW1,IDM_VIEWPORT_NEW2))
if (Inside((int)LOWORD(wParam), IDM_VIEWPORT_NEW1, IDM_VIEWPORT_NEW2))
{
::Viewports.CreateViewport(LOWORD(wParam)-IDM_VIEWPORT_NEW1);
::Viewports.CreateViewport(LOWORD(wParam) - IDM_VIEWPORT_NEW1);
return true;
}
// Remove player
if (Inside((int) LOWORD(wParam),IDM_PLAYER_QUIT1,IDM_PLAYER_QUIT2))
if (Inside((int)LOWORD(wParam), IDM_PLAYER_QUIT1, IDM_PLAYER_QUIT2))
{
C4Player *plr = ::Players.Get(LOWORD(wParam) - IDM_PLAYER_QUIT1);
if (!plr) return true;
@ -270,10 +444,10 @@ INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPara
return true;
}
// Remove client
if (Inside((int) LOWORD(wParam),IDM_NET_CLIENT1,IDM_NET_CLIENT2))
if (Inside((int)LOWORD(wParam), IDM_NET_CLIENT1, IDM_NET_CLIENT2))
{
if (!::Control.isCtrlHost()) return false;
Game.Clients.CtrlRemove(Game.Clients.getClientByID(LOWORD(wParam)-IDM_NET_CLIENT1), LoadResStr("IDS_MSG_KICKBYMENU"));
Game.Clients.CtrlRemove(Game.Clients.getClientByID(LOWORD(wParam) - IDM_NET_CLIENT1), LoadResStr("IDS_MSG_KICKBYMENU"));
return true;
}
return false;
@ -286,7 +460,7 @@ INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPara
return false;
//------------------------------------------------------------------------------------------------------------
case WM_COPYDATA:
{
{
COPYDATASTRUCT* pcds = reinterpret_cast<COPYDATASTRUCT *>(lParam);
if (pcds->dwData == WM_USER_RELOADFILE)
{
@ -297,12 +471,30 @@ INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPara
Game.ReloadFile(szPath);
}
return false;
}
//------------------------------------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------------------------------------
case WM_INPUTLANGCHANGE:
::Application.OnKeyboardLayoutChanged();
break;
//------------------------------------------------------------------------------------------------------------
// Resizing
case WM_GETMINMAXINFO:
// Window may not become smaller than initial size
if (Console.state && Console.state->console_default_width)
{
MINMAXINFO *info = reinterpret_cast<MINMAXINFO *>(lParam);
info->ptMinTrackSize.x = Console.state->console_default_width;
info->ptMinTrackSize.y = Console.state->console_default_height;
}
return 0;
case WM_SIZING: Console.state->ConsoleUpdateSize(); break;
case WM_WINDOWPOSCHANGED:
{
const WINDOWPOS *data = reinterpret_cast<const WINDOWPOS *>(lParam);
if (data && !(data->flags & SWP_NOSIZE)) Console.state->ConsoleUpdateSize();
break;
}
}
return false;
}
@ -399,7 +591,7 @@ INT_PTR CALLBACK ToolsDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
break;
//----------------------------------------------------------------------------------------------
case WM_DESTROY:
StoreWindowPosition(hDlg, "Property", Config.GetSubkeyPath("Console"), false);
StoreWindowPosition(hDlg, "Tools", Config.GetSubkeyPath("Console"), false);
break;
//----------------------------------------------------------------------------------------------
case WM_INITDIALOG:
@ -511,13 +703,22 @@ INT_PTR CALLBACK PropertyDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPar
break;
//------------------------------------------------------------------------------------------------
case WM_DESTROY:
StoreWindowPosition(hDlg, "Property", Config.GetSubkeyPath("Console"), false);
StoreWindowPosition(hDlg, "Property", Config.GetSubkeyPath("Console"), true);
break;
//------------------------------------------------------------------------------------------------
case WM_INITDIALOG:
SendMessage(hDlg,DM_SETDEFID,(WPARAM)IDOK,(LPARAM)0);
return true;
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
// Callbacks during/after window resizing
case WM_SIZING: Console.state->PropertyDlgUpdateSize(); break;
case WM_WINDOWPOSCHANGED:
{
const WINDOWPOS *data = reinterpret_cast<const WINDOWPOS *>(lParam);
if (data && !(data->flags & SWP_NOSIZE)) Console.state->PropertyDlgUpdateSize();
break;
}
//------------------------------------------------------------------------------------------------
case WM_COMMAND:
// Evaluate command
switch (LOWORD(wParam))
@ -601,6 +802,9 @@ C4Window* C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
LocalFree(lpMsgBuf);
return NULL;
}
// Remember metrics
state->console_handle = hWindow;
state->ConsoleInitLayout();
// Restore window position
RestoreWindowPosition(hWindow, "Main", Config.GetSubkeyPath("Console"));
// Set icon
@ -839,6 +1043,8 @@ bool C4ConsoleGUI::PropertyDlgOpen()
PropertyDlgProc);
if (!hDialog) return false;
state->hPropertyDlg = hDialog;
// Remember initial layout
state->PropertyDlgInitLayout();
// Set text
SetWindowTextW(hDialog,LoadResStrW("IDS_DLG_PROPERTIES"));
// Enable controls
@ -957,7 +1163,7 @@ bool C4ConsoleGUI::ToolsDlgOpen(C4ToolsDlg *dlg)
dlg->state->pPreviewWindow = new C4ConsoleGUIPreviewWindow(GetDlgItem(dlg->state->hDialog, IDC_PREVIEW));
}
// Show window
RestoreWindowPosition(dlg->state->hDialog, "Property", Config.GetSubkeyPath("Console"));
RestoreWindowPosition(dlg->state->hDialog, "Tools", Config.GetSubkeyPath("Console"));
SetWindowPos(dlg->state->hDialog,Console.hWindow,0,0,0,0,SWP_NOSIZE | SWP_NOMOVE);
ShowWindow(dlg->state->hDialog,SW_SHOWNOACTIVATE);
return true;

View File

@ -185,9 +185,6 @@ bool C4Application::DoInit(int argc, char * argv[])
pWindow->SetSize(Config.Graphics.WindowX, Config.Graphics.WindowY);
}
// after initializing graphics, the particle system can check for compatibility
::Particles.DoInit();
// Initialize gamepad
if (!pGamePadControl && Config.General.GamepadEnabled)
pGamePadControl = new C4GamePadControl();

View File

@ -378,7 +378,7 @@ void C4Viewport::Execute()
bool draw_game = true;
if (Player == NO_OWNER)
if (!::Application.isEditor && !::Game.DebugMode)
if (!::Network.isEnabled() || !::Network.Clients.GetLocal() || ::Network.Clients.GetLocal()->isObserver())
if (!::Network.isEnabled() || !::Network.Clients.GetLocal() || !::Network.Clients.GetLocal()->isObserver())
if (::Game.PlayerInfos.GetJoinIssuedPlayerCount() > 0) // free scrolling allowed if the scenario was started explicitely without players to inspect the landscape
draw_game = false;
// Draw

View File

@ -103,7 +103,7 @@ namespace
#undef USERPARAM_CONST
CStdGL::CStdGL():
pMainCtx(0), CurrentVBO(0)
pMainCtx(0), CurrentVBO(0), NextVAOID(VAOIDs.end())
{
GenericVBOs[0] = 0;
Default();
@ -220,6 +220,11 @@ bool CStdGL::PrepareSpriteShader(C4Shader& shader, const char* name, int ssc, C4
uniformNames[C4SSU_AmbientTex] = "ambientTex";
uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
uniformNames[C4SSU_MaterialAmbient] = "materialAmbient"; // unused
uniformNames[C4SSU_MaterialDiffuse] = "materialDiffuse"; // unused
uniformNames[C4SSU_MaterialSpecular] = "materialSpecular"; // unused
uniformNames[C4SSU_MaterialEmission] = "materialEmission"; // unused
uniformNames[C4SSU_MaterialShininess] = "materialShininess"; // unused
uniformNames[C4SSU_Bones] = "bones"; // unused
uniformNames[C4SSU_CullMode] = "cullMode"; // unused
uniformNames[C4SSU_Count] = NULL;
@ -376,16 +381,6 @@ bool CStdGL::CreatePrimarySurfaces(unsigned int, unsigned int, int iColorDepth,
{
// store options
bool ok = RestoreDeviceObjects();
// - AMD GPUs have supported OpenGL 2.1 since 2007
// - nVidia GPUs have supported OpenGL 2.1 since 2005
// - Intel integrated GPUs have supported OpenGL 2.1 since Clarkdale (maybe earlier).
// And we've already been using features from OpenGL 2.1. Nobody has complained yet.
// So checking for 2.1 support should be fine.
if (!GLEW_VERSION_2_1)
{
return Error(" gl: OpenGL Version 2.1 or higher required. A better graphics driver will probably help.");
}
return ok;
}
@ -646,21 +641,31 @@ void CStdGL::PerformMultiBlt(C4Surface* sfcTarget, DrawOperation op, const C4Blt
glBufferSubData(GL_ARRAY_BUFFER, 0, n_vertices * sizeof(C4BltVertex), vertices);
}
const GLuint position = shader_call->GetAttribute(C4SSA_Position);
const GLuint color = shader_call->GetAttribute(C4SSA_Color);
const GLuint texcoord = has_tex ? shader_call->GetAttribute(C4SSA_TexCoord) : 0;
glEnableVertexAttribArray(position);
glEnableVertexAttribArray(color);
if(has_tex)
// Choose the VAO that corresponds to the chosen VBO. Also, use one
// that supplies texture coordinates if we have texturing enabled.
GLuint vao;
const unsigned int vao_index = vbo_index + (has_tex ? N_GENERIC_VBOS : 0);
const unsigned int vao_id = GenericVAOs[vao_index];
const bool has_vao = GetVAO(vao_id, vao);
glBindVertexArray(vao);
if (!has_vao)
{
glEnableVertexAttribArray(texcoord);
glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, sizeof(C4BltVertex), reinterpret_cast<const uint8_t*>(offsetof(C4BltVertex, tx)));
}
// Initialize VAO for this context
const GLuint position = shader_call->GetAttribute(C4SSA_Position);
const GLuint color = shader_call->GetAttribute(C4SSA_Color);
const GLuint texcoord = has_tex ? shader_call->GetAttribute(C4SSA_TexCoord) : 0;
glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, sizeof(C4BltVertex), reinterpret_cast<const uint8_t*>(offsetof(C4BltVertex, ftx)));
glVertexAttribPointer(color, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(C4BltVertex), reinterpret_cast<const uint8_t*>(offsetof(C4BltVertex, color)));
glEnableVertexAttribArray(position);
glEnableVertexAttribArray(color);
if (has_tex)
glEnableVertexAttribArray(texcoord);
glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, sizeof(C4BltVertex), reinterpret_cast<const uint8_t*>(offsetof(C4BltVertex, ftx)));
glVertexAttribPointer(color, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(C4BltVertex), reinterpret_cast<const uint8_t*>(offsetof(C4BltVertex, color)));
if (has_tex)
glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, sizeof(C4BltVertex), reinterpret_cast<const uint8_t*>(offsetof(C4BltVertex, tx)));
}
switch (op)
{
@ -675,10 +680,8 @@ void CStdGL::PerformMultiBlt(C4Surface* sfcTarget, DrawOperation op, const C4Blt
break;
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
if(has_tex) glDisableVertexAttribArray(texcoord);
glDisableVertexAttribArray(position);
glDisableVertexAttribArray(color);
}
C4Shader* CStdGL::GetSpriteShader(bool haveBase, bool haveOverlay, bool haveNormal)
@ -806,6 +809,8 @@ bool CStdGL::RestoreDeviceObjects()
GenericVBOSizes[i] = GENERIC_VBO_SIZE;
glBindBuffer(GL_ARRAY_BUFFER, GenericVBOs[i]);
glBufferData(GL_ARRAY_BUFFER, GenericVBOSizes[i] * sizeof(C4BltVertex), NULL, GL_STREAM_DRAW);
GenericVAOs[i] = GenVAOID();
GenericVAOs[i + N_GENERIC_VBOS] = GenVAOID();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
@ -832,9 +837,13 @@ bool CStdGL::InvalidateDeviceObjects()
// invalidate generic VBOs
if (GenericVBOs[0] != 0)
{
glDeleteBuffers(N_GENERIC_VBOS, GenericVBOs);
GenericVBOs[0] = 0;
CurrentVBO = 0;
GenericVBOs[0] = 0;
CurrentVBO = 0;
for (unsigned int i = 0; i < N_GENERIC_VBOS * 2; ++i)
FreeVAOID(GenericVAOs[i]);
}
// invalidate shaders
@ -932,4 +941,120 @@ void CStdGL::Default()
Workarounds.LowMaxVertexUniformCount = false;
}
unsigned int CStdGL::GenVAOID()
{
// Generate a new VAO ID. Make them sequential so that the actual
// VAOs in the context can be simply maintained with a lookup table.
unsigned int id;
if (NextVAOID == VAOIDs.begin())
{
// Insert at the beginning
id = 1;
}
else
{
// Insert at the end, or somewhere in the middle
std::set<unsigned int>::iterator iter = NextVAOID;
--iter;
id = *iter + 1;
}
// Actually insert the ID
#ifdef NDEBUG
std::set<unsigned int>::iterator inserted_iter = VAOIDs.insert(NextVAOID, id);
#else
std::pair<std::set<unsigned int>::iterator, bool> inserted = VAOIDs.insert(id);
assert(inserted.second == true);
std::set<unsigned int>::iterator inserted_iter = inserted.first;
#endif
// Update next VAO ID: increment iterator until we find a gap
// in the sequence.
NextVAOID = inserted_iter;
unsigned int prev_id = id;
++NextVAOID;
while(NextVAOID != VAOIDs.end() && prev_id + 1 == *NextVAOID)
{
prev_id = *NextVAOID;
++NextVAOID;
}
return id;
}
void CStdGL::FreeVAOID(unsigned int vaoid)
{
std::set<unsigned int>::iterator iter = VAOIDs.find(vaoid);
assert(iter != VAOIDs.end());
// Delete this VAO in the current context
if (pCurrCtx)
{
if (vaoid < pCurrCtx->hVAOs.size() && pCurrCtx->hVAOs[vaoid] != 0)
{
glDeleteVertexArrays(1, &pCurrCtx->hVAOs[vaoid]);
pCurrCtx->hVAOs[vaoid] = 0;
}
}
// For all other contexts, mark it to be deleted as soon as we select
// that context. Otherwise we would need to do a lot of context
// switching at this point.
for (std::list<CStdGLCtx*>::iterator iter = CStdGLCtx::contexts.begin(); iter != CStdGLCtx::contexts.end(); ++iter)
{
CStdGLCtx* ctx = *iter;
if (ctx != pCurrCtx && vaoid < ctx->hVAOs.size() && ctx->hVAOs[vaoid] != 0)
if (std::find(ctx->VAOsToBeDeleted.begin(), ctx->VAOsToBeDeleted.end(), vaoid) == ctx->VAOsToBeDeleted.end())
ctx->VAOsToBeDeleted.push_back(vaoid);
}
// Delete the VAO ID from our list of VAO IDs in use
// If the Next VAO ID is 1, then no matter what we delete we don't need
// to update anything. If it is not at the beginning, then move it to the
// gap we just created if it was at a higher place, to make sure we keep
// the numbers as sequential as possible.
unsigned int nextVaoID = 1;
if (NextVAOID != VAOIDs.begin())
{
std::set<unsigned int>::iterator next_vao_iter = NextVAOID;
--next_vao_iter;
nextVaoID = *next_vao_iter + 1;
}
assert(vaoid != nextVaoID);
if (vaoid < nextVaoID || iter == NextVAOID)
NextVAOID = VAOIDs.erase(iter);
else
VAOIDs.erase(iter);
}
bool CStdGL::GetVAO(unsigned int vaoid, GLuint& vao)
{
assert(pCurrCtx != NULL);
if (vaoid >= pCurrCtx->hVAOs.size())
{
// Resize the VAO array so that all generated VAO IDs fit
// in it, and not only the one requested in this call.
// We hope to get away with fewer reallocations this way.
assert(VAOIDs.find(vaoid) != VAOIDs.end());
std::set<unsigned int>::iterator iter = VAOIDs.end();
--iter;
pCurrCtx->hVAOs.resize(*iter + 1);
}
if (pCurrCtx->hVAOs[vaoid] == 0)
{
glGenVertexArrays(1, &pCurrCtx->hVAOs[vaoid]);
vao = pCurrCtx->hVAOs[vaoid];
return false;
}
vao = pCurrCtx->hVAOs[vaoid];
return true;
}
#endif // USE_CONSOLE

View File

@ -68,6 +68,12 @@ enum C4SS_Uniforms
C4SSU_AmbientTransform, // C4SSC_LIGHT
C4SSU_AmbientBrightness, // C4SSC_LIGHT
C4SSU_MaterialAmbient, // for meshes
C4SSU_MaterialDiffuse, // for meshes
C4SSU_MaterialSpecular, // for meshes
C4SSU_MaterialEmission, // for meshes
C4SSU_MaterialShininess, // for meshes
C4SSU_Bones, // for meshes
C4SSU_CullMode, // for meshes
@ -99,7 +105,7 @@ class CStdGLCtx
{
public:
CStdGLCtx(); // ctor
~CStdGLCtx() { Clear(); }; // dtor
~CStdGLCtx() { Clear(); } // dtor
void Clear(); // clear objects
@ -130,6 +136,15 @@ protected:
/*GLXContext*/void * ctx;
#endif
// Global list of all OpenGL contexts in use
static std::list<CStdGLCtx*> contexts;
std::list<CStdGLCtx*>::iterator this_context;
// VAOs available on this context
std::vector<GLuint> hVAOs;
// VAOs to be deleted the next time this context is being made current.
std::vector<unsigned int> VAOsToBeDeleted;
friend class CStdGL;
friend class C4Surface;
};
@ -188,8 +203,31 @@ protected:
GLuint GenericVBOs[N_GENERIC_VBOS];
unsigned int GenericVBOSizes[N_GENERIC_VBOS];
unsigned int CurrentVBO;
// We need twice as much VAOs, since the sprite rendering routines work
// both with and without textures (in which case we either need texture
// coordinates or not).
unsigned int GenericVAOs[N_GENERIC_VBOS * 2];
// VAO IDs currently in use.
std::set<unsigned int> VAOIDs;
std::set<unsigned int>::iterator NextVAOID;
public:
// Create a new (unique) VAO ID. A VAO ID is a number that identifies
// a certain VAO across all OpenGL contexts. This indirection is needed
// because, unlike most other GL state, VAOs are not shared between
// OpenGL contexts.
unsigned int GenVAOID();
// Free the given VAO ID, i.e. it can be re-used for new VAOs. This causes
// the VAO associated with this ID to be deleted in all OpenGL contexts.
void FreeVAOID(unsigned int vaoid);
// Return a VAO with the given vao ID in the "vao" output variable.
// If the function returns false, the VAO was newly created, otherwise
// an existing VAO is returned.
bool GetVAO(unsigned int vaoid, GLuint& vao);
// General
void Clear();
void Default();

View File

@ -24,6 +24,11 @@
#ifndef USE_CONSOLE
static const int REQUESTED_GL_CTX_MAJOR = 3;
static const int REQUESTED_GL_CTX_MINOR = 2;
std::list<CStdGLCtx*> CStdGLCtx::contexts;
void CStdGLCtx::SelectCommon()
{
pGL->pCurrCtx = this;
@ -32,6 +37,22 @@ void CStdGLCtx::SelectCommon()
glDepthFunc(GL_LESS);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
// Delete pending VAOs
std::vector<GLuint> toBeDeleted;
if (!VAOsToBeDeleted.empty())
{
for (unsigned int i = 0; i < VAOsToBeDeleted.size(); ++i)
{
if (VAOsToBeDeleted[i] < hVAOs.size() && hVAOs[VAOsToBeDeleted[i]] != 0)
{
toBeDeleted.push_back(hVAOs[VAOsToBeDeleted[i]]);
hVAOs[VAOsToBeDeleted[i]] = 0;
}
}
glDeleteVertexArrays(toBeDeleted.size(), &toBeDeleted[0]);
VAOsToBeDeleted.clear();
}
}
void CStdGLCtx::Reinitialize()
@ -214,6 +235,7 @@ bool CStdGLCtx::InitGlew(HINSTANCE hInst)
else
{
// init extensions
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if(err != GLEW_OK)
{
@ -239,7 +261,7 @@ bool CStdGLCtx::InitGlew(HINSTANCE hInst)
return glewInitialized;
}
CStdGLCtx::CStdGLCtx(): pWindow(0), hDC(0) { }
CStdGLCtx::CStdGLCtx(): pWindow(0), hDC(0), this_context(contexts.end()) { }
void CStdGLCtx::Clear()
{
@ -250,6 +272,12 @@ void CStdGLCtx::Clear()
hDC=0;
}
pWindow = 0; hWindow = NULL;
if (this_context != contexts.end())
{
contexts.erase(this_context);
this_context = contexts.end();
}
}
bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *pApp, HWND hWindow)
@ -305,17 +333,23 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *pApp, HWND hWindow)
else
{
// create context
if (Config.Graphics.DebugOpenGL && wglCreateContextAttribsARB)
if (wglCreateContextAttribsARB)
{
const int attribs[] = {
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
WGL_CONTEXT_FLAGS_ARB, Config.Graphics.DebugOpenGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
WGL_CONTEXT_MAJOR_VERSION_ARB, REQUESTED_GL_CTX_MAJOR,
WGL_CONTEXT_MINOR_VERSION_ARB, REQUESTED_GL_CTX_MINOR,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0
};
DebugLog(" gl: Creating debug context.");
if (Config.Graphics.DebugOpenGL)
DebugLog(" gl: Creating debug context.");
hrc = wglCreateContextAttribsARB(hDC, 0, attribs);
}
else
{
DebugLog(" gl: wglCreateContextAttribsARB not available; creating default context.");
hrc = wglCreateContext(hDC);
}
@ -335,6 +369,7 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *pApp, HWND hWindow)
// After selecting the new context, we have to reinitialize GLEW to
// update its function pointers - the driver may elect to expose
// different extensions depending on the context attributes
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (err != GLEW_OK)
{
@ -342,6 +377,8 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *pApp, HWND hWindow)
pGL->Error(reinterpret_cast<const char*>(glewGetErrorString(err)));
return false;
}
this_context = contexts.insert(contexts.end(), this);
return true;
}
@ -417,7 +454,7 @@ void InitGLXPointers()
}
}
CStdGLCtx::CStdGLCtx(): pWindow(0), ctx(0) { }
CStdGLCtx::CStdGLCtx(): pWindow(0), ctx(0), this_context(contexts.end()) { }
void CStdGLCtx::Clear()
{
@ -429,6 +466,12 @@ void CStdGLCtx::Clear()
ctx = 0;
}
pWindow = 0;
if (this_context != contexts.end())
{
contexts.erase(this_context);
this_context = contexts.end();
}
}
bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
@ -448,6 +491,7 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
GLXContext dummy_ctx = glXCreateContext(dpy, vis_info, 0, True);
XFree(vis_info);
glXMakeCurrent(dpy, pWindow->renderwnd, dummy_ctx);
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (err != GLEW_OK)
{
@ -456,7 +500,10 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
// Create Context with sharing (if this is the main context, our ctx will be 0, so no sharing)
const int attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, REQUESTED_GL_CTX_MAJOR,
GLX_CONTEXT_MINOR_VERSION_ARB, REQUESTED_GL_CTX_MINOR,
GLX_CONTEXT_FLAGS_ARB, (Config.Graphics.DebugOpenGL ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
None
};
GLXContext share_context = (pGL->pMainCtx != this) ? static_cast<GLXContext>(pGL->pMainCtx->ctx) : 0;
@ -479,12 +526,15 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
if (!ctx) return pGL->Error(" gl: Unable to create context");
if (!Select(true)) return pGL->Error(" gl: Unable to select context");
// init extensions
glewExperimental = GL_TRUE;
err = glewInit();
if (GLEW_OK != err)
{
// Problem: glewInit failed, something is seriously wrong.
return pGL->Error(reinterpret_cast<const char*>(glewGetErrorString(err)));
}
this_context = contexts.insert(contexts.end(), this);
return true;
}
@ -538,11 +588,17 @@ bool CStdGLCtx::PageFlip()
#elif defined(USE_SDL_MAINLOOP)
CStdGLCtx::CStdGLCtx(): pWindow(0) { }
CStdGLCtx::CStdGLCtx(): pWindow(0), this_context(contexts.end()) { }
void CStdGLCtx::Clear()
{
pWindow = 0;
if (this_context != contexts.end())
{
contexts.erase(this_context);
this_context = contexts.end();
}
}
bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
@ -554,12 +610,15 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
// No luck at all?
if (!Select(true)) return pGL->Error(" gl: Unable to select context");
// init extensions
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (GLEW_OK != err)
{
// Problem: glewInit failed, something is seriously wrong.
return pGL->Error(reinterpret_cast<const char*>(glewGetErrorString(err)));
}
this_context = contexts.insert(contexts.end(), this);
return true;
}

View File

@ -478,6 +478,8 @@ static NSOpenGLContext* MainContext;
+ (NSOpenGLContext*) createContext:(CStdGLCtx*) pMainCtx
{
std::vector<NSOpenGLPixelFormatAttribute> attribs;
attribs.push_back(NSOpenGLPFAOpenGLProfile);
attribs.push_back(NSOpenGLProfileVersion3_2Core);
attribs.push_back(NSOpenGLPFADepthSize);
attribs.push_back(16);
if (!Application.isEditor && Config.Graphics.MultiSampling > 0)
@ -495,10 +497,9 @@ static NSOpenGLContext* MainContext;
}
attribs.push_back(NSOpenGLPFANoRecovery);
//attribs.push_back(NSOpenGLPFADoubleBuffer);
attribs.push_back(NSOpenGLPFAWindow);
//attribs.push_back(NSOpenGLPFAWindow); // cannot create a core profile with this
attribs.push_back(0);
NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
NSOpenGLContext* result = [[NSOpenGLContext alloc] initWithFormat:format shareContext:pMainCtx ? pMainCtx->objectiveCObject<NSOpenGLContext>() : nil];
if (!MainContext)
MainContext = result;
@ -555,13 +556,19 @@ static NSOpenGLContext* MainContext;
#pragma mark CStdGLCtx: Initialization
CStdGLCtx::CStdGLCtx(): pWindow(0) {}
CStdGLCtx::CStdGLCtx(): pWindow(0), this_context(contexts.end()) {}
void CStdGLCtx::Clear()
{
Deselect();
setObjectiveCObject(nil);
pWindow = 0;
if (this_context != contexts.end())
{
contexts.erase(this_context);
this_context = contexts.end();
}
}
void C4Window::EnumerateMultiSamples(std::vector<int>& samples) const
@ -582,6 +589,7 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
// No luck at all?
if (!Select(true)) return pGL->Error(" gl: Unable to select context");
// init extensions
glewExperimental = GL_TRUE; // Init GL 3.0+ function pointers
GLenum err = glewInit();
if (GLEW_OK != err)
{
@ -594,6 +602,8 @@ bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
{
[controller.openGLView setContext:ctx];
}
this_context = contexts.insert(contexts.end(), this);
return true;
}

View File

@ -59,8 +59,8 @@ namespace
////////////////////////////////////////////
StdStrBuf Texture2DToCode(int index, bool hasTextureAnimation)
{
if (hasTextureAnimation) return FormatString("texture2D(oc_Texture%d, (oc_TextureMatrix%d * vec4(texcoord, 0.0, 1.0)).xy)", index, index);
return FormatString("texture2D(oc_Texture%d, texcoord)", index);
if (hasTextureAnimation) return FormatString("texture(oc_Texture%d, (oc_TextureMatrix%d * vec4(texcoord, 0.0, 1.0)).xy)", index, index);
return FormatString("texture(oc_Texture%d, texcoord)", index);
}
StdStrBuf TextureUnitSourceToCode(int index, StdMeshMaterialTextureUnit::BlendOpSourceType source, const float manualColor[3], float manualAlpha, bool hasTextureAnimation)
@ -123,17 +123,17 @@ namespace
case StdMeshMaterialPass::DF_AlwaysFail:
return StdStrBuf("discard;");
case StdMeshMaterialPass::DF_Less:
return FormatString("if (!(color.a < %f)) discard;", pass.AlphaRejectionValue);
return FormatString("if (!(fragColor.a < %f)) discard;", pass.AlphaRejectionValue);
case StdMeshMaterialPass::DF_LessEqual:
return FormatString("if (!(color.a <= %f)) discard;", pass.AlphaRejectionValue);
return FormatString("if (!(fragColor.a <= %f)) discard;", pass.AlphaRejectionValue);
case StdMeshMaterialPass::DF_Equal:
return FormatString("if (!(color.a == %f)) discard;", pass.AlphaRejectionValue);
return FormatString("if (!(fragColor.a == %f)) discard;", pass.AlphaRejectionValue);
case StdMeshMaterialPass::DF_NotEqual:
return FormatString("if (!(color.a != %f)) discard;", pass.AlphaRejectionValue);
return FormatString("if (!(fragColor.a != %f)) discard;", pass.AlphaRejectionValue);
case StdMeshMaterialPass::DF_Greater:
return FormatString("if (!(color.a > %f)) discard;", pass.AlphaRejectionValue);
return FormatString("if (!(fragColor.a > %f)) discard;", pass.AlphaRejectionValue);
case StdMeshMaterialPass::DF_GreaterEqual:
return FormatString("if (!(color.a >= %f)) discard;", pass.AlphaRejectionValue);
return FormatString("if (!(fragColor.a >= %f)) discard;", pass.AlphaRejectionValue);
default:
assert(false);
return StdStrBuf();
@ -238,10 +238,10 @@ namespace
"\n"
"slice(texture)\n"
"{\n"
" vec4 diffuse = color;\n"
" vec4 diffuse = fragColor;\n"
" vec4 currentColor = diffuse;\n"
" %s\n"
" color = currentColor;\n"
" fragColor = currentColor;\n"
"}\n"
"\n"
"slice(finish)\n"
@ -614,7 +614,10 @@ namespace
bool using_shared_vertices = instance.GetSubMesh().GetVertices().empty();
GLuint vbo = mesh_instance.GetMesh().GetVBO();
size_t buffer_offset = using_shared_vertices ? 0 : instance.GetSubMesh().GetOffsetInBuffer();
GLuint ibo = mesh_instance.GetIBO();
unsigned int vaoid = mesh_instance.GetVAOID();
size_t vertex_buffer_offset = using_shared_vertices ? 0 : instance.GetSubMesh().GetOffsetInVBO();
size_t index_buffer_offset = instance.GetSubMesh().GetOffsetInIBO(); // note this is constant
// Cook the bone transform matrixes into something that OpenGL can use. This could be moved into RenderMeshImpl.
// Or, even better, we could upload them into a UBO, but Intel doesn't support them prior to Sandy Bridge.
@ -666,13 +669,6 @@ namespace
else
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
// Set material properties
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pass.Ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pass.Diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pass.Specular);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, pass.Emissive);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pass.Shininess);
glFrontFace(parity ? GL_CW : GL_CCW);
if(mesh_instance.GetCompletion() < 1.0f)
{
@ -734,6 +730,14 @@ namespace
call.SetUniformMatrix4x4(C4SSU_ModelViewMatrix, modelviewMatrix);
call.SetUniformMatrix3x3Transpose(C4SSU_NormalMatrix, normalMatrixTranspose);
// Upload material properties
call.SetUniform4fv(C4SSU_MaterialAmbient, 1, pass.Ambient);
call.SetUniform4fv(C4SSU_MaterialDiffuse, 1, pass.Diffuse);
call.SetUniform4fv(C4SSU_MaterialSpecular, 1, pass.Specular);
call.SetUniform4fv(C4SSU_MaterialEmission, 1, pass.Emissive);
call.SetUniform1f(C4SSU_MaterialShininess, pass.Shininess);
// Upload the current bone transformation matrixes (if there are any)
if (!bones.empty())
{
@ -743,24 +747,39 @@ namespace
glUniformMatrix4x3fv(shader->GetUniform(C4SSU_Bones), bones.size(), GL_TRUE, &bones[0].m[0][0]);
}
// Bind the vertex data of the mesh
GLuint vao;
const bool has_vao = pGL->GetVAO(vaoid, vao);
glBindVertexArray(vao);
if (!has_vao)
{
// Bind the vertex data of the mesh
// Note this relies on the fact that all vertex
// attributes for all shaders are at the same
// locations.
// TODO: And this fails if the mesh changes
// from a material with texture to one without
// or vice versa.
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
#define VERTEX_OFFSET(field) reinterpret_cast<const uint8_t *>(offsetof(StdMeshVertex, field))
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(shader->GetAttribute(C4SSA_Position), 3, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(x));
glVertexAttribPointer(shader->GetAttribute(C4SSA_Normal), 3, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(nx));
glVertexAttribPointer(shader->GetAttribute(C4SSA_TexCoord), 2, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(u));
glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneWeights0), 4, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(bone_weight));
glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneWeights1), 4, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(bone_weight) + 4 * sizeof(std::remove_all_extents<decltype(StdMeshVertex::bone_weight)>::type));
glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneIndices0), 4, GL_SHORT, GL_FALSE, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(bone_index));
glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneIndices1), 4, GL_SHORT, GL_FALSE, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(bone_index) + 4 * sizeof(std::remove_all_extents<decltype(StdMeshVertex::bone_index)>::type));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_Position));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_Normal));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_TexCoord));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneWeights0));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneWeights1));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneIndices0));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneIndices1));
glVertexAttribPointer(shader->GetAttribute(C4SSA_Position), 3, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(x));
glVertexAttribPointer(shader->GetAttribute(C4SSA_Normal), 3, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(nx));
if (shader->GetAttribute(C4SSA_TexCoord) != -1)
glVertexAttribPointer(shader->GetAttribute(C4SSA_TexCoord), 2, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(u));
glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneWeights0), 4, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(bone_weight));
glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneWeights1), 4, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(bone_weight) + 4 * sizeof(std::remove_all_extents<decltype(StdMeshVertex::bone_weight)>::type));
glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneIndices0), 4, GL_SHORT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(bone_index));
glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneIndices1), 4, GL_SHORT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(bone_index) + 4 * sizeof(std::remove_all_extents<decltype(StdMeshVertex::bone_index)>::type));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_Position));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_Normal));
if (shader->GetAttribute(C4SSA_TexCoord) != -1)
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_TexCoord));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneWeights0));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneWeights1));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneIndices0));
glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneIndices1));
#undef VERTEX_OFFSET
}
// Bind textures
for (unsigned int j = 0; j < pass.TextureUnits.size(); ++j)
@ -821,15 +840,10 @@ namespace
}
size_t vertex_count = 3 * instance.GetNumFaces();
glDrawElements(GL_TRIANGLES, vertex_count, GL_UNSIGNED_INT, instance.GetFaces());
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(shader->GetAttribute(C4SSA_Position));
glDisableVertexAttribArray(shader->GetAttribute(C4SSA_Normal));
glDisableVertexAttribArray(shader->GetAttribute(C4SSA_TexCoord));
glDisableVertexAttribArray(shader->GetAttribute(C4SSA_BoneWeights0));
glDisableVertexAttribArray(shader->GetAttribute(C4SSA_BoneWeights1));
glDisableVertexAttribArray(shader->GetAttribute(C4SSA_BoneIndices0));
glDisableVertexAttribArray(shader->GetAttribute(C4SSA_BoneIndices1));
assert (vertex_buffer_offset % sizeof(StdMeshVertex) == 0);
size_t base_vertex = vertex_buffer_offset / sizeof(StdMeshVertex);
glDrawElementsBaseVertex(GL_TRIANGLES, vertex_count, GL_UNSIGNED_INT, reinterpret_cast<void*>(index_buffer_offset), base_vertex);
glBindVertexArray(0);
call.Finish();
if(!pass.DepthCheck)
@ -869,14 +883,9 @@ namespace
for (; attach_iter != instance.AttachedMeshesEnd() && ((*attach_iter)->GetFlags() & StdMeshInstance::AM_DrawBefore); ++attach_iter)
RenderAttachedMesh(projectionMatrix, modelviewMatrix, *attach_iter, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, outRect, parity);
GLint modes[2];
// Check if we should draw in wireframe or normal mode
if(dwBlitMode & C4GFXBLIT_WIREFRAME)
{
// save old mode
glGetIntegerv(GL_POLYGON_MODE, modes);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
// Render each submesh
for (unsigned int i = 0; i < mesh.GetNumSubMeshes(); ++i)
@ -884,10 +893,7 @@ namespace
// reset old mode to prevent rendering errors
if(dwBlitMode & C4GFXBLIT_WIREFRAME)
{
glPolygonMode(GL_FRONT, modes[0]);
glPolygonMode(GL_BACK, modes[1]);
}
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Render non-AM_DrawBefore attached meshes
for (; attach_iter != instance.AttachedMeshesEnd(); ++attach_iter)

View File

@ -55,7 +55,7 @@ bool C4FacetSurface::Create(int iWdt, int iHgt, int iWdt2, int iHgt2)
Clear();
// Create surface
Face.Default();
if (!Face.Create(iWdt,iHgt,false,0,0)) return false;
if (!Face.Create(iWdt,iHgt)) return false;
// Set facet
if (iWdt2==C4FCT_Full) iWdt2=Face.Wdt; if (iWdt2==C4FCT_Height) iWdt2=Face.Hgt; if (iWdt2==C4FCT_Width) iWdt2=Face.Wdt;
if (iHgt2==C4FCT_Full) iHgt2=Face.Hgt; if (iHgt2==C4FCT_Height) iHgt2=Face.Hgt; if (iHgt2==C4FCT_Width) iHgt2=Face.Wdt;

View File

@ -311,7 +311,7 @@ bool CStdFont::AddSurface()
psfcFontData = pNewSfcs;
C4Surface *sfcNew = psfcFontData[iNumFontSfcs] = new C4Surface();
++iNumFontSfcs;
if (iSfcSizes) if (!sfcNew->Create(iSfcSizes, iSfcSizes,false,0,0)) return false;
if (iSfcSizes) if (!sfcNew->Create(iSfcSizes, iSfcSizes)) return false;
// If old surface was locked, unlock it and lock the new one in its stead
if (sfcCurrent && sfcCurrent->IsLocked())
{

View File

@ -289,7 +289,7 @@ void C4Shader::Clear()
#ifndef USE_CONSOLE
if (!hProg) return;
// Need to be detached, then deleted
glDeleteObjectARB(hProg);
glDeleteProgram(hProg);
hProg = 0;
// Clear uniform data
Uniforms.clear();
@ -313,43 +313,46 @@ bool C4Shader::Init(const char *szWhat, const char **szUniforms, const char **sz
#ifndef USE_CONSOLE
// Attempt to create shaders
const GLint hVert = Create(GL_VERTEX_SHADER_ARB,
FormatString("%s vertex shader", szWhat).getData(),
VertexShader.getData());
const GLint hFrag = Create(GL_FRAGMENT_SHADER_ARB,
FormatString("%s fragment shader", szWhat).getData(),
FragmentShader.getData());
const GLuint hVert = Create(GL_VERTEX_SHADER,
FormatString("%s vertex shader", szWhat).getData(),
VertexShader.getData());
const GLuint hFrag = Create(GL_FRAGMENT_SHADER,
FormatString("%s fragment shader", szWhat).getData(),
FragmentShader.getData());
if(!hFrag || !hVert)
{
if (hVert) glDeleteObjectARB(hVert);
if (hFrag) glDeleteShader(hFrag);
if (hVert) glDeleteShader(hVert);
return false;
}
// Link program
const GLint hNewProg = glCreateProgramObjectARB();
const GLuint hNewProg = glCreateProgram();
#ifdef GL_KHR_debug
if (glObjectLabel)
glObjectLabel(GL_PROGRAM, hNewProg, -1, szWhat);
#endif
glAttachObjectARB(hNewProg, hVert);
glAttachObjectARB(hNewProg, hFrag);
glLinkProgramARB(hNewProg);
glAttachShader(hNewProg, hVert);
glAttachShader(hNewProg, hFrag);
glLinkProgram(hNewProg);
// Delete vertex and fragment shader after we linked the program
glDeleteObjectARB(hFrag);
glDeleteObjectARB(hVert);
glDeleteShader(hFrag);
glDeleteShader(hVert);
// Link successful?
DumpInfoLog(FormatString("%s shader program", szWhat).getData(), hNewProg);
if(GetObjectStatus(hNewProg, GL_OBJECT_LINK_STATUS_ARB) != 1) {
glDeleteObjectARB(hNewProg);
DumpInfoLog(FormatString("%s shader program", szWhat).getData(), hNewProg, true);
GLint status;
glGetProgramiv(hNewProg, GL_LINK_STATUS, &status);
if(status != GL_TRUE) {
glDeleteProgram(hNewProg);
ShaderLogF(" gl: Failed to link %s shader!", szWhat);
return false;
}
ShaderLogF(" gl: %s shader linked successfully", szWhat);
// Everything successful, delete old shader
if (hProg != 0) glDeleteObjectARB(hProg);
if (hProg != 0) glDeleteProgram(hProg);
hProg = hNewProg;
// Allocate uniform and attribute arrays
@ -368,13 +371,13 @@ bool C4Shader::Init(const char *szWhat, const char **szUniforms, const char **sz
// Get uniform and attribute locations. Note this is expected to fail for a few of them
// because the respective uniforms got optimized out!
for (int i = 0; i < iUniformCount; i++) {
Uniforms[i].address = glGetUniformLocationARB(hProg, szUniforms[i]);
Uniforms[i].address = glGetUniformLocation(hProg, szUniforms[i]);
Uniforms[i].name = szUniforms[i];
ShaderLogF("Uniform %s = %d", szUniforms[i], Uniforms[i].address);
}
for (int i = 0; i < iAttributeCount; i++) {
Attributes[i].address = glGetAttribLocationARB(hProg, szAttributes[i]);
Attributes[i].address = glGetAttribLocation(hProg, szAttributes[i]);
Attributes[i].name = szAttributes[i];
ShaderLogF("Attribute %s = %d", szAttributes[i], Attributes[i].address);
}
@ -465,15 +468,15 @@ StdStrBuf C4Shader::Build(const ShaderSliceList &Slices, bool fDebug)
StdStrBuf Buf;
#ifndef USE_CONSOLE
GLint iMaxFrags = 0, iMaxVerts = 0;
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB, &iMaxFrags);
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &iMaxVerts);
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &iMaxFrags);
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &iMaxVerts);
#else
int iMaxFrags = INT_MAX, iMaxVerts = INT_MAX;
#endif
Buf.Format("#version %d\n"
"#define MAX_FRAGMENT_UNIFORM_COMPONENTS %d\n"
"#define MAX_VERTEX_UNIFORM_COMPONENTS %d\n",
C4Shader_Version, iMaxFrags, iMaxVerts);
"#define MAX_FRAGMENT_UNIFORM_COMPONENTS %d\n"
"#define MAX_VERTEX_UNIFORM_COMPONENTS %d\n",
C4Shader_Version, iMaxFrags, iMaxVerts);
// Put slices
int iPos = -1, iNextPos = -1;
@ -516,56 +519,56 @@ StdStrBuf C4Shader::Build(const ShaderSliceList &Slices, bool fDebug)
}
#ifndef USE_CONSOLE
GLhandleARB C4Shader::Create(GLenum iShaderType, const char *szWhat, const char *szShader)
GLuint C4Shader::Create(GLenum iShaderType, const char *szWhat, const char *szShader)
{
// Create shader
GLhandleARB hShader = glCreateShaderObjectARB(iShaderType);
GLuint hShader = glCreateShader(iShaderType);
#ifdef GL_KHR_debug
if (glObjectLabel)
glObjectLabel(GL_SHADER, hShader, -1, szWhat);
#endif
// Compile
glShaderSourceARB(hShader, 1, &szShader, 0);
glCompileShaderARB(hShader);
glShaderSource(hShader, 1, &szShader, 0);
glCompileShader(hShader);
// Dump any information to log
DumpInfoLog(szWhat, hShader);
DumpInfoLog(szWhat, hShader, false);
// Success?
if(GetObjectStatus(hShader, GL_OBJECT_COMPILE_STATUS_ARB) == 1)
int status;
glGetShaderiv(hShader, GL_COMPILE_STATUS, &status);
if (status == GL_TRUE)
return hShader;
// Did not work :/
glDeleteObjectARB(hShader);
glDeleteShader(hShader);
return 0;
}
void C4Shader::DumpInfoLog(const char *szWhat, GLhandleARB hShader)
void C4Shader::DumpInfoLog(const char *szWhat, GLuint hShader, bool forProgram)
{
// Get length of info line
int iLength = 0;
glGetObjectParameterivARB(hShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &iLength);
GLint iLength = 0;
if (forProgram)
glGetProgramiv(hShader, GL_INFO_LOG_LENGTH, &iLength);
else
glGetShaderiv(hShader, GL_INFO_LOG_LENGTH, &iLength);
if(iLength <= 1) return;
// Allocate buffer, get data
char *pBuf = new char [iLength + 1];
std::vector<char> buf(iLength + 1);
int iActualLength = 0;
glGetInfoLogARB(hShader, iLength, &iActualLength, pBuf);
if (forProgram)
glGetProgramInfoLog(hShader, iLength, &iActualLength, &buf[0]);
else
glGetShaderInfoLog(hShader, iLength, &iActualLength, &buf[0]);
if(iActualLength > iLength || iActualLength <= 0) return;
// Terminate, log
pBuf[iActualLength] = '\0';
buf[iActualLength] = '\0';
ShaderLogF(" gl: Compiling %s:", szWhat);
ShaderLog(pBuf);
delete[] pBuf;
}
int C4Shader::GetObjectStatus(GLhandleARB hObj, GLenum type)
{
int iStatus = 0;
glGetObjectParameterivARB(hObj, type, &iStatus);
return iStatus;
ShaderLog(&buf[0]);
}
#endif
@ -605,7 +608,7 @@ void C4ShaderCall::Start()
const_cast<C4Shader *>(pShader)->Refresh();
// Activate shader
glUseProgramObjectARB(pShader->hProg);
glUseProgram(pShader->hProg);
fStarted = true;
}
@ -613,7 +616,7 @@ void C4ShaderCall::Finish()
{
// Remove shader
if (fStarted) {
glUseProgramObjectARB(0);
glUseProgram(0);
}
iUnits = 0;

View File

@ -23,7 +23,7 @@
#include "C4Surface.h"
// Shader version
const int C4Shader_Version = 120; // GLSL 1.20 / OpenGL 2.1
const int C4Shader_Version = 150; // GLSL 1.50 / OpenGL 3.2
// Maximum number of texture coordinates
const int C4Shader_MaxTexCoords = 8;
@ -77,7 +77,7 @@ private:
#ifndef USE_CONSOLE
// shaders
GLhandleARB hProg;
GLuint hProg;
// shader variables
struct Variable { int address; const char* name; };
std::vector<Variable> Uniforms;
@ -151,9 +151,8 @@ private:
StdStrBuf Build(const ShaderSliceList &Slices, bool fDebug = false);
#ifndef USE_CONSOLE
GLhandleARB Create(GLenum iShaderType, const char *szWhat, const char *szShader);
void DumpInfoLog(const char *szWhat, GLhandleARB hShader);
int GetObjectStatus(GLhandleARB hObj, GLenum type);
GLuint Create(GLenum iShaderType, const char *szWhat, const char *szShader);
void DumpInfoLog(const char *szWhat, GLuint hShader, bool forProgram);
#endif
public:
@ -186,35 +185,35 @@ public:
// something could be done about it.
void SetUniform1i(int iUniform, int iX) const {
if (pShader->HaveUniform(iUniform))
glUniform1iARB(pShader->GetUniform(iUniform), iX);
glUniform1i(pShader->GetUniform(iUniform), iX);
}
void SetUniform1f(int iUniform, float gX) const {
if (pShader->HaveUniform(iUniform))
glUniform1fARB(pShader->GetUniform(iUniform), gX);
glUniform1f(pShader->GetUniform(iUniform), gX);
}
void SetUniform2f(int iUniform, float gX, float gY) const {
if (pShader->HaveUniform(iUniform))
glUniform2fARB(pShader->GetUniform(iUniform), gX, gY);
glUniform2f(pShader->GetUniform(iUniform), gX, gY);
}
void SetUniform1iv(int iUniform, int iLength, const int *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform1ivARB(pShader->GetUniform(iUniform), iLength, pVals);
glUniform1iv(pShader->GetUniform(iUniform), iLength, pVals);
}
void SetUniform1fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform1fvARB(pShader->GetUniform(iUniform), iLength, pVals);
glUniform1fv(pShader->GetUniform(iUniform), iLength, pVals);
}
void SetUniform2fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform2fvARB(pShader->GetUniform(iUniform), iLength, pVals);
glUniform2fv(pShader->GetUniform(iUniform), iLength, pVals);
}
void SetUniform3fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform3fvARB(pShader->GetUniform(iUniform), iLength, pVals);
glUniform3fv(pShader->GetUniform(iUniform), iLength, pVals);
}
void SetUniform4fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform4fvARB(pShader->GetUniform(iUniform), iLength, pVals);
glUniform4fv(pShader->GetUniform(iUniform), iLength, pVals);
}
// Matrices are in row-major order
@ -235,7 +234,7 @@ public:
void SetUniformMatrix4x4fv(int iUniform, int iLength, const float* pVals) const {
if (pShader->HaveUniform(iUniform))
glUniformMatrix4fvARB(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
glUniformMatrix4fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
}
void SetUniformMatrix3x3(int iUniform, const StdMeshMatrix& matrix)
@ -267,14 +266,14 @@ public:
if (pShader->HaveUniform(iUniform))
{
const float mat[16] = { matrix(0, 0), matrix(1, 0), matrix(2, 0), 0.0f, matrix(0, 1), matrix(1, 1), matrix(2, 1), 0.0f, matrix(0, 2), matrix(1, 2), matrix(2, 2), 0.0f, matrix(0, 3), matrix(1, 3), matrix(2, 3), 1.0f };
glUniformMatrix4fvARB(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
glUniformMatrix4fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
}
}
void SetUniformMatrix4x4(int iUniform, const StdProjectionMatrix& matrix)
{
if (pShader->HaveUniform(iUniform))
glUniformMatrix4fvARB(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data());
glUniformMatrix4fv(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data());
}
void Start();

View File

@ -51,7 +51,7 @@ C4Surface::C4Surface(int iWdt, int iHgt, int iFlags) : fIsBackground(false)
{
Default();
// create
Create(iWdt, iHgt, false, 0, iFlags);
Create(iWdt, iHgt, iFlags);
}
C4Surface::C4Surface(C4AbstractApp * pApp, C4Window * pWindow):
@ -94,7 +94,6 @@ void C4Surface::Default()
pWindow=NULL;
ClrByOwnerClr=0;
iTexSize=iTexX=iTexY=0;
fIsRenderTarget=false;
fIsBackground=false;
#ifdef _DEBUG
dbg_idx = NULL;
@ -172,7 +171,7 @@ void C4Surface::Clip(int iX, int iY, int iX2, int iY2)
ClipX2=Clamp(iX2,0,Wdt-1); ClipY2=Clamp(iY2,0,Hgt-1);
}
bool C4Surface::Create(int iWdt, int iHgt, bool fIsRenderTarget, int MaxTextureSize, int iFlags)
bool C4Surface::Create(int iWdt, int iHgt, int iFlags)
{
Clear(); Default();
// check size
@ -187,9 +186,8 @@ bool C4Surface::Create(int iWdt, int iHgt, bool fIsRenderTarget, int MaxTextureS
Format=pGL->sfcFmt;
#endif
byBytesPP=pDraw->byByteCnt;
this->fIsRenderTarget = fIsRenderTarget;
// create textures
if (!CreateTextures(MaxTextureSize, iFlags)) { Clear(); return false; }
if (!CreateTextures(iFlags)) { Clear(); return false; }
// update clipping
NoClip();
// success
@ -203,7 +201,7 @@ bool C4Surface::Copy(C4Surface &fromSfc)
// Default to other surface's color depth
Default();
// Create surface (TODO: copy flags)
if (!Create(fromSfc.Wdt, fromSfc.Hgt, false, 0, 0)) return false;
if (!Create(fromSfc.Wdt, fromSfc.Hgt)) return false;
// Blit copy
if (!pDraw->BlitSurface(&fromSfc, this, 0, 0, false))
{ Clear(); return false; }
@ -211,39 +209,16 @@ bool C4Surface::Copy(C4Surface &fromSfc)
return true;
}
namespace
{
int GetNeedTexSize(int Size)
{
int iNeedSize = Size;
#ifndef USE_CONSOLE
if (!pGL || !GLEW_ARB_texture_non_power_of_two)
#endif
{
int n=0;
while ((1<<++n) < iNeedSize) {}
iNeedSize = 1<<n;
}
return iNeedSize;
}
}
bool C4Surface::CreateTextures(int MaxTextureSize, int Flags)
bool C4Surface::CreateTextures(int Flags)
{
// free previous
FreeTextures();
iTexSize=std::min(GetNeedTexSize(std::max(Wdt, Hgt)), pDraw->MaxTexSize);
if (MaxTextureSize)
iTexSize=std::min(iTexSize, MaxTextureSize);
iTexSize=std::min(std::max(Wdt, Hgt), pDraw->MaxTexSize);
// get the number of textures needed for this size
iTexX=(Wdt-1)/iTexSize +1;
iTexY=(Hgt-1)/iTexSize +1;
// get mem for texture array
textures.reserve(iTexX * iTexY);
// cvan't be render target if it's not a single surface
if (!IsSingleSurface()) fIsRenderTarget = false;
// create textures
for (int y = 0; y < iTexY; ++y)
{
@ -251,8 +226,8 @@ bool C4Surface::CreateTextures(int MaxTextureSize, int Flags)
{
int sizeX = iTexSize;
int sizeY = iTexSize;
if(x == iTexX-1) sizeX = GetNeedTexSize( (Wdt - 1) % iTexSize + 1);
if(y == iTexY-1) sizeY = GetNeedTexSize( (Hgt - 1) % iTexSize + 1);
if(x == iTexX-1) sizeX = (Wdt - 1) % iTexSize + 1;
if(y == iTexY-1) sizeY = (Hgt - 1) % iTexSize + 1;
textures.emplace_back(sizeX, sizeY, Flags);
@ -337,7 +312,7 @@ bool C4Surface::CreateColorByOwner(C4Surface *pBySurface)
if (!pBySurface) return false;
if (pBySurface->textures.empty()) return false;
// create in same size
if (!Create(pBySurface->Wdt, pBySurface->Hgt, false, 0, 0)) return false;
if (!Create(pBySurface->Wdt, pBySurface->Hgt)) return false;
// copy scale
Scale = pBySurface->Scale;
// set main surface
@ -421,7 +396,7 @@ bool C4Surface::ReadBMP(CStdStream &hGroup, int iFlags)
}
// Create and lock surface
if (!Create(BitmapInfo.Info.biWidth,BitmapInfo.Info.biHeight, false, 0, iFlags)) return false;
if (!Create(BitmapInfo.Info.biWidth,BitmapInfo.Info.biHeight, iFlags)) return false;
if (!Lock()) { Clear(); return false; }
// create line buffer
@ -729,6 +704,8 @@ bool C4Surface::SetPixDw(int iX, int iY, DWORD dwClr)
uint16_t wClr=ClrDw2W(dwClr);
glTexSubImage2D(GL_TEXTURE_2D, 0, iX, iY, 1, 1, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV, &wClr);
}
const bool fMipMap = (pTexRef->iFlags & C4SF_MipMap) != 0;
if (fMipMap) glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
// Otherwise, make sure that the texlock covers the new pixel
@ -968,8 +945,8 @@ void C4TexRef::CreateTexture()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, fTileable ? GL_REPEAT : GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fMipMap ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
if (fMipMap) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iSizeX, iSizeY, 0, GL_BGRA, pDraw->byByteCnt == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
if (fMipMap) glGenerateMipmap(GL_TEXTURE_2D);
#endif
}
@ -1038,6 +1015,10 @@ void C4TexRef::Unlock()
assert(pGL->pMainCtx);
pGL->pMainCtx->Select();
}
const bool fTileable = (iFlags & C4SF_Tileable) != 0;
const bool fMipMap = (iFlags & C4SF_MipMap) != 0;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// reuse the existing texture
@ -1047,6 +1028,7 @@ void C4TexRef::Unlock()
GL_BGRA, pDraw->byByteCnt == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV, texLock.pBits);
delete[] static_cast<unsigned char*>(texLock.pBits); texLock.pBits=NULL;
if (fMipMap) glGenerateMipmap(GL_TEXTURE_2D);
#endif
}

View File

@ -78,7 +78,6 @@ public:
int iTexSize; // size of textures
int iTexX, iTexY; // number of textures in x/y-direction
int ClipX,ClipY,ClipX2,ClipY2;
bool fIsRenderTarget; // set for surfaces to be used as offscreen render targets
bool fIsBackground; // background surfaces fill unused pixels with black, rather than transparency - must be set prior to loading
#ifdef _DEBUG
int *dbg_idx;
@ -127,7 +126,7 @@ public:
bool SetPixDw(int iX, int iY, DWORD dwCol); // set pix in surface only
bool SetPixAlpha(int iX, int iY, BYTE byAlpha); // adjust alpha value of pixel
bool BltPix(int iX, int iY, C4Surface *sfcSource, int iSrcX, int iSrcY, bool fTransparency); // blit pixel from source to this surface (assumes clipped coordinates!)
bool Create(int iWdt, int iHgt, bool fIsRenderTarget, int MaxTextureSize, int iFlags);
bool Create(int iWdt, int iHgt, int iFlags = 0);
bool Copy(C4Surface &fromSfc);
bool CreateColorByOwner(C4Surface *pBySurface); // create ColorByOwner-surface
bool SetAsClrByOwnerOf(C4Surface *pOfSurface); // assume that ColorByOwner-surface has been created, and just assign it; fails if the size doesn't match
@ -163,7 +162,7 @@ public:
private:
void MapBytes(BYTE *bpMap);
bool ReadBytes(BYTE **lpbpData, void *bpTarget, int iSize);
bool CreateTextures(int MaxTextureSize, int iFlags); // create ppTex-array
bool CreateTextures(int iFlags); // create ppTex-array
void FreeTextures(); // free ppTex-array if existant
bool GetTexAtImpl(C4TexRef **ppTexRef, int &rX, int &rY);

View File

@ -162,7 +162,7 @@ bool C4Surface::ReadPNG(CStdStream &hGroup, int iFlags)
// abort if loading wasn't successful
if (!fSuccess) return false;
// create surface(s) - do not create an 8bit-buffer!
if (!Create(png.iWdt, png.iHgt, false, 0, iFlags)) return false;
if (!Create(png.iWdt, png.iHgt, iFlags)) return false;
// lock for writing data
if (!Lock()) return false;
if (textures.empty())
@ -342,7 +342,7 @@ bool C4Surface::ReadJPEG(CStdStream &hGroup, int iFlags)
jpeg_start_decompress(&cinfo);
// create surface(s) - do not create an 8bit-buffer!
if (!Create(cinfo.output_width, cinfo.output_height, false, 0, iFlags)) return false;
if (!Create(cinfo.output_width, cinfo.output_height, iFlags)) return false;
// JSAMPLEs per row in output buffer
row_stride = cinfo.output_width * cinfo.output_components;
// Make a one-row-high sample array that will go away at jpeg_destroy_decompress
@ -377,7 +377,7 @@ bool C4Surface::ReadJPEG(CStdStream &hGroup, int iFlags)
bool C4Surface::ReadJPEG(CStdStream &, int) {
// Dummy surface
if (!Create(1, 1, false, 1, 0)) return false;
if (!Create(1, 1)) return false;
return true;
}

View File

@ -368,10 +368,8 @@ void CPNGFile::WaitForSaves()
first = false;
#ifdef HAVE_WINTHREAD
Sleep(100);
#elif defined (__APPLE__)
#else
sched_yield();
#elif defined(HAVE_PTHREAD)
pthread_yield();
#endif
}
}

View File

@ -322,7 +322,6 @@ namespace C4GameLobby
else if (eToState == CDS_Countdown)
{
StartSoundEffect("Fire::Fuse");
StartSoundEffect("Structures::Elevator::Moving", true);
}
if (eToState == CDS_Countdown || eToState == CDS_LongCountdown)
{
@ -373,7 +372,7 @@ namespace C4GameLobby
{
// first countdown message
OnLog(Pkt.GetCountdownMsg(!fWasCountdown).getData(), C4GUI_LogFontClr2);
StartSoundEffect("Hits::Materials::Wood::WoodHit*");
StartSoundEffect("UI::Tick");
}
}

View File

@ -831,7 +831,7 @@ bool C4KeyboardInput::DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent,
{
// store last-key-info
LastKeyExtraData.iStrength = (iStrength >= 0) ? iStrength : ((InEvent != KEYEV_Up) * 100);
LastKeyExtraData.game_x = LastKeyExtraData.game_y = LastKeyExtraData.vp_x = LastKeyExtraData.vp_y = 0;
LastKeyExtraData.game_x = LastKeyExtraData.game_y = LastKeyExtraData.vp_x = LastKeyExtraData.vp_y = C4KeyEventData::KeyPos_None;
// check all key events generated by this key: First the keycode itself, then any more generic key events like KEY_Any
const int32_t iKeyRangeMax = 5;
int32_t iKeyRangeCnt=0, j;

View File

@ -225,9 +225,10 @@ private:
// extra data associated with a key event
struct C4KeyEventData
{
enum { KeyPos_None = 0x7fffff }; // value used to denote invalid/none key positions
int32_t iStrength; // pressure between 0 and 100 (100 for nomal keypress)
int32_t game_x,game_y, vp_x,vp_y; // position for mouse event, landscape+viewport coordinates
C4KeyEventData() : iStrength(0), game_x(0), game_y(0), vp_x(0), vp_y(0) {}
C4KeyEventData() : iStrength(0), game_x(KeyPos_None), game_y(KeyPos_None), vp_x(KeyPos_None), vp_y(KeyPos_None) {}
C4KeyEventData(int32_t iStrength, int32_t game_x, int32_t game_y, int32_t vp_x, int32_t vp_y) : iStrength(iStrength), game_x(game_x), game_y(game_y),vp_x(vp_x),vp_y(vp_y) {}
void CompileFunc(StdCompiler *pComp);
bool operator ==(const struct C4KeyEventData &cmp) const;

View File

@ -1694,13 +1694,22 @@ bool C4ScriptGuiWindow::UpdateLayout(C4TargetFacet &cgo, float parentWidth, floa
// If this window contains text, we auto-fit to the text height;
// but we only break text when the window /would/ crop it otherwise.
StdCopyStrBuf *strBuf = props[C4ScriptGuiWindowPropertyName::text].GetStrBuf();
int minRequiredTextHeight = 0;
if (strBuf && !(style & C4ScriptGuiWindowStyleFlag::NoCrop))
{
StdStrBuf sText;
int32_t textHgt = ::GraphicsResource.FontRegular.BreakMessage(strBuf->getData(), rcBounds.Wdt, &sText, true);
const int32_t rawTextHeight = ::GraphicsResource.FontRegular.BreakMessage(strBuf->getData(), rcBounds.Wdt, &sText, true);
// enable auto scroll
if (textHgt > rcBounds.Hgt)
rcBounds.Hgt = textHgt;
if (rawTextHeight - 1 > rcBounds.Hgt)
{
// If we need to scroll, we will also have to add a scrollbar that takes up some width.
// Recalculate the actual height, taking into account the scrollbar.
// This is not done in the calculation earlier, because then e.g. a 2x1em field could not contain two letters
// but would immediately add a linebreak.
// In the case that this window auto-resizes (FitChildren), the small additional margin to the bottom should not matter much.
const int32_t actualTextHeight = ::GraphicsResource.FontRegular.BreakMessage(strBuf->getData(), rcBounds.Wdt - pScrollBar->rcBounds.Wdt, &sText, true);
minRequiredTextHeight = actualTextHeight;
}
}
UpdateChildLayout(cgo, width, height);
@ -1716,7 +1725,7 @@ bool C4ScriptGuiWindow::UpdateLayout(C4TargetFacet &cgo, float parentWidth, floa
// check if we need a scroll-bar
int32_t topMostChild = 0;
int32_t bottomMostChild = 0;
int32_t bottomMostChild = minRequiredTextHeight;
for (Element * element : *this)
{
C4ScriptGuiWindow *child = static_cast<C4ScriptGuiWindow*>(element);
@ -1843,12 +1852,18 @@ bool C4ScriptGuiWindow::Draw(C4TargetFacet &cgo, int32_t player, C4Rect *current
{
StdStrBuf sText;
int alignment = ALeft;
// If we are showing a scrollbar, we need to leave a bit of space for it so that it doesn't overlap the text.
const int scrollbarXOffset = pScrollBar->IsVisible() ? pScrollBar->rcBounds.Wdt : 0;
const int scrollbarScroll = pScrollBar->IsVisible() ? this->GetScrollY() : 0;
const int actualDrawingWidth = outDrawWdt - scrollbarXOffset;
// If we are set to NoCrop, the message will be split by string-defined line breaks only.
int allowedTextWidth = outDrawWdt;
int allowedTextWidth = actualDrawingWidth;
if (style & C4ScriptGuiWindowStyleFlag::NoCrop)
allowedTextWidth = std::numeric_limits<int>::max();
int32_t textHgt = ::GraphicsResource.FontRegular.BreakMessage(strBuf->getData(), allowedTextWidth, &sText, true);
float textYOffset = 0.0f, textXOffset = 0.0f;
float textYOffset = static_cast<float>(-scrollbarScroll), textXOffset = 0.0f;
if (style & C4ScriptGuiWindowStyleFlag::TextVCenter)
textYOffset = float(outDrawHgt) / 2.0f - float(textHgt) / 2.0f;
else if (style & C4ScriptGuiWindowStyleFlag::TextBottom)
@ -1857,7 +1872,7 @@ bool C4ScriptGuiWindow::Draw(C4TargetFacet &cgo, int32_t player, C4Rect *current
{
int wdt, hgt;
::GraphicsResource.FontRegular.GetTextExtent(sText.getData(), wdt, hgt, true);
textXOffset = float(outDrawWdt) / 2.0f;
textXOffset = float(actualDrawingWidth) / 2.0f;
alignment = ACenter;
}
else if (style & C4ScriptGuiWindowStyleFlag::TextRight)
@ -1865,7 +1880,7 @@ bool C4ScriptGuiWindow::Draw(C4TargetFacet &cgo, int32_t player, C4Rect *current
alignment = ARight;
int wdt, hgt;
::GraphicsResource.FontRegular.GetTextExtent(sText.getData(), wdt, hgt, true);
textXOffset = float(outDrawWdt);
textXOffset = float(actualDrawingWidth);
}
pDraw->TextOut(sText.getData(), ::GraphicsResource.FontRegular, 1.0, cgo.Surface, outDrawX + textXOffset, outDrawY + textYOffset, 0xffffffff, alignment);
}
@ -1896,20 +1911,13 @@ bool C4ScriptGuiWindow::GetClippingRect(int32_t &left, int32_t &top, int32_t &ri
if (IsRoot() || (style & C4ScriptGuiWindowStyleFlag::NoCrop))
return false;
if (pScrollBar->IsVisible())
{
left = rcBounds.x;
top = rcBounds.y;
right = rcBounds.Wdt + left;
bottom = rcBounds.Hgt + top;
return true;
}
/*const int32_t &style = props[C4ScriptGuiWindowPropertyName::style].GetInt();
if (!isMainWindow && !(style & C4ScriptGuiWindowStyleFlag::NoCrop))
return static_cast<C4ScriptGuiWindow*>(GetParent())->GetClippingRect(left, top, right, bottom);
*/
return false;
// Other windows always clip their children.
// This implicitly clips childrens' text to the parent size, too.
left = rcBounds.x;
top = rcBounds.y;
right = rcBounds.Wdt + left;
bottom = rcBounds.Hgt + top;
return true;
}
void C4ScriptGuiWindow::SetTag(C4String *tag)

View File

@ -54,8 +54,10 @@ const char *const SEPERATOR_TEXTURE = "--SEP--";
C4LandscapeRenderGL::C4LandscapeRenderGL()
{
ZeroMem(Surfaces, sizeof(Surfaces));
ZeroMem(hMaterialTexture, sizeof(hMaterialTexture));
hMaterialTexture = 0;
hVBO = 0;
hVAOIDNoLight = 0;
hVAOIDLight = 0;
}
C4LandscapeRenderGL::~C4LandscapeRenderGL()
@ -142,12 +144,26 @@ void C4LandscapeRenderGL::Clear()
delete Surfaces[i];
Surfaces[i] = NULL;
}
if (hMaterialTexture) glDeleteTextures(1, &hMaterialTexture);
hMaterialTexture = 0;
glDeleteTextures(C4LR_MipMapCount, hMaterialTexture);
std::fill_n(hMaterialTexture, C4LR_MipMapCount, 0);
if (hVBO != 0)
{
glDeleteBuffers(1, &hVBO);
hVBO = 0;
}
glDeleteBuffers(1, &hVBO);
hVBO = 0;
if (hVAOIDLight != 0)
{
pGL->FreeVAOID(hVAOIDLight);
hVAOIDLight = 0;
}
if (hVAOIDNoLight != 0)
{
pGL->FreeVAOID(hVAOIDNoLight);
hVAOIDNoLight = 0;
}
}
bool C4LandscapeRenderGL::InitLandscapeTexture()
@ -166,7 +182,7 @@ bool C4LandscapeRenderGL::InitLandscapeTexture()
for(int i = 0; i < C4LR_SurfaceCount; i++)
{
Surfaces[i] = new C4Surface();
if(!Surfaces[i]->Create(iSfcWdt, iSfcHgt, false, 0, 0))
if(!Surfaces[i]->Create(iSfcWdt, iSfcHgt))
return false;
}
@ -181,9 +197,7 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
AddTexturesFromMap(pTexs);
// Determine depth to use
iMaterialTextureDepth = 1;
while(iMaterialTextureDepth < 2*int32_t(MaterialTextureMap.size()))
iMaterialTextureDepth <<= 1;
iMaterialTextureDepth = 2*MaterialTextureMap.size();
int32_t iNormalDepth = iMaterialTextureDepth / 2;
// Find the largest texture
@ -197,15 +211,16 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
// Get size for our textures. We might be limited by hardware
int iTexWdt = pRefSfc->Wdt, iTexHgt = pRefSfc->Hgt;
GLint iMaxTexSize;
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &iMaxTexSize);
GLint iMaxTexSize, iMaxTexLayers;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &iMaxTexSize);
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &iMaxTexLayers);
if (iTexWdt > iMaxTexSize || iTexHgt > iMaxTexSize)
{
iTexWdt = std::min(iTexWdt, iMaxTexSize);
iTexHgt = std::min(iTexHgt, iMaxTexSize);
LogF(" gl: Material textures too large, GPU only supports %dx%d! Cropping might occur!", iMaxTexSize, iMaxTexSize);
}
if(iMaterialTextureDepth >= iMaxTexSize)
if(iMaterialTextureDepth >= iMaxTexLayers)
{
LogF(" gl: Too many material textures! GPU only supports 3D texture depth of %d!", iMaxTexSize);
return false;
@ -288,70 +303,34 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
// Clear error error(s?)
while(glGetError()) {}
// Alloc 3D textures
glEnable(GL_TEXTURE_3D);
glGenTextures(C4LR_MipMapCount, hMaterialTexture);
// Generate textures (mipmaps too!)
// Alloc 2D texture array
glGenTextures(1, &hMaterialTexture);
// Generate textures
int iSizeSum = 0;
BYTE *pLastData = new BYTE [iSize / 4];
for(int iMMLevel = 0; iMMLevel < C4LR_MipMapCount; iMMLevel++)
{
// Scale the texture down for mip-mapping
if(iMMLevel) {
BYTE *pOut = pData;
BYTE *pIn[4] = {
pLastData, pLastData + iBytesPP,
pLastData + iBytesPP * iTexWdt, pLastData + iBytesPP * iTexWdt + iBytesPP
};
for (int i = 0; i < iMaterialTextureDepth; ++i)
for (int y = 0; y < iTexHgt / 2; ++y)
{
for (int x = 0; x < iTexWdt / 2; ++x)
{
for (int j = 0; j < iBytesPP; j++)
{
unsigned int s = 0;
s += *pIn[0]++; s += 3 * *pIn[1]++; s += 3 * *pIn[2]++; s += *pIn[3]++;
*pOut++ = BYTE(s / 8);
}
pIn[0] += iBytesPP; pIn[1] += iBytesPP; pIn[2] += iBytesPP; pIn[3] += iBytesPP;
}
pIn[0] += iBytesPP * iTexWdt; pIn[1] += iBytesPP * iTexWdt;
pIn[2] += iBytesPP * iTexWdt; pIn[3] += iBytesPP * iTexWdt;
}
iTexWdt /= 2; iTexHgt /= 2;
}
// Select texture
glBindTexture(GL_TEXTURE_3D, hMaterialTexture[iMMLevel]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Select texture
glBindTexture(GL_TEXTURE_2D_ARRAY, hMaterialTexture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// We fully expect to tile these
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// We fully expect to tile these
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// Make it happen!
glTexImage3D(GL_TEXTURE_3D, 0, 4, iTexWdt, iTexHgt, iMaterialTextureDepth, 0, GL_BGRA,
iBytesPP == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV,
pData);
// Make it happen!
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, iTexWdt, iTexHgt, iMaterialTextureDepth, 0, GL_BGRA,
iBytesPP == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV,
pData);
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
// Exchange buffers
BYTE *tmp = pLastData;
pLastData = pData;
pData = tmp;
// Statistics
iSizeSum += iTexWdt * iTexHgt * iMaterialTextureDepth * iBytesPP;
}
// Statistics
iSizeSum += iTexWdt * iTexHgt * iMaterialTextureDepth * iBytesPP;
// Dispose of data
delete [] pData;
delete [] pLastData;
glDisable(GL_TEXTURE_3D);
// Check whether we were successful
if(int err = glGetError())
@ -361,10 +340,9 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
}
// Announce the good news
LogF(" gl: Texturing uses %d slots at %dx%d, %d levels (%d MB total)",
LogF(" gl: Texturing uses %d slots at %dx%d (%d MB total)",
static_cast<int>(MaterialTextureMap.size()),
iMaterialWidth, iMaterialHeight,
C4LR_MipMapCount,
iSizeSum / 1000000);
return true;
@ -634,13 +612,6 @@ bool C4LandscapeRenderGL::LoadShader(C4GroupSet *pGroups, C4Shader& shader, cons
bool C4LandscapeRenderGL::LoadShaders(C4GroupSet *pGroups)
{
// No support?
if(!GLEW_ARB_fragment_program)
{
Log(" gl: no shader support!");
return false;
}
// First, clear out all existing shaders
ClearShaders();
@ -677,6 +648,11 @@ bool C4LandscapeRenderGL::InitVBO()
glBindBuffer(GL_ARRAY_BUFFER, hVBO);
glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(float), NULL, GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Also allocate the VAO IDs
assert(hVAOIDLight == 0);
assert(hVAOIDNoLight == 0);
hVAOIDLight = pGL->GenVAOID();
hVAOIDNoLight = pGL->GenVAOID();
return true;
}
@ -1033,13 +1009,7 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh
}
if(ShaderCall.AllocTexUnit(C4LRU_MaterialTex))
{
// Decide which mip-map level to use
double z = 0.5; int iMM = 0;
while(pGL->Zoom < z * ::Game.C4S.Landscape.MaterialZoom && iMM + 1 <C4LR_MipMapCount)
{ z /= 2; iMM++; }
glBindTexture(GL_TEXTURE_3D, hMaterialTexture[iMM]);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D_ARRAY, hMaterialTexture);
}
if(ShaderCall.AllocTexUnit(C4LRU_MatMapTex))
{
@ -1120,27 +1090,30 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh
glBindBuffer(GL_ARRAY_BUFFER, hVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, nFloats * sizeof(float), vtxData);
// Setup state
glEnableVertexAttribArray(shader->GetAttribute(C4LRA_Position));
glEnableVertexAttribArray(shader->GetAttribute(C4LRA_LandscapeTexCoord));
glVertexAttribPointer(shader->GetAttribute(C4LRA_Position), 2, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(shader->GetAttribute(C4LRA_LandscapeTexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(8 * sizeof(float)));
if (Light)
// Bind VAO
unsigned int vaoid = Light ? hVAOIDLight : hVAOIDNoLight;
GLuint vao;
const bool has_vao = pGL->GetVAO(vaoid, vao);
glBindVertexArray(vao);
if (!has_vao)
{
glEnableVertexAttribArray(shader->GetAttribute(C4LRA_LightTexCoord));
glVertexAttribPointer(shader->GetAttribute(C4LRA_LightTexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(16 * sizeof(float)));
// Setup state
glEnableVertexAttribArray(shader->GetAttribute(C4LRA_Position));
glEnableVertexAttribArray(shader->GetAttribute(C4LRA_LandscapeTexCoord));
if (Light)
glEnableVertexAttribArray(shader->GetAttribute(C4LRA_LightTexCoord));
glVertexAttribPointer(shader->GetAttribute(C4LRA_Position), 2, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(shader->GetAttribute(C4LRA_LandscapeTexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(8 * sizeof(float)));
if (Light)
glVertexAttribPointer(shader->GetAttribute(C4LRA_LightTexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(16 * sizeof(float)));
}
// Do the blit
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Reset state
glDisableVertexAttribArray(shader->GetAttribute(C4LRA_Position));
glDisableVertexAttribArray(shader->GetAttribute(C4LRA_LandscapeTexCoord));
if (Light)
glDisableVertexAttribArray(shader->GetAttribute(C4LRA_LightTexCoord));
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
ShaderCall.Finish();

View File

@ -72,9 +72,6 @@ const int C4LR_BytesPerPx = 3;
const int C4LR_BytesPerSurface = 4;
const int C4LR_SurfaceCount = (C4LR_ByteCount + C4LR_BytesPerSurface - 1) / C4LR_BytesPerSurface;
// How many mip-map levels should be used at maximum?
const int C4LR_MipMapCount = 6;
class C4Landscape; class C4TextureMap;
class C4LandscapeRender
@ -122,10 +119,13 @@ private:
static const char *UniformNames[];
// VBO for landscape vertex data
GLuint hVBO;
// VAO IDs for rendering landscape w/ and w/o light
unsigned int hVAOIDNoLight;
unsigned int hVAOIDLight;
// 3D texture of material textures
GLuint hMaterialTexture[C4LR_MipMapCount];
// material texture positions in 3D texture
// 2D texture array of material textures
GLuint hMaterialTexture;
// material texture positions in texture array
std::vector<StdCopyStrBuf> MaterialTextureMap;
// depth of material texture in layers
int32_t iMaterialTextureDepth;

View File

@ -38,7 +38,7 @@ bool C4LandscapeRenderClassic::ReInit(int32_t iWidth, int32_t iHeight)
delete Surface32; Surface32 = NULL;
Surface32 = new C4Surface();
// without shaders, the FoW is only as detailed as the landscape has tiles.
if (!Surface32->Create(iWidth, iHeight, false, 0, 0))
if (!Surface32->Create(iWidth, iHeight))
return false;
// Safe back info
this->iWidth = iWidth;

View File

@ -967,16 +967,6 @@ bool C4ParticleChunk::Exec(C4Object *obj, float timeDelta)
return particleCount > 0;
}
#if defined(__APPLE__)
#undef glGenVertexArrays
#undef glBindVertexArray
#undef glDeleteVertexArrays
#define glGenVertexArrays glGenVertexArraysAPPLE
#define glBindVertexArray glBindVertexArrayAPPLE
#define glDeleteVertexArrays glDeleteVertexArraysAPPLE
#endif
void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call, int texUnit, const StdProjectionMatrix& modelview)
{
if (particleCount == 0) return;
@ -1021,66 +1011,46 @@ void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call,
glObjectLabel(GL_BUFFER, drawingDataVertexBufferObject, -1, "<particles>/VBO");
#endif
// generate new vertex arrays object
if (!Particles.useVAOWorkaround)
{
glGenVertexArrays(1, &drawingDataVertexArraysObject);
assert (drawingDataVertexArraysObject != 0 && "Could not generate OpenGL vertex arrays object.");
// set up the vertex array structure once
glBindVertexArray(drawingDataVertexArraysObject);
#ifdef GL_KHR_debug
if (glObjectLabel)
glObjectLabel(GL_VERTEX_ARRAY, drawingDataVertexArraysObject, -1, "<particles>/VAO");
#endif
glEnableVertexAttribArray(call.GetAttribute(C4SSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4SSA_Color));
glEnableVertexAttribArray(call.GetAttribute(C4SSA_TexCoord));
glVertexAttribPointer(call.GetAttribute(C4SSA_Position), 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, x)));
glVertexAttribPointer(call.GetAttribute(C4SSA_TexCoord), 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, u)));
glVertexAttribPointer(call.GetAttribute(C4SSA_Color), 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, r)));
glBindVertexArray(0);
}
// generate new VAO ID
drawingDataVertexArraysObject = pGL->GenVAOID();
assert (drawingDataVertexArraysObject != 0 && "Could not generate a VAO ID.");
}
assert ((Particles.useVAOWorkaround || drawingDataVertexArraysObject != 0) && "No vertex arrays object has been created yet.");
assert ((drawingDataVertexBufferObject != 0) && "No buffer object has been created yet.");
// bind the VBO and push the new vertex data
// this has to be done before binding the vertex arrays object
// Push the new vertex data
glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(C4Particle::DrawingData::Vertex) * particleCount, &vertexCoordinates[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// bind VAO and set correct state
if (!Particles.useVAOWorkaround)
{
glBindVertexArray(drawingDataVertexArraysObject);
}
else
// set up the vertex array structure
GLuint vao;
const bool has_vao = pGL->GetVAO(drawingDataVertexArraysObject, vao);
glBindVertexArray(vao);
assert ((drawingDataVertexBufferObject != 0) && "No buffer object has been created yet.");
assert ((drawingDataVertexArraysObject != 0) && "No vertex arrays object has been created yet.");
if (!has_vao)
{
glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ::Particles.GetIBO());
#ifdef GL_KHR_debug
if (glObjectLabel)
glObjectLabel(GL_VERTEX_ARRAY, vao, -1, "<particles>/VAO");
#endif
glEnableVertexAttribArray(call.GetAttribute(C4SSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4SSA_Color));
glEnableVertexAttribArray(call.GetAttribute(C4SSA_TexCoord));
glVertexAttribPointer(call.GetAttribute(C4SSA_Position), 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, x)));
glVertexAttribPointer(call.GetAttribute(C4SSA_TexCoord), 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, u)));
glVertexAttribPointer(call.GetAttribute(C4SSA_Color), 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, r)));
}
if (!Particles.usePrimitiveRestartIndexWorkaround)
{
glDrawElements(GL_TRIANGLE_STRIP, static_cast<GLsizei> (5 * particleCount), GL_UNSIGNED_INT, ::Particles.GetPrimitiveRestartArray());
}
else
{
glMultiDrawElements(GL_TRIANGLE_STRIP, ::Particles.GetMultiDrawElementsCountArray(), GL_UNSIGNED_INT, const_cast<const GLvoid**>(::Particles.GetMultiDrawElementsIndexArray()), static_cast<GLsizei> (particleCount));
}
glDrawElements(GL_TRIANGLE_STRIP, static_cast<GLsizei> (5 * particleCount), GL_UNSIGNED_INT, 0);
// reset buffer data
if (!Particles.useVAOWorkaround)
{
glBindVertexArray(0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
bool C4ParticleChunk::IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t _attachment) const
@ -1093,7 +1063,7 @@ void C4ParticleChunk::ClearBufferObjects()
if (drawingDataVertexBufferObject != 0) // the value 0 as a buffer index is reserved and will never be returned by glGenBuffers
glDeleteBuffers(1, &drawingDataVertexBufferObject);
if (drawingDataVertexArraysObject != 0)
glDeleteVertexArrays(1, &drawingDataVertexArraysObject);
pGL->FreeVAOID(drawingDataVertexArraysObject);
drawingDataVertexArraysObject = 0;
drawingDataVertexBufferObject = 0;
@ -1158,11 +1128,8 @@ void C4ParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
pDraw->DeactivateBlitModulation();
pDraw->ResetBlitMode();
if (!Particles.usePrimitiveRestartIndexWorkaround)
{
glPrimitiveRestartIndex(0xffffffff);
glEnable(GL_PRIMITIVE_RESTART);
}
glPrimitiveRestartIndex(0xffffffff);
glEnable(GL_PRIMITIVE_RESTART);
// enable shader
C4ShaderCall call(pGL->GetSpriteShader(true, false, false));
@ -1176,16 +1143,6 @@ void C4ParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
// texture unit. Will be used for each particle chunk to bind
// their texture to this unit.
const GLint texUnit = call.AllocTexUnit(C4SSU_BaseTex);
// Texture coordinates are always associated to texture unit 0, since
// there is only one set of texture coordinates
glClientActiveTexture(GL_TEXTURE0);
if (Particles.useVAOWorkaround)
{
glEnableVertexAttribArray(call.GetAttribute(C4SSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4SSA_Color));
glEnableVertexAttribArray(call.GetAttribute(C4SSA_TexCoord));
}
accessMutex.Enter();
@ -1206,17 +1163,7 @@ void C4ParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
accessMutex.Leave();
if (Particles.useVAOWorkaround)
{
glDisableVertexAttribArray(call.GetAttribute(C4SSA_Position));
glDisableVertexAttribArray(call.GetAttribute(C4SSA_Color));
glDisableVertexAttribArray(call.GetAttribute(C4SSA_TexCoord));
}
if (!Particles.usePrimitiveRestartIndexWorkaround)
{
glDisable(GL_PRIMITIVE_RESTART);
}
glDisable(GL_PRIMITIVE_RESTART);
}
void C4ParticleList::Clear()
@ -1286,8 +1233,8 @@ C4ParticleSystem::C4ParticleSystem() : frameCounterAdvancedEvent(false)
{
currentSimulationTime = 0;
globalParticles = 0;
usePrimitiveRestartIndexWorkaround = false;
useVAOWorkaround = false;
ibo = 0;
ibo_size = 0;
}
C4ParticleSystem::~C4ParticleSystem()
@ -1296,30 +1243,6 @@ C4ParticleSystem::~C4ParticleSystem()
calculationThread.SignalStop();
CalculateNextStep();
for (std::vector<uint32_t *>::iterator iter = multiDrawElementsIndexArray.begin(); iter != multiDrawElementsIndexArray.end(); ++iter)
delete[] (*iter);
}
void C4ParticleSystem::DoInit()
{
// we use features that are only supported from 3.1 upwards. Check whether the graphics card supports that and - if not - use workarounds
if (!GLEW_VERSION_3_1 || (glPrimitiveRestartIndex == 0))
{
usePrimitiveRestartIndexWorkaround = true;
LogSilent("WARNING (particle system): Your graphics card does not support glPrimitiveRestartIndex - a (slower) fallback will be used!");
}
assert (glGenBuffers != 0 && "Your graphics card does not seem to support buffer objects.");
useVAOWorkaround = false;
#ifndef USE_WIN32_WINDOWS
// Every window in developers' mode has an own OpenGL context at the moment. Certain objects are not shared between contexts.
// In that case we can just use the slower workaround without VAOs to allow the developer to view particles in every viewport.
// The best solution would obviously be to make all windows use a single OpenGL context. This has to be considered as a workaround.
if (Application.isEditor)
useVAOWorkaround = true;
#endif
}
void C4ParticleSystem::ExecuteCalculation()
@ -1345,8 +1268,6 @@ void C4ParticleSystem::ExecuteCalculation()
particleListAccessMutex.Leave();
}
}
#else // ifdef USE_CONSOLE
void C4ParticleSystem::DoInit() {}
#endif
C4ParticleList *C4ParticleSystem::GetNewParticleList(C4Object *forObject)
@ -1492,59 +1413,31 @@ void C4ParticleSystem::Create(C4ParticleDef *of_def, C4ParticleValueProvider &x,
void C4ParticleSystem::PreparePrimitiveRestartIndices(uint32_t forAmount)
{
if (!usePrimitiveRestartIndexWorkaround)
// prepare array with indices, separated by special primitive restart index
const uint32_t PRI = 0xffffffff;
size_t neededAmount = 5 * forAmount;
if (ibo == 0) glGenBuffers(1, &ibo);
if (ibo_size < neededAmount * sizeof(GLuint))
{
// prepare array with indices, separated by special primitive restart index
const uint32_t PRI = 0xffffffff;
size_t neededAmount = 5 * forAmount;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
if (primitiveRestartIndices.size() < neededAmount)
std::vector<GLuint> ibo_data;
ibo_data.reserve(neededAmount);
unsigned int index = 0;
for (unsigned int i = 0; i < neededAmount; ++i)
{
uint32_t oldValue = 0;
if (primitiveRestartIndices.size() > 2)
{
oldValue = primitiveRestartIndices[primitiveRestartIndices.size()-1];
if (oldValue == PRI)
oldValue = primitiveRestartIndices[primitiveRestartIndices.size()-2];
++oldValue;
}
size_t oldSize = primitiveRestartIndices.size();
primitiveRestartIndices.resize(neededAmount);
for (size_t i = oldSize; i < neededAmount; ++i)
{
if (((i+1) % 5 == 0) && (i != 0))
{
primitiveRestartIndices[i] = PRI;
}
else
{
primitiveRestartIndices[i] = oldValue++;
}
}
}
}
else
{
// prepare arrays for glMultiDrawElements
if (multiDrawElementsCountArray.size() <= forAmount)
{
multiDrawElementsCountArray.resize(forAmount, 4);
if ((i+1) % 5 == 0)
ibo_data.push_back(PRI);
else
ibo_data.push_back(index++);
}
if (multiDrawElementsIndexArray.size() <= forAmount)
{
uint32_t oldSize = multiDrawElementsIndexArray.size();
multiDrawElementsIndexArray.resize(forAmount);
for (; oldSize < forAmount; ++oldSize)
{
multiDrawElementsIndexArray[oldSize] = new uint32_t[4];
for (uint32_t i = 0; i < 4; ++i)
multiDrawElementsIndexArray[oldSize][i] = 4 * oldSize + i;
}
}
ibo_size = neededAmount * sizeof(GLuint);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ibo_size, &ibo_data[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
#endif
@ -1552,6 +1445,9 @@ void C4ParticleSystem::PreparePrimitiveRestartIndices(uint32_t forAmount)
void C4ParticleSystem::Clear()
{
#ifndef USE_CONSOLE
if (ibo != 0) glDeleteBuffers(1, &ibo);
ibo = 0; ibo_size = 0;
currentSimulationTime = 0;
ClearAllParticles();
#endif

View File

@ -343,7 +343,7 @@ private:
// OpenGL optimizations
GLuint drawingDataVertexBufferObject;
GLuint drawingDataVertexArraysObject;
unsigned int drawingDataVertexArraysObject;
void ClearBufferObjects();
// delete the particle at indexTo. If possible, replace it with the particle at indexFrom to keep the particles tighly packed
@ -445,10 +445,8 @@ class C4ParticleSystem
private:
// contains an array with indices for vertices, separated by a primitive restart index
std::vector<uint32_t> primitiveRestartIndices;
// these are fallbacks for if primitiveRestartIndex is not supported by the graphics card
std::vector<GLsizei> multiDrawElementsCountArray;
std::vector<uint32_t *> multiDrawElementsIndexArray;
GLuint ibo;
size_t ibo_size;
std::list<C4ParticleList> particleLists;
CStdCSec particleListAccessMutex;
@ -475,7 +473,6 @@ public:
frameCounterAdvancedEvent.Set();
#endif
}
void DoInit();
// resets the internal state of the particle system and unloads all definitions
void Clear();
void DrawGlobalParticles(C4TargetFacet cgo)
@ -502,17 +499,9 @@ public:
C4ParticleSystemDefinitionList definitions;
#ifndef USE_CONSOLE
// on some graphics card, glPrimitiveRestartIndex might not be supported
bool usePrimitiveRestartIndexWorkaround;
GLsizei *GetMultiDrawElementsCountArray() { return &multiDrawElementsCountArray[0]; }
GLvoid **GetMultiDrawElementsIndexArray() { return reinterpret_cast<GLvoid**> (&multiDrawElementsIndexArray[0]); }
// if true, OpenGL VAOs will not be used (instead the slower direct calls will be made)
bool useVAOWorkaround;
// usually, the following methods are used for drawing
GLuint GetIBO() const { return ibo; }
void PreparePrimitiveRestartIndices(uint32_t forSize);
void *GetPrimitiveRestartArray() { return (void*)&primitiveRestartIndices[0]; }
// creates a new particle
void Create(C4ParticleDef *of_def, C4ParticleValueProvider &x, C4ParticleValueProvider &y, C4ParticleValueProvider &speedX, C4ParticleValueProvider &speedY, C4ParticleValueProvider &lifetime, C4PropList *properties, int amount = 1, C4Object *object=NULL);

View File

@ -34,9 +34,9 @@ C4Shader *C4FoW::GetFramebufShader()
// this is about how to utilise old frame buffer data in the lights texture.
// Or put in other words: This shader is responsible for fading lights out.
const char* FramebufVertexShader =
"attribute vec2 oc_Position;\n"
"attribute vec2 oc_TexCoord;\n"
"varying vec2 texcoord;\n"
"in vec2 oc_Position;\n"
"in vec2 oc_TexCoord;\n"
"out vec2 texcoord;\n"
"uniform mat4 projectionMatrix;\n"
"\n"
"slice(position)\n"
@ -50,12 +50,13 @@ C4Shader *C4FoW::GetFramebufShader()
"}";
const char* FramebufFragmentShader =
"varying vec2 texcoord;\n"
"in vec2 texcoord;\n"
"uniform sampler2D tex;\n"
"out vec4 fragColor;\n"
"\n"
"slice(color)\n"
"{\n"
" gl_FragColor = texture2D(tex, texcoord);\n"
" fragColor = texture(tex, texcoord);\n"
"}";
FramebufShader.AddVertexSlices("built-in FoW framebuf shader", FramebufVertexShader);
@ -90,9 +91,9 @@ C4Shader *C4FoW::GetRenderShader()
{
// Create the render shader. Fairly simple pass-through.
const char* RenderVertexShader =
"attribute vec2 oc_Position;\n"
"attribute vec4 oc_Color;\n"
"varying vec4 vtxColor;\n"
"in vec2 oc_Position;\n"
"in vec4 oc_Color;\n"
"out vec4 vtxColor;\n"
"uniform mat4 projectionMatrix;\n"
"uniform vec2 vertexOffset;\n"
"\n"
@ -107,11 +108,12 @@ C4Shader *C4FoW::GetRenderShader()
"}";
const char* RenderFragmentShader =
"varying vec4 vtxColor;\n"
"in vec4 vtxColor;\n"
"out vec4 fragColor;\n"
"\n"
"slice(color)\n"
"{\n"
" gl_FragColor = vtxColor;\n"
" fragColor = vtxColor;\n"
"}";
RenderShader.AddVertexSlices("built-in FoW render shader", RenderVertexShader);

View File

@ -109,14 +109,20 @@ void C4FoWDrawTriangulator::Reset()
}
C4FoWDrawLightTextureStrategy::C4FoWDrawLightTextureStrategy(const C4FoWLight* light)
: light(light), region(NULL), vbo_size(0)
: light(light), region(NULL), vbo_size(0), ibo_size(0)
{
glGenBuffers(1, &vbo);
glGenBuffers(2, bo);
vaoids[0] = pGL->GenVAOID();
vaoids[1] = pGL->GenVAOID();
vaoids[2] = pGL->GenVAOID();
}
C4FoWDrawLightTextureStrategy::~C4FoWDrawLightTextureStrategy()
{
glDeleteBuffers(1, &vbo);
glDeleteBuffers(2, bo);
pGL->FreeVAOID(vaoids[2]);
pGL->FreeVAOID(vaoids[1]);
pGL->FreeVAOID(vaoids[0]);
}
void C4FoWDrawLightTextureStrategy::Begin(const C4FoWRegion* regionPar)
@ -129,6 +135,9 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
// If we have nothing to draw (e.g. directly after initialization), abort early.
if (vertices.empty()) return;
const GLuint vbo = bo[0];
const GLuint ibo = bo[1];
// Upload vertices
glBindBuffer(GL_ARRAY_BUFFER, vbo);
if (vbo_size < vertices.size())
@ -140,6 +149,20 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
{
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(Vertex), &vertices[0]);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Upload indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
if (ibo_size < triangulator.GetNIndices())
{
glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangulator.GetNIndices() * 3 * sizeof(GLuint), triangulator.GetIndices(), GL_DYNAMIC_DRAW);
ibo_size = triangulator.GetNIndices();
}
else
{
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, triangulator.GetNIndices() * 3 * sizeof(GLuint), triangulator.GetIndices());
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// Region dimensions
const float width = region->getSurfaceWidth();
@ -154,10 +177,18 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
glScissor(0, height, width, height);
// Setup state for 1st pass
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r1)));
GLuint vao1, vao2, vao3;
const bool has_vao1 = pGL->GetVAO(vaoids[0], vao1);
glBindVertexArray(vao1);
if (!has_vao1)
{
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r1)));
}
// Set up blend equation, see C4FoWDrawLightTextureStrategy::DrawVertex
// for details.
@ -165,31 +196,51 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
// Render 1st pass
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, triangulator.GetIndices());
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
// Prepare state for 2nd pass
glBlendFunc(GL_ONE, GL_ONE);
//glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r2)));
const bool has_vao2 = pGL->GetVAO(vaoids[1], vao2);
glBindVertexArray(vao2);
if (!has_vao2)
{
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r2)));
}
// Render 2nd pass
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, triangulator.GetIndices());
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
// Prepare state for 3rd pass (color pass)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
glScissor(0, 0, width, height);
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r3)));
y_offset[1] = height;
call.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
const bool has_vao3 = pGL->GetVAO(vaoids[2], vao3);
glBindVertexArray(vao3);
if (!has_vao3)
{
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r3)));
}
// Render 3rd pass
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, triangulator.GetIndices());
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
// Reset GL state
glDisableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
glDisableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glDisable(GL_SCISSOR_TEST);
// Assume the capacity stays the same:
@ -283,14 +334,16 @@ void C4FoWDrawLightTextureStrategy::DrawLightVertex(float x, float y)
}
C4FoWDrawWireframeStrategy::C4FoWDrawWireframeStrategy(const C4FoWLight* light, const C4TargetFacet *screen) :
light(light), screen(screen), vbo_size(0)
light(light), screen(screen), vbo_size(0), ibo_size(0)
{
glGenBuffers(1, &vbo);
glGenBuffers(2, bo);
vaoid = pGL->GenVAOID();
}
C4FoWDrawWireframeStrategy::~C4FoWDrawWireframeStrategy()
{
glDeleteBuffers(1, &vbo);
glDeleteBuffers(2, bo);
pGL->FreeVAOID(vaoid);
}
void C4FoWDrawWireframeStrategy::Begin(const C4FoWRegion* region)
@ -302,6 +355,9 @@ void C4FoWDrawWireframeStrategy::End(C4ShaderCall& call)
// If we have nothing to draw (e.g. directly after initialization), abort early.
if (vertices.empty()) return;
const GLuint vbo = bo[0];
const GLuint ibo = bo[1];
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Upload vertices
@ -316,22 +372,40 @@ void C4FoWDrawWireframeStrategy::End(C4ShaderCall& call)
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(Vertex), &vertices[0]);
}
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
// Upload indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
if (ibo_size < triangulator.GetNIndices())
{
glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangulator.GetNIndices() * 3 * sizeof(GLuint), triangulator.GetIndices(), GL_STREAM_DRAW);
ibo_size = triangulator.GetNIndices();
}
else
{
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, triangulator.GetNIndices() * 3 * sizeof(GLuint), triangulator.GetIndices());
}
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r)));
GLuint vao;
const bool has_vao = pGL->GetVAO(vaoid, vao);
glBindVertexArray(vao);
if (!has_vao)
{
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r)));
}
// Set Y offset for vertex
const float y_offset[] = { 0.0f, 0.0f };
call.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, triangulator.GetIndices());
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
// Reset GL state
glDisableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
glDisableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Assume the capacity stays the same:

View File

@ -139,9 +139,11 @@ private:
float r3, g3, b3, a3; // color for third pass
};
GLuint vbo;
GLuint bo[2];
std::vector<Vertex> vertices;
unsigned int vbo_size;
unsigned int ibo_size;
unsigned int vaoids[3]; // Three VAOs for the three passes
};
/** This draw strategy is the debug draw strategy (press Ctrl+F7,...) that
@ -170,9 +172,12 @@ private:
const C4FoWLight* light;
const C4TargetFacet* screen;
GLuint vbo;
GLuint bo[2];
GLuint ibo;
std::vector<Vertex> vertices;
unsigned int vbo_size;
unsigned int ibo_size;
unsigned int vaoid;
};
#endif

View File

@ -21,7 +21,7 @@ C4FoWRegion::C4FoWRegion(C4FoW *pFoW, C4Player *pPlayer)
: pFoW(pFoW)
, pPlayer(pPlayer)
#ifndef USE_CONSOLE
, hFrameBufDraw(0), hFrameBufRead(0), hVBO(0)
, hFrameBufDraw(0), hFrameBufRead(0), hVBO(0), vaoid(0)
#endif
, Region(0,0,0,0), OldRegion(0,0,0,0)
, pSurface(new C4Surface), pBackSurface(new C4Surface)
@ -33,13 +33,17 @@ C4FoWRegion::~C4FoWRegion()
{
#ifndef USE_CONSOLE
if (hFrameBufDraw) {
glDeleteFramebuffersEXT(1, &hFrameBufDraw);
glDeleteFramebuffersEXT(1, &hFrameBufRead);
glDeleteFramebuffers(1, &hFrameBufDraw);
glDeleteFramebuffers(1, &hFrameBufRead);
}
if (hVBO) {
glDeleteBuffers(1, &hVBO);
}
if (vaoid) {
pGL->FreeVAOID(vaoid);
}
#endif
}
@ -67,9 +71,9 @@ bool C4FoWRegion::BindFramebuf()
// Create the new surfaces
std::unique_ptr<C4Surface> pNewSurface(new C4Surface);
std::unique_ptr<C4Surface> pNewBackSurface(new C4Surface);
if (!pNewSurface->Create(iWdt, iHgt, false, 0, 0))
if (!pNewSurface->Create(iWdt, iHgt))
return false;
if (!pNewBackSurface->Create(iWdt, iHgt, false, 0, 0))
if (!pNewBackSurface->Create(iWdt, iHgt))
return false;
// Copy over old content. This avoids flicker in already
@ -124,28 +128,28 @@ bool C4FoWRegion::BindFramebuf()
// Generate frame buffer object
if (!hFrameBufDraw)
{
glGenFramebuffersEXT(1, &hFrameBufDraw);
glGenFramebuffersEXT(1, &hFrameBufRead);
glGenFramebuffers(1, &hFrameBufDraw);
glGenFramebuffers(1, &hFrameBufRead);
}
// Bind current texture to frame buffer
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, hFrameBufDraw);
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, hFrameBufRead);
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hFrameBufDraw);
glBindFramebuffer(GL_READ_FRAMEBUFFER, hFrameBufRead);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
pSurface->textures[0].texName, 0);
if (!pBackSurface->textures.empty())
glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
pBackSurface->textures[0].texName, 0);
// Check status, unbind if something was amiss
GLenum status1 = glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT),
status2 = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
if (status1 != GL_FRAMEBUFFER_COMPLETE_EXT ||
(pBackSurface && status2 != GL_FRAMEBUFFER_COMPLETE_EXT))
GLenum status1 = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER),
status2 = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
if (status1 != GL_FRAMEBUFFER_COMPLETE ||
(pBackSurface && status2 != GL_FRAMEBUFFER_COMPLETE))
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return false;
}
#endif
@ -283,6 +287,9 @@ bool C4FoWRegion::Render(const C4TargetFacet *pOnScreen)
glGenBuffers(1, &hVBO);
glBindBuffer(GL_ARRAY_BUFFER, hVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vtxData), vtxData, GL_STREAM_DRAW);
assert(vaoid == 0);
vaoid = pGL->GenVAOID();
}
else
{
@ -301,22 +308,27 @@ bool C4FoWRegion::Render(const C4TargetFacet *pOnScreen)
brightBlend = 1.0f / 16.0f; // Intensity more slowly
glBlendColor(0.0f,normalBlend,normalBlend,brightBlend);
glEnableVertexAttribArray(pShader->GetAttribute(C4FoWFSA_Position));
glEnableVertexAttribArray(pShader->GetAttribute(C4FoWFSA_TexCoord));
glVertexAttribPointer(pShader->GetAttribute(C4FoWFSA_Position), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(8 * sizeof(float)));
glVertexAttribPointer(pShader->GetAttribute(C4FoWFSA_TexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(0));
GLuint vao;
const bool has_vao = pGL->GetVAO(vaoid, vao);
glBindVertexArray(vao);
if (!has_vao)
{
glEnableVertexAttribArray(pShader->GetAttribute(C4FoWFSA_Position));
glEnableVertexAttribArray(pShader->GetAttribute(C4FoWFSA_TexCoord));
glVertexAttribPointer(pShader->GetAttribute(C4FoWFSA_Position), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(8 * sizeof(float)));
glVertexAttribPointer(pShader->GetAttribute(C4FoWFSA_TexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(0));
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(pShader->GetAttribute(C4FoWFSA_Position));
glDisableVertexAttribArray(pShader->GetAttribute(C4FoWFSA_TexCoord));
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
Call.Finish();
}
// Done!
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
pDraw->RestorePrimaryClipper();
OldRegion = Region;

View File

@ -39,6 +39,7 @@ private:
#ifndef USE_CONSOLE
GLuint hFrameBufDraw, hFrameBufRead;
GLuint hVBO;
unsigned int vaoid;
#endif
public:

View File

@ -15,6 +15,7 @@
*/
#include "C4Include.h"
#include <C4DrawGL.h>
#include <StdMesh.h>
#include <algorithm>
@ -545,14 +546,14 @@ std::vector<int> StdMeshSkeleton::GetMatchingBones(const StdMeshSkeleton& child_
}
StdSubMesh::StdSubMesh() :
Material(NULL), buffer_offset(0)
Material(NULL), vertex_buffer_offset(0), index_buffer_offset(0)
{
}
StdMesh::StdMesh() :
Skeleton(new StdMeshSkeleton)
#ifndef USE_CONSOLE
, vbo(0)
, vbo(0), ibo(0), vaoid(0)
#endif
{
BoundingBox.x1 = BoundingBox.y1 = BoundingBox.z1 = 0.0f;
@ -563,8 +564,12 @@ StdMesh::StdMesh() :
StdMesh::~StdMesh()
{
#ifndef USE_CONSOLE
if (ibo)
glDeleteBuffers(1, &ibo);
if (vbo)
glDeleteBuffers(1, &vbo);
if (vaoid)
pGL->FreeVAOID(vaoid);
#endif
}
@ -574,6 +579,11 @@ void StdMesh::PostInit()
// Order submeshes so that opaque submeshes come before non-opaque ones
std::sort(SubMeshes.begin(), SubMeshes.end(), StdMeshSubMeshVisibilityCmpPred());
UpdateVBO();
UpdateIBO();
// Allocate a VAO ID as well
assert(vaoid == 0);
vaoid = pGL->GenVAOID();
#endif
}
@ -621,7 +631,7 @@ void StdMesh::UpdateVBO()
for (auto &submesh : SubMeshes)
{
// Store the offset, so the render code can use it later
submesh.buffer_offset = cursor - buffer;
submesh.vertex_buffer_offset = cursor - buffer;
if (submesh.Vertices.empty()) continue;
size_t vertices_size = sizeof(submesh.Vertices[0]) * submesh.Vertices.size();
@ -632,6 +642,28 @@ void StdMesh::UpdateVBO()
// Unbind the buffer so following rendering calls do not use it
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void StdMesh::UpdateIBO()
{
assert(ibo == 0);
if (ibo != 0)
glDeleteBuffers(1, &ibo);
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
size_t total_faces = 0;
for (auto &submesh : SubMeshes)
total_faces += submesh.GetNumFaces();
glBufferData(GL_ELEMENT_ARRAY_BUFFER, total_faces * 3 * sizeof(GLuint), NULL, GL_STATIC_DRAW);
size_t offset = 0;
for (auto &submesh : SubMeshes)
{
submesh.index_buffer_offset = offset * 3 * sizeof(GLuint);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, submesh.index_buffer_offset, submesh.GetNumFaces() * 3 * sizeof(GLuint), &submesh.Faces[0]);
offset += submesh.GetNumFaces();
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
#endif
StdSubMeshInstance::StdSubMeshInstance(StdMeshInstance& instance, const StdSubMesh& submesh, float completion):
@ -1055,6 +1087,9 @@ StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
BoneTransforms(Mesh->GetSkeleton().GetNumBones(), StdMeshMatrix::Identity()),
SubMeshInstances(Mesh->GetNumSubMeshes()), AttachParent(NULL),
BoneTransformsDirty(false)
#ifndef USE_CONSOLE
, ibo(0), vaoid(0)
#endif
{
// Create submesh instances
for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
@ -1069,6 +1104,11 @@ StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
StdMeshInstance::~StdMeshInstance()
{
#ifndef USE_CONSOLE
if (ibo) glDeleteBuffers(1, &ibo);
if (vaoid) pGL->FreeVAOID(vaoid);
#endif
// If we are attached then detach from parent
if (AttachParent)
AttachParent->Parent->DetachMesh(AttachParent->Number);
@ -1094,6 +1134,9 @@ void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
SubMeshInstances[i]->SetFaceOrdering(*this, Mesh->GetSubMesh(i), ordering);
// Faces have been reordered: upload new order to GPU
UpdateIBO();
// Update attachments (only own meshes for now... others might be displayed both attached and non-attached...)
// still not optimal.
for (AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
@ -1108,6 +1151,9 @@ void StdMeshInstance::SetFaceOrderingForClrModulation(uint32_t clrmod)
for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
SubMeshInstances[i]->SetFaceOrderingForClrModulation(*this, Mesh->GetSubMesh(i), clrmod);
// Faces have been reordered: upload new order to GPU
UpdateIBO();
// Update attachments (only own meshes for now... others might be displayed both attached and non-attached...)
// still not optimal.
for (AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
@ -1125,6 +1171,9 @@ void StdMeshInstance::SetCompletion(float completion)
// full pool.
for(unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
SubMeshInstances[i]->LoadFacesForCompletion(*this, Mesh->GetSubMesh(i), completion);
// Faces have been reordered: upload new order to GPU
UpdateIBO();
#endif
}
@ -1534,6 +1583,9 @@ void StdMeshInstance::ReorderFaces(StdMeshMatrix* global_trans)
}
// TODO: Also reorder submeshes, attached meshes and include AttachTransformation for attached meshes...
// Faces have been reordered: upload new order to GPU
UpdateIBO();
#endif
}
@ -1860,3 +1912,69 @@ void StdMeshInstance::SetBoneTransformsDirty(bool value)
}
}
}
#ifndef USE_CONSOLE
void StdMeshInstance::UpdateIBO()
{
// First, find out whether we have fixed face ordering or not
bool all_submeshes_fixed = true;
for (StdSubMeshInstance* inst : SubMeshInstances)
{
all_submeshes_fixed = (inst->GetFaceOrdering() == StdSubMeshInstance::FO_Fixed);
if (!all_submeshes_fixed) break;
// If true, submesh is 100% complete
all_submeshes_fixed = inst->GetNumFaces() == inst->GetSubMesh().GetNumFaces();
if (!all_submeshes_fixed) break;
}
// If the face ordering is fixed, then we don't need a custom
// IBO. This is typically the case for all meshes without transparency
// and 100% completion.
if (all_submeshes_fixed)
{
if (ibo) glDeleteBuffers(1, &ibo);
if (vaoid) pGL->FreeVAOID(vaoid);
ibo = 0; vaoid = 0;
}
else
{
// We have a custom face ordering, or we render only a subset
// of our faces. Create a custom IBO and upload the index
// data.
if (ibo == 0)
{
// This is required, because the IBO binding is part
// of the VAO state. If we create a new IBO we cannot
// keep using any old VAO. But we always create and
// destroy them together, so we can assert here.
assert(vaoid == 0);
size_t total_faces = 0;
for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
total_faces += Mesh->GetSubMesh(i).GetNumFaces();
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
// TODO: Optimize mode. In many cases this is still fairly static.
glBufferData(GL_ELEMENT_ARRAY_BUFFER, total_faces * 3 * sizeof(GLuint), NULL, GL_STREAM_DRAW);
}
else
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
}
for (StdSubMeshInstance* inst : SubMeshInstances)
{
assert(inst->GetNumFaces() <= inst->GetSubMesh().GetNumFaces());
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, inst->GetSubMesh().GetOffsetInIBO(), inst->GetNumFaces() * 3 * sizeof(GLuint), &inst->Faces[0]);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
if (vaoid == 0)
vaoid = pGL->GenVAOID();
}
}
#endif

View File

@ -165,14 +165,16 @@ public:
const StdMeshMaterial& GetMaterial() const { return *Material; }
// Return the offset into the backing vertex buffer where this SubMesh's data starts
size_t GetOffsetInBuffer() const { return buffer_offset; }
size_t GetOffsetInVBO() const { return vertex_buffer_offset; }
size_t GetOffsetInIBO() const { return index_buffer_offset; }
private:
StdSubMesh();
std::vector<Vertex> Vertices; // Empty if we use shared vertices
std::vector<StdMeshFace> Faces;
size_t buffer_offset;
size_t vertex_buffer_offset;
size_t index_buffer_offset;
const StdMeshMaterial* Material;
};
@ -203,6 +205,8 @@ public:
#ifndef USE_CONSOLE
GLuint GetVBO() const { return vbo; }
GLuint GetIBO() const { return ibo; }
unsigned int GetVAOID() const { return vaoid; }
#endif
void SetLabel(const std::string &label) { Label = label; }
@ -210,7 +214,10 @@ public:
private:
#ifndef USE_CONSOLE
GLuint vbo;
GLuint ibo;
unsigned int vaoid;
void UpdateVBO();
void UpdateIBO();
#endif
StdMesh(const StdMesh& other); // non-copyable
@ -265,7 +272,7 @@ protected:
const StdSubMesh *base;
// Faces sorted according to current face ordering
std::vector<StdMeshFace> Faces; // TODO: Indices could also be stored on GPU in a vbo (element index array). Should be done in a next step if at all.
std::vector<StdMeshFace> Faces;
const StdMeshMaterial* Material;
@ -602,7 +609,16 @@ public:
const StdMesh& GetMesh() const { return *Mesh; }
#ifndef USE_CONSOLE
GLuint GetIBO() const { return ibo ? ibo : Mesh->GetIBO(); }
unsigned int GetVAOID() const { return vaoid ? vaoid : Mesh->GetVAOID(); }
#endif
protected:
#ifndef USE_CONSOLE
void UpdateIBO();
#endif
AttachedMesh* AttachMeshImpl(StdMeshInstance& instance, AttachedMesh::Denumerator* denumerator, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation, uint32_t flags, bool own_child, unsigned int new_attach_number);
template<typename IteratorType, typename FuncObj>
@ -633,6 +649,15 @@ protected:
AttachedMesh* AttachParent;
bool BoneTransformsDirty;
#ifndef USE_CONSOLE
// private instance index buffer, and a VAO that is bound to it
// instead of the mesh's. We use a private IBO when we use custom
// face ordering. Otherwise, when we use the default face ordering,
// these members are 0 and we use the mesh's IBO and VAO instead.
GLuint ibo;
unsigned int vaoid;
#endif
private:
StdMeshInstance(const StdMeshInstance& other); // noncopyable
StdMeshInstance& operator=(const StdMeshInstance& other); // noncopyable

View File

@ -878,6 +878,11 @@ bool StdMeshMaterialProgram::CompileShader(StdMeshMaterialLoader& loader, C4Shad
uniformNames[C4SSU_AmbientTex] = "ambientTex";
uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
uniformNames[C4SSU_MaterialAmbient] = "materialAmbient";
uniformNames[C4SSU_MaterialDiffuse] = "materialDiffuse";
uniformNames[C4SSU_MaterialSpecular] = "materialSpecular";
uniformNames[C4SSU_MaterialEmission] = "materialEmission";
uniformNames[C4SSU_MaterialShininess] = "materialShininess";
uniformNames[C4SSU_Bones] = "bones";
uniformNames[C4SSU_CullMode] = "cullMode";
for (unsigned int i = 0; i < ParameterNames.size(); ++i)

View File

@ -96,7 +96,7 @@ END
IDD_CONSOLE DIALOGEX 0, 0, 232, 127
STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_CONTROLPARENT
CAPTION "CONSOLE"
MENU IDR_CONSOLEMENU
@ -121,7 +121,7 @@ BEGIN
END
IDD_PROPERTIES DIALOGEX 0, 0, 168, 62
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_THICKFRAME
CAPTION "PROPERTIES"
FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN

View File

@ -828,7 +828,7 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
pContext = pCurCtx->Obj;
}
pFunc->CheckParTypes(pPars);
pFunc->CheckParTypes(pPars, true);
// Script function?
C4AulScriptFunc *pSFunc = pFunc->SFunc();

View File

@ -61,14 +61,24 @@ StdStrBuf C4AulFunc::GetFullName()
return r;
}
void C4AulFunc::CheckParTypes(const C4Value pPars[]) const {
bool C4AulFunc::CheckParTypes(const C4Value pPars[], bool fPassErrors) const {
// Convert parameters (typecheck)
const C4V_Type *pTypes = GetParType();
int parcount = GetParCount();
for (int i = 0; i < parcount; i++) {
if (!pPars[i].CheckParConversion(pTypes[i]))
throw C4AulExecError(FormatString("call to \"%s\" parameter %d: passed %s, but expected %s",
GetName(), i + 1, pPars[i].GetTypeName(), GetC4VName(pTypes[i])
).getData());
{
C4AulExecError e(FormatString(
"call to \"%s\" parameter %d: passed %s, but expected %s",
GetName(), i + 1, pPars[i].GetTypeName(), GetC4VName(pTypes[i])).getData());
if (fPassErrors)
throw e;
else
{
e.show();
return false;
}
}
}
return true;
}

View File

@ -79,10 +79,14 @@ public:
virtual C4V_Type GetRetType() const = 0;
C4Value Exec(C4PropList * p = NULL, C4AulParSet *pPars = NULL, bool fPassErrors=false)
{
// Every parameter type allows conversion from nil, so no parameters are always allowed
if (!pPars)
return Exec(p, C4AulParSet().Par, fPassErrors);
if (!CheckParTypes(pPars->Par, fPassErrors)) return C4Value();
return Exec(p, pPars->Par, fPassErrors);
}
virtual C4Value Exec(C4PropList * p, C4Value pPars[], bool fPassErrors=false) = 0;
void CheckParTypes(const C4Value pPars[]) const;
bool CheckParTypes(const C4Value pPars[], bool fPassErrors) const;
};
#endif

View File

@ -349,7 +349,6 @@ static C4Value FnCall(C4PropList * _this, C4Value * Pars)
}
if (!fn)
throw C4AulExecError(FormatString("Call: no function %s", Pars[0].GetDataString().getData()).getData());
fn->CheckParTypes(ParSet.Par);
return fn->Exec(_this, &ParSet, true);
}