/****
* Classes
****/
var Cube = Container.expand(function (size) {
var self = Container.call(this);
size = size || 100;
self.faces = [];
self.rotationX = 0;
self.rotationY = 0;
self.rotationZ = 0;
var vertices = [{
x: -size / 2,
y: -size / 2,
z: -size / 2
}, {
x: size / 2,
y: -size / 2,
z: -size / 2
}, {
x: size / 2,
y: size / 2,
z: -size / 2
}, {
x: -size / 2,
y: size / 2,
z: -size / 2
}, {
x: -size / 2,
y: -size / 2,
z: size / 2
}, {
x: size / 2,
y: -size / 2,
z: size / 2
}, {
x: size / 2,
y: size / 2,
z: size / 2
}, {
x: -size / 2,
y: size / 2,
z: size / 2
}];
var faces = [[0, 1, 2, 3],
// Front
[4, 5, 6, 7],
// Back
[0, 1, 5, 4],
// Top
[2, 3, 7, 6],
// Bottom
[0, 3, 7, 4],
// Left
[1, 2, 6, 5] // Right
];
faces.forEach(function (faceIndices) {
var facePoints = faceIndices.map(function (index) {
return vertices[index];
});
var face = new SimpleFace({
points: facePoints,
tint: 0xFFFFFF
});
face.originalPoints = facePoints.map(function (p) {
return {
x: p.x,
y: p.y,
z: p.z
};
});
self.addChild(face);
self.faces.push(face);
});
self.rotate3D = function (angleX, angleY, angleZ) {
self.rotationX += angleX;
self.rotationY += angleY;
self.rotationZ += angleZ;
var cosX = Math.cos(self.rotationX),
sinX = Math.sin(self.rotationX);
var cosY = Math.cos(self.rotationY),
sinY = Math.sin(self.rotationY);
var cosZ = Math.cos(self.rotationZ),
sinZ = Math.sin(self.rotationZ);
self.faces.forEach(function (face) {
var transformedPoints = face.originalPoints.map(function (point) {
var x = point.x,
y = point.y,
z = point.z;
// Apply rotation around X-axis
var newY = cosX * y - sinX * z;
var newZ = sinX * y + cosX * z;
// Apply rotation around Y-axis
var newX = cosY * x + sinY * newZ;
newZ = cosY * newZ - sinY * x;
// Apply rotation around Z-axis
x = cosZ * newX - sinZ * newY;
y = sinZ * newX + cosZ * newY;
return {
x: x,
y: y,
z: newZ
};
});
// Store transformed points
face.transformedPoints = transformedPoints;
face.points = transformedPoints; // Ensure points are updated here!
});
};
});
var SimpleFace = Container.expand(function (options) {
var self = Container.call(this);
options = options || {};
self.points = options.points || [{
x: -50,
y: -50
}, {
x: 50,
y: -50
}, {
x: 50,
y: 50
}, {
x: -50,
y: 50
}];
self.tint = options.tint || 0xFFFFFF;
self.fillDensity = 300;
self.lines = [];
for (var i = 0; i < self.points.length; i++) {
var start = self.points[i];
var end = self.points[(i + 1) % self.points.length];
var line = drawLine(start.x, start.y, end.x, end.y, self.tint);
self.addChild(line);
self.lines.push(line);
}
self.fillLines = [];
for (var i = 1; i <= self.fillDensity; i++) {
var ratio = i / (self.fillDensity + 1);
var fillStart = interpolate(self.points[0], self.points[1], ratio);
var fillEnd = interpolate(self.points[3], self.points[2], ratio);
var tint = calculateShadowTint(fillStart);
var fillLine = drawLine(fillStart.x, fillStart.y, fillEnd.x, fillEnd.y, tint);
self.addChild(fillLine);
self.fillLines.push(fillLine);
}
function calculateShadowTint(point) {
var normal = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
x: 0,
y: 0,
z: 1
};
var dx = light.x - point.x;
var dy = light.y - point.y;
var dz = light.z - point.z;
var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
// Normalize intensity (closer = brighter)
var intensity = Math.max(0, 1 - distance / 6000);
// Calculate light direction
var lightDir = {
x: dx / distance,
y: dy / distance,
z: dz / distance
};
// Ensure `normal` exists
if (!normal) {
normal = {
x: 0,
y: 0,
z: 1
}; // Default normal
}
// Calculate dot product for directional shading
var dot = Math.max(0.2, lightDir.x * normal.x + lightDir.y * normal.y + lightDir.z * normal.z);
dot = Math.max(0, dot); // Ensure it's non-negative (shadows only)
// Combine distance and angle effects
var finalIntensity = Math.pow(intensity * dot, 1.2);
// Convert intensity to grayscale color
var brightness = Math.floor(finalIntensity * 255);
return brightness << 16 | brightness << 8 | brightness;
}
function interpolate(p1, p2, t) {
return {
x: p1.x + (p2.x - p1.x) * t,
y: p1.y + (p2.y - p1.y) * t,
z: p1.z + (p2.z - p1.z) * t // Added Z interpolation
};
}
self.updateFace = function (transformedPoints) {
if (!transformedPoints || transformedPoints.length < 4) {
return;
} // Prevents errors
self.lines.forEach(function (line, i) {
var start = transformedPoints[i];
var end = transformedPoints[(i + 1) % transformedPoints.length];
line.x = start.x;
line.y = start.y;
var distance = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
line.width = distance;
var angle = Math.atan2(end.y - start.y, end.x - start.x);
line.rotation = angle;
});
// Recalculate normal vector for shading direction
var edge1 = {
x: transformedPoints[1].x - transformedPoints[0].x,
y: transformedPoints[1].y - transformedPoints[0].y,
z: transformedPoints[1].z - transformedPoints[0].z
};
var edge2 = {
x: transformedPoints[3].x - transformedPoints[0].x,
y: transformedPoints[3].y - transformedPoints[0].y,
z: transformedPoints[3].z - transformedPoints[0].z
};
var normal = {
x: edge1.y * edge2.z - edge1.z * edge2.y,
y: edge1.z * edge2.x - edge1.x * edge2.z,
z: edge1.x * edge2.y - edge1.y * edge2.x
};
// Normalize the normal vector
var normLength = Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
if (normLength !== 0) {
normal.x /= normLength;
normal.y /= normLength;
normal.z /= normLength;
} else {
normal = {
x: 0,
y: 0,
z: 1
}; // Default normal if zero-length
}
// Update each fill line dynamically
var start = transformedPoints[0];
var end = transformedPoints[3];
self.fillLines.forEach(function (fillLine, i) {
var ratio = (i + 1) / (self.fillDensity + 1);
var fillStart = interpolate(start, transformedPoints[1], ratio);
var fillEnd = interpolate(end, transformedPoints[2], ratio);
var tint = calculateShadowTint(fillStart, normal);
fillLine.tint = tint;
fillLine.x = fillStart.x;
fillLine.y = fillStart.y;
var distance = Math.sqrt(Math.pow(fillEnd.x - fillStart.x, 2) + Math.pow(fillEnd.y - fillStart.y, 2));
fillLine.width = distance;
var angle = Math.atan2(fillEnd.y - fillStart.y, fillEnd.x - fillStart.x);
fillLine.rotation = angle;
});
};
function interpolate(p1, p2, t) {
return {
x: p1.x + (p2.x - p1.x) * t,
y: p1.y + (p2.y - p1.y) * t,
z: p1.z + (p2.z - p1.z) * t // Added Z interpolation
};
}
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x24aae2
});
/****
* Game Code
****/
var light = {
x: game.width / 2 + 100,
y: game.height / 2 - 300,
z: 800
};
/****
* Dynamic Shadows
****/
function calculateShadowTint(point) {
var normal = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
x: 0,
y: 0,
z: 1
};
var dx = light.x - point.x;
var dy = light.y - point.y;
var dz = light.z - point.z;
var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
// Normalize intensity (closer = brighter)
var intensity = Math.max(0, 1 - distance / 500);
// Calculate light direction
var lightDir = {
x: dx / distance,
y: dy / distance,
z: dz / distance
};
// Ensure `normal` exists
if (!normal) {
normal = {
x: 0,
y: 0,
z: 1
}; // Default normal
}
// Calculate dot product for directional shading
var dot = lightDir.x * normal.x + lightDir.y * normal.y + lightDir.z * normal.z;
dot = Math.max(0, dot); // Ensure it's non-negative (shadows only)
// Combine distance and angle effects
var finalIntensity = intensity * dot;
// Convert intensity to grayscale color
var brightness = Math.floor(finalIntensity * 255);
return brightness << 16 | brightness << 8 | brightness;
}
/****
* Utility Functions
****/
function drawLine(x1, y1, x2, y2, tint) {
var line = LK.getAsset('line', {
anchorX: 0,
anchorY: 0,
x: x1,
y: y1,
tint: tint
});
var distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
line.width = distance;
var angle = Math.atan2(y2 - y1, x2 - x1);
line.rotation = angle;
return line;
}
/****
* Game Variables
****/
var cube;
var rotationSpeedX = 0.01;
var rotationSpeedY = 0.01;
function gameInitialize() {
cube = new Cube(1000);
cube.x = game.width / 2;
cube.y = game.height / 2;
game.addChild(cube);
}
game.update = function () {
// Apply rotation first
cube.rotate3D(rotationSpeedX, rotationSpeedY, 0);
// Update each face dynamically after transformation
cube.faces.forEach(function (face) {
face.points = face.transformedPoints; // Make sure it updates with the rotated values
face.updateFace(face.transformedPoints);
});
};
gameInitialize();
// Define JSON object if not already defined
var JSON = JSON || {
parse: function parse(s) {
return eval('(' + s + ')');
},
stringify: function stringify(o) {
var r = [];
if (typeof o == "string") {
return '"' + o.replace(/"/g, '\\"') + '"';
}
if (typeof o == "number" || typeof o == "boolean") {
return o.toString();
}
if (o instanceof Array) {
for (var i = 0; i < o.length; i++) {
r.push(JSON.stringify(o[i]));
}
return '[' + r.join(',') + ']';
}
for (var k in o) {
r.push('"' + k + '":' + JSON.stringify(o[k]));
}
return '{' + r.join(',') + '}';
}
};