240 lines
7.7 KiB
JavaScript
240 lines
7.7 KiB
JavaScript
// UTK RENDER https://github.com/utkuali/utk_render
|
|
|
|
import { CfxTexture, LinearFilter, Mesh, NearestFilter, OrthographicCamera, PlaneBufferGeometry, RGBAFormat, Scene, ShaderMaterial, UnsignedByteType, WebGLRenderTarget, WebGLRenderer } from "/module/Three.js";
|
|
|
|
var isAnimated = false;
|
|
var MainRender;
|
|
|
|
// from https://stackoverflow.com/a/12300351
|
|
function dataURItoBlob(dataURI) {
|
|
const byteString = atob(dataURI.split(',')[1]);
|
|
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
|
|
|
|
const ab = new ArrayBuffer(byteString.length);
|
|
const ia = new Uint8Array(ab);
|
|
|
|
for (let i = 0; i < byteString.length; i++) {
|
|
ia[i] = byteString.charCodeAt(i);
|
|
}
|
|
|
|
const blob = new Blob([ab], { type: mimeString });
|
|
return blob;
|
|
}
|
|
|
|
const normalModeAspectRatio = 1.6;
|
|
|
|
// citizenfx/screenshot-basic
|
|
class GameRender {
|
|
constructor() {
|
|
window.addEventListener('resize', this.resize);
|
|
this.width = window.innerWidth;
|
|
this.height = window.innerHeight;
|
|
this.landscape = false;
|
|
$('#camera-canvas').css({
|
|
width: '100%',
|
|
transform: 'rotate(0deg)'
|
|
});
|
|
|
|
this.resizedWidth = this.width / normalModeAspectRatio;
|
|
this.resizedHeight = this.height;
|
|
|
|
const cameraRTT = new OrthographicCamera(this.width / -2, this.width / 2, this.height / 2, this.height / -2, -10000, 10000);
|
|
cameraRTT.position.z = 100;
|
|
cameraRTT.setViewOffset(this.width, this.height, 0, 0, this.width, this.height);
|
|
|
|
const sceneRTT = new Scene();
|
|
|
|
const rtTexture = new WebGLRenderTarget(this.width, this.height, { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat, type: UnsignedByteType });
|
|
const gameTexture = new CfxTexture();
|
|
gameTexture.needsUpdate = true;
|
|
|
|
const material = new ShaderMaterial({
|
|
uniforms: { "tDiffuse": { value: gameTexture } },
|
|
vertexShader: `
|
|
varying vec2 vUv;
|
|
|
|
void main() {
|
|
vUv = vec2(uv.x, 1.0-uv.y);
|
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
}
|
|
`,
|
|
fragmentShader: `
|
|
varying vec2 vUv;
|
|
uniform sampler2D tDiffuse;
|
|
|
|
void main() {
|
|
gl_FragColor = texture2D(tDiffuse, vUv);
|
|
}
|
|
`
|
|
});
|
|
|
|
this.material = material;
|
|
|
|
const plane = new PlaneBufferGeometry(this.width, this.height);
|
|
const quad = new Mesh(plane, material);
|
|
quad.position.z = -100;
|
|
sceneRTT.add(quad);
|
|
|
|
const renderer = new WebGLRenderer();
|
|
renderer.setSize(this.width, this.height);
|
|
renderer.autoClear = false;
|
|
|
|
let appendArea = document.createElement("div");
|
|
appendArea.id = "three-game-render";
|
|
|
|
document.body.append(appendArea);
|
|
|
|
appendArea.appendChild(renderer.domElement);
|
|
appendArea.style.display = 'none';
|
|
|
|
this.renderer = renderer;
|
|
this.rtTexture = rtTexture;
|
|
this.sceneRTT = sceneRTT;
|
|
this.cameraRTT = cameraRTT;
|
|
this.gameTexture = gameTexture;
|
|
|
|
this.animate = this.animate.bind(this);
|
|
|
|
this.animate();
|
|
}
|
|
|
|
setLandscape(bool) {
|
|
this.landscape = bool;
|
|
if (bool) {
|
|
this.resizedWidth = this.width;
|
|
this.resizedHeight = this.height;
|
|
$('#camera-canvas').css({
|
|
width: '155%',
|
|
transform: 'rotate(90deg)'
|
|
});
|
|
Rotate(true)
|
|
} else {
|
|
this.resizedWidth = this.width / normalModeAspectRatio;
|
|
this.resizedHeight = this.height;
|
|
$('#camera-canvas').css({
|
|
width: '100%',
|
|
transform: 'rotate(0deg)'
|
|
|
|
});
|
|
Rotate(false)
|
|
}
|
|
this.resize()
|
|
}
|
|
|
|
getLandscape() {
|
|
return this.landscape;
|
|
}
|
|
|
|
resize(screenshot) {
|
|
const cameraRTT = new OrthographicCamera(this.width / -2, this.width / 2, this.height / 2, this.height / -2, -10000, 10000);
|
|
if (screenshot === true) {
|
|
// cameraRTT.setViewOffset(this.width, this.height, 0, 0, this.width, this.height);
|
|
} else {
|
|
// const width = Math.floor(this.height / 2);
|
|
// const width = Math.floor(this.height * 10 / 23);
|
|
// cameraRTT.setViewOffset(this.width, this.height, 0, 0, this.width, this.height);
|
|
const portrait = this.width === this.resizedWidth
|
|
const width = portrait ? this.resizedWidth : this.resizedWidth / 3.5;
|
|
// cameraRTT.setViewOffset(this.width, this.height, width, 0, this.resizedWidth, this.height);
|
|
if (this.canvas) this.canvas.width = this.resizedWidth;
|
|
|
|
// cameraRTT.setViewOffset(this.width, this.height, this.width, 0, width, this.height);
|
|
}
|
|
|
|
this.cameraRTT = cameraRTT;
|
|
|
|
const sceneRTT = new Scene();
|
|
|
|
const plane = new PlaneBufferGeometry(this.width, this.height);
|
|
const quad = new Mesh(plane, this.material);
|
|
quad.position.z = -100;
|
|
sceneRTT.add(quad);
|
|
|
|
this.sceneRTT = sceneRTT;
|
|
|
|
this.rtTexture = new WebGLRenderTarget(this.width, this.height, { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat, type: UnsignedByteType });
|
|
|
|
this?.renderer?.setSize?.(this.width, this.height);
|
|
}
|
|
|
|
animate() {
|
|
if (isAnimated) {
|
|
this.renderer.clear();
|
|
this.renderer.render(this.sceneRTT, this.cameraRTT, this.rtTexture, true);
|
|
|
|
const width = this.width;
|
|
const height = this.height;
|
|
|
|
if (this.canvas.width !== this.resizedWidth || this.canvas.height !== height) {
|
|
this.canvas.width = this.resizedWidth;
|
|
this.canvas.height = height;
|
|
}
|
|
|
|
const read = new Uint8Array(width * height * 4);
|
|
this.renderer.readRenderTargetPixels(this.rtTexture, 0, 0, width, height, read);
|
|
|
|
const cxt = this.canvas.getContext('2d');
|
|
const imageData = new ImageData(new Uint8ClampedArray(read.buffer), width, height);
|
|
cxt.putImageData(imageData, 0, 0);
|
|
requestAnimationFrame(this.animate);
|
|
}
|
|
}
|
|
|
|
createTempCanvas() {
|
|
this.canvas = document.createElement("canvas");
|
|
this.canvas.style.display = 'inline';
|
|
this.canvas.width = this.width;
|
|
this.canvas.height = this.height;
|
|
}
|
|
|
|
renderToTarget(element) {
|
|
this.resize(false);
|
|
this.canvas = element;
|
|
isAnimated = true;
|
|
this.animate()
|
|
}
|
|
|
|
requestScreenshot = (url, field) => new Promise(res => {
|
|
if (!url || !field) return console.error('url or field is not defined');
|
|
isAnimated = true;
|
|
const imageURL = this.canvas.toDataURL("image/png", 1.0);
|
|
|
|
const formData = new FormData();
|
|
formData.append(field, dataURItoBlob(imageURL), `screenshot.png`);
|
|
|
|
fetch(url, {
|
|
method: 'POST',
|
|
mode: 'cors',
|
|
body: formData
|
|
})
|
|
.then(response => response.text())
|
|
.then(text => {
|
|
text = JSON.parse(text);
|
|
if (!text.attachments) return res(text.data.url);
|
|
res(text.attachments[0].proxy_url)
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
res(false);
|
|
});
|
|
})
|
|
|
|
stop() {
|
|
isAnimated = false;
|
|
if (this.canvas) {
|
|
if (this.canvas.style.display != "none") {
|
|
this.canvas.style.display = "none";
|
|
}
|
|
}
|
|
this.resize(true);
|
|
this.resizedWidth = this.width;
|
|
this.resizedHeight = this.height;
|
|
this.setLandscape(false);
|
|
}
|
|
}
|
|
|
|
setTimeout(() => {
|
|
MainRender = new GameRender();
|
|
window.MainRender = MainRender;
|
|
}, 1000);
|