chore: update .gitignore, enhance imgui.ini layout, and refine castle and character motion examples
This commit is contained in:
1 parent
ac42525522
commit
58e01f6d53
6 files changed
+102
-16
No files matched your search
@@ -22,6 +22,7 @@ test/assets/classic/.downloaded
|
||||
# Skinned character: CesiumMan glTF is committed under test/assets/skinned/cesium_man/
|
||||
# (see ATTRIBUTION.md). Re-run scripts/download_skinned_character.sh to refresh.
|
||||
.vscode/
|
||||
.local-tools/
|
||||
.superpowers/
|
||||
artifacts-local-test/
|
||||
imgui.ini
|
||||
|
||||
@@ -13,3 +13,13 @@ Pos=12,8
|
||||
Size=803,322
|
||||
Collapsed=0
|
||||
|
||||
[Window][###map-input/Window]
|
||||
Pos=24,88
|
||||
Size=672,504
|
||||
Collapsed=0
|
||||
|
||||
[Window][###hud/建造面板]
|
||||
Pos=724,20
|
||||
Size=532,756
|
||||
Collapsed=0
|
||||
|
||||
@@ -65,19 +65,21 @@ function rebuildCastle() {
|
||||
|
||||
if (castleCamera == null) {
|
||||
castleCamera = eve.Camera3D();
|
||||
castleCamera.setEye(54.0, 34.0, -58.0);
|
||||
castleCamera.setTarget(0.0, 5.0, 0.0);
|
||||
castleCamera.setUp(0.0, 1.0, 0.0);
|
||||
castleCamera.setFov(43.0);
|
||||
castleCamera.setAmbient(0.23, 0.25, 0.29);
|
||||
castleCamera.setActive(true);
|
||||
gfx.setDirectionalLight(-0.55, -1.0, -0.35, 1.45, 1.30, 1.08);
|
||||
}
|
||||
// Default far plane is 100 m; a rotating keep with outer rings sits past that.
|
||||
castleCamera.setEye(88.0, 52.0, -96.0);
|
||||
castleCamera.setTarget(0.0, 7.0, 0.0);
|
||||
castleCamera.setUp(0.0, 1.0, 0.0);
|
||||
castleCamera.setFov(38.0);
|
||||
castleCamera.setClipPlanes(0.5, 2000.0);
|
||||
castleCamera.setAmbient(0.23, 0.25, 0.29);
|
||||
gfx.setDirectionalLight(-0.55, -1.0, -0.35, 1.45, 1.30, 1.08);
|
||||
if (castleParts.len() == 0) rebuildCastle();
|
||||
gfx.setBackgroundColor(0.10, 0.14, 0.20, 1.0);
|
||||
|
||||
function eve_update(dt) {
|
||||
castleYaw += dt * 3.5;
|
||||
castleYaw += dt * 0.22;
|
||||
foreach (part in castleParts) part.setYaw(castleYaw);
|
||||
if (castlePressed("r") || castlePressed("R")) { castleSeed += 1; rebuildCastle(); }
|
||||
if (castlePressed("1")) { rings = max(1, rings - 1); rebuildCastle(); }
|
||||
|
||||
@@ -32,6 +32,34 @@ function humanoidProfile(target) {
|
||||
return p;
|
||||
}
|
||||
|
||||
// createRenderable enables the glTF extension PBR path whenever Assimp reports
|
||||
// an alphaMode. That shader is meant for IBL/capture, not this UI viewport:
|
||||
// albedo is decoded twice, linear ORM/normal maps are treated as sRGB, and the
|
||||
// characters render as a flat red unlit mass. Rebuild a conventional Material
|
||||
// so renderScene3DToCanvas uses mesh3d lighting like the original showcase.
|
||||
function labPreviewMaterial(part, albedo) {
|
||||
local surface = gfx.newMaterial();
|
||||
surface.setShadingModel("pbr");
|
||||
surface.setMetallic(0.0);
|
||||
surface.setRoughness(0.78);
|
||||
surface.setReceiveLight(true);
|
||||
surface.setReceiveShadow(true);
|
||||
surface.setCastShadow(true);
|
||||
surface.setTint(part.getTintR(), part.getTintG(), part.getTintB(), 1.0);
|
||||
if (albedo != null) {
|
||||
surface.setAlbedoTexture(albedo);
|
||||
surface.setTint(1.0, 1.0, 1.0, 1.0);
|
||||
part.setTexture(albedo);
|
||||
part.setTint(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
part.setMaterial(surface);
|
||||
part.setMetallic(0.0);
|
||||
part.setRoughness(0.78);
|
||||
part.setCastShadow(true);
|
||||
part.setReceiveShadow(true);
|
||||
part.setReceiveLight(true);
|
||||
}
|
||||
|
||||
function addLabActor(model, name, x, source) {
|
||||
local sk = source ? lab.skeleton : anim.newSkeletonFromModel(model);
|
||||
local a = { model=model, name=name, skeleton=sk, clips=[], parts=[], skins=[],
|
||||
@@ -50,14 +78,15 @@ function addLabActor(model, name, x, source) {
|
||||
if (!model.hasBones(i)) continue;
|
||||
local part = model3d.createRenderable(gfx, model, i);
|
||||
part.setPosition(x, 0.0, 0.0);
|
||||
part.setCastShadow(true); part.setReceiveShadow(true); part.setReceiveLight(true);
|
||||
part.setRoughness(0.78);
|
||||
local albedo = null;
|
||||
local imported = part.getMaterial();
|
||||
if (imported != null) albedo = imported.getAlbedoTexture();
|
||||
local texturePath = model.getMaterialTexturePath(model.getMaterialIndex(i), "base_color", 0);
|
||||
if (texturePath != "") {
|
||||
local texture = gfx.newTextureFromFile("assets/quaternius/" + texturePath);
|
||||
a.textures.push(texture); part.setTexture(texture);
|
||||
part.getMaterial().setAlbedoTexture(texture);
|
||||
albedo = gfx.newTextureFromFile("assets/quaternius/" + texturePath);
|
||||
a.textures.push(albedo);
|
||||
}
|
||||
labPreviewMaterial(part, albedo);
|
||||
a.parts.push(part);
|
||||
a.skins.push(anim.newSkinFromModel(model, i, sk));
|
||||
}
|
||||
@@ -98,11 +127,17 @@ eve_init = function() {
|
||||
"TARGET / Male", 2.5, false);
|
||||
lab.ground = eve.Renderable3D(); lab.ground.setMesh(gfx.newMeshCube(1.0));
|
||||
lab.ground.setPosition(0.0,-0.10,0.0); lab.ground.setScale(10.0,0.16,5.0);
|
||||
lab.ground.setTint(0.13,0.17,0.21,1.0); lab.ground.setReceiveShadow(true);
|
||||
lab.ground.setTint(0.13,0.17,0.21,1.0); lab.ground.setRoughness(0.92);
|
||||
lab.ground.setReceiveShadow(true); lab.ground.setReceiveLight(true); lab.ground.setCastShadow(false);
|
||||
lab.camera = eve.Camera3D(); labCamera(); lab.camera.setUp(0.0,1.0,0.0);
|
||||
lab.camera.setFov(26.0); lab.camera.setAmbient(0.38,0.40,0.46); lab.camera.setActive(true);
|
||||
gfx.setDirectionalLight(-0.4, 1.0, 0.7, 1.60, 1.49, 1.34);
|
||||
local key = eve.Light3D(); key.setType("dir"); key.setDirection(-0.4,1.0,0.7);
|
||||
key.setColor(1.0,0.93,0.84,1.6); lab.lights.push(key);
|
||||
key.setColor(1.0,0.93,0.84,1.6); key.setCastShadow(true); key.setShadowStrength(0.85);
|
||||
lab.lights.push(key);
|
||||
local fill = eve.Light3D(); fill.setType("point");
|
||||
fill.setPosition(2.2, 2.6, 3.4); fill.setRadius(12.0);
|
||||
fill.setColor(0.55, 0.68, 0.92, 1.15); lab.lights.push(fill);
|
||||
gfx.setBackgroundColor(0.045,0.065,0.09,1.0);
|
||||
ui.setScale(1.0); ui.setTheme("dark"); ui.beginBuild(); ui.beginWindow("Character Motion Lab", "root");
|
||||
ui.text("CHARACTER MOTION LAB / Quaternius CC0", "title");
|
||||
|
||||
@@ -43,9 +43,18 @@ struct Builder {
|
||||
float x0=cx+std::cos(a0)*radius, z0=cz+std::sin(a0)*radius;
|
||||
float x1=cx+std::cos(a1)*radius, z1=cz+std::sin(a1)*radius;
|
||||
float am=(a0+a1)*0.5f;
|
||||
quad(x0,y0,z0, x1,y0,z1, x1,y1,z1, x0,y1,z0,
|
||||
// CCW from the outside, matching Graphics::newMeshCylinder / Vulkan
|
||||
// front faces. The previous order wound the wall and cap inward, so
|
||||
// back-face culling showed the interior of every tower.
|
||||
quad(x0,y0,z0, x0,y1,z0, x1,y1,z1, x1,y0,z1,
|
||||
std::cos(am),0,std::sin(am));
|
||||
quad(cx,y1,cz, x0,y1,z0, x1,y1,z1, cx,y1,cz, 0,1,0);
|
||||
const uint32_t cap = uint32_t(mesh.getVertexCount());
|
||||
mesh.addVertex(cx, y1, cz, 0, 1, 0, 0.5f, 0.5f);
|
||||
mesh.addVertex(x1, y1, z1, 0, 1, 0, 0.5f + 0.5f * std::cos(a1),
|
||||
0.5f + 0.5f * std::sin(a1));
|
||||
mesh.addVertex(x0, y1, z0, 0, 1, 0, 0.5f + 0.5f * std::cos(a0),
|
||||
0.5f + 0.5f * std::sin(a0));
|
||||
mesh.addTriangle(cap, cap + 1, cap + 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -369,6 +369,28 @@ bool meshNormalsFiniteUnit(const MeshBuild &m, float tol = 0.15f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool meshWindingAgreesWithNormals(const MeshBuild &m, float minFraction = 0.9f) {
|
||||
int ok = 0, counted = 0;
|
||||
for (int t = 0; t + 2 < m.getIndexCount(); t += 3) {
|
||||
const int i0 = m.getIndex(t), i1 = m.getIndex(t + 1), i2 = m.getIndex(t + 2);
|
||||
const float ax = m.getPositionX(i1) - m.getPositionX(i0);
|
||||
const float ay = m.getPositionY(i1) - m.getPositionY(i0);
|
||||
const float az = m.getPositionZ(i1) - m.getPositionZ(i0);
|
||||
const float bx = m.getPositionX(i2) - m.getPositionX(i0);
|
||||
const float by = m.getPositionY(i2) - m.getPositionY(i0);
|
||||
const float bz = m.getPositionZ(i2) - m.getPositionZ(i0);
|
||||
const float gx = ay * bz - az * by, gy = az * bx - ax * bz, gz = ax * by - ay * bx;
|
||||
const float gLen = std::sqrt(gx * gx + gy * gy + gz * gz);
|
||||
if (gLen < 1e-8f) continue;
|
||||
const float nx = (m.getNormalX(i0) + m.getNormalX(i1) + m.getNormalX(i2)) / 3.f;
|
||||
const float ny = (m.getNormalY(i0) + m.getNormalY(i1) + m.getNormalY(i2)) / 3.f;
|
||||
const float nz = (m.getNormalZ(i0) + m.getNormalZ(i1) + m.getNormalZ(i2)) / 3.f;
|
||||
++counted;
|
||||
if (gx * nx + gy * ny + gz * nz > 0.f) ++ok;
|
||||
}
|
||||
return counted > 0 && float(ok) / float(counted) >= minFraction;
|
||||
}
|
||||
|
||||
bool meshPositionsFinite(const MeshBuild &m) {
|
||||
for (int i = 0; i < m.getVertexCount(); ++i) {
|
||||
if (!std::isfinite(m.getPositionX(i)) || !std::isfinite(m.getPositionY(i)) ||
|
||||
@@ -3536,6 +3558,13 @@ TEST_CASE("procgen.mesh.castle.multilevelDeterministicAndComplete") {
|
||||
CHECK(stairs->getVertexCount() > 100);
|
||||
CHECK_EQ(stairs->getMeta("group", ""), "stairs");
|
||||
CHECK(meshIndicesInRange(*stairs));
|
||||
int towerGroup = -1;
|
||||
for (int i = 0; i < a.getGroupCount(); ++i)
|
||||
if (a.getGroupName(i) == "towers") towerGroup = i;
|
||||
REQUIRE(towerGroup >= 0);
|
||||
auto towers = a.copyGroup(towerGroup);
|
||||
REQUIRE(towers.get() != nullptr);
|
||||
CHECK(meshWindingAgreesWithNormals(*towers));
|
||||
}
|
||||
|
||||
TEST_CASE("procgen.mesh.castle.parametersControlTopologyAndBounds") {
|
||||
|
||||
Reference in new issue
Block a user