일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 방학셰이더스터디
- 3D AI
- 컬러라이즈
- fbx 보안
- 게임개발
- pureref
- 셰이더그래프
- 재질
- 프로젝트
- fbx 안열림
- 노말맵
- 3d 뷰어
- 배경모델링
- 모작
- 유니티
- 공부
- 레퍼런스 프로그램
- 엔진심화
- 손맵
- fbx
- 3d 모델링 ai
- 메가스캔
- Colorize
- 모델링 ai
- 3d 모델을 로드할 수 없음
- rodin ai
- 게임제작
- 검색 프로그램
- normal map
- 메가스캔 유료화
- Today
- Total
베개발
메가스캔 내년부터 유료화?? 에픽게임즈 'Fab' 공개 + 메가스캔 일괄구매 스크립트 공유 본문
※ 현재 메가스캔 유료화 전환되어 해당 스크립트는 사용이 불가능합니다 ㅠㅠ
■ 에픽게임즈 FAB 공개
에픽 게임즈가 언리얼 마켓플레이스 서비스를 대신할 팹(Fab)이라는 새로운 마켓 사이트를 공개했다.
에픽 게임즈는 2024년 10월 중순, 디지털 에셋 통합 플랫폼인 Fab을 공식적으로 출시하며,
기존의 언리얼 엔진 마켓플레이스와 퀵셀 Bridge를 통한 메가스캔 서비스를 종료한다.
메가스캔 에셋들은 2024년 말까지 무료로 제공되지만, 2025년부터는 대부분 유료화된다.
유저들은 올해 말까지 무료로 에셋을 다운로드할 수 있으며, 다운로드한 에셋은 영구적으로 사용할 수 있다.
미리미리 구매버튼을 눌러놓자.
참고로 에셋이 18000개가 넘어가는데, 자동으로 전부 구매하는 스크립트가 있다. (밑에서 언급)
에픽게임즈의 일정은 다음과 같다.
지금 마켓플레이스 마이그레이션을 위한 퍼블리싱 포탈은 열려있으며,
10월 중순에 Fab이 오픈될 예정이다.
에픽게임즈는 Fab으로 기존의 언리얼 엔진 마켓플레이스, Sketchfab, 아트스테이션 마켓플레이스, 퀵셀의 메가스캔을 하나로 합친다고 한다.(!)
어도비와 파트너십을 체결해 서브스턴스 페인터와도 바로 연동이 된다고 하며,
실시간으로 에셋을 돌려 볼 수 있는 3D 뷰어도 있다고 하니 에셋 서치가 좀 더 편해질 것으로 기대된다.
심지어는 향후 Minecraft와 Roblox 에셋도 추가할 계획이라고 한다..!!
■ Fab 바로가기 링크
■ 메가스캔 에셋 자동으로 전부 구매하기
X(전 트위터)를 둘러보다 메가스캔 유료화 소식을 알게되었고 멘붕오던중, 해외분이 스크립트를 올려주셨다.
메가스캔 자동구매하는법
1) https://quixel.com/ 에 로그인한다.
2) https://quixel.com/megascans/collections 으로 이동한다.
3) 아래 스크립트를 복사한다.
(async (startPage = 0, autoClearConsole = true) => {
const getCookie = (name) => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
};
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const fetchWithTimeout = (resource, options = {}) => {
const { timeout = 10000 } = options;
return Promise.race([
fetch(resource, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeout)
),
]);
};
const callCacheApi = async (params = {}) => {
const defaultParams = {
page: 0,
maxValuesPerFacet: 1000,
hitsPerPage: 1000,
attributesToRetrieve: ["id", "name"].join(","),
};
const fetchData = async () => {
const response = await fetchWithTimeout("https://proxy-algolia-prod.quixel.com/algolia/cache", {
headers: {
"x-api-key": "2Zg8!d2WAHIUW?pCO28cVjfOt9seOWPx@2j",
},
body: JSON.stringify({
url: "https://6UJ1I5A072-2.algolianet.com/1/indexes/assets/query?x-algolia-application-id=6UJ1I5A072&x-algolia-api-key=e93907f4f65fb1d9f813957bdc344892",
params: new URLSearchParams({ ...defaultParams, ...params }).toString(),
}),
method: "POST",
});
if (!response.ok) {
throw new Error(`Error fetching from Cache API: ${response.statusText}`);
}
return await response.json();
};
return await retryOperation(fetchData, 2000, 5);
};
const callAcl = async ({ id, name }) => {
const fetchData = async () => {
const response = await fetchWithTimeout("https://quixel.com/v1/acl", {
headers: {
authorization: "Bearer " + authToken,
"content-type": "application/json;charset=UTF-8",
},
body: JSON.stringify({ assetID: id }),
method: "POST",
});
if (!response.ok) {
throw new Error(`Error adding item ${id} | ${name}: ${response.statusText}`);
}
const json = await response.json();
if (json?.isError) {
console.error(` --> **Failed to add item** Item ${id} | ${name} (${json?.msg})`);
} else {
console.log(` --> Added item ${id} | ${name}`);
}
};
return await retryOperation(fetchData, 2000, 5);
};
const callAcquired = async () => {
const fetchData = async () => {
const response = await fetchWithTimeout("https://quixel.com/v1/assets/acquired", {
headers: {
authorization: "Bearer " + authToken,
"content-type": "application/json;charset=UTF-8",
},
method: "GET",
});
if (!response.ok) {
throw new Error(`Error fetching acquired items: ${response.statusText}`);
}
return await response.json();
};
return await retryOperation(fetchData, 2000, 5);
};
const retryOperation = async (operation, delay, retries) => {
let lastError;
for (let attempt = 1; attempt <= retries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
console.warn(`Attempt ${attempt} failed (${error.message}). Retrying in ${delay}ms...`);
await sleep(delay);
delay *= 2; // Exponential backoff
}
}
throw lastError;
};
let authToken = "";
const initialize = async () => {
console.log("-> Checking Auth API Token...");
try {
const authCookie = getCookie("auth") ?? "{}";
authToken = JSON.parse(decodeURIComponent(authCookie))?.token;
if (!authToken) {
throw new Error("-> Error: Authentication token not found. Please log in again.");
}
} catch (_) {
throw new Error("-> Error: Authentication token not found. Please log in again.");
}
console.log("-> Fetching acquired items...");
acquiredItems = (await callAcquired()).map((a) => a.assetID);
console.log("-> Fetching total number of pages...");
const initialData = await callCacheApi();
totalPages = initialData.nbPages;
itemsPerPage = initialData.hitsPerPage;
totalItems = initialData.nbHits;
console.log("-> ==============================================");
console.log(`-> Total items: ${totalItems}`);
console.log(`-> ${totalPages} total pages with ${itemsPerPage} items per page`);
console.log(`-> Total items to add: ${totalItems - acquiredItems.length}.`);
console.log("-> ==============================================");
if (!confirm(`Click OK to add ${totalItems - acquiredItems.length} items to your account.`)) {
throw new Error("-> Process cancelled by user.");
}
};
let acquiredItems = [];
let totalPages = 0;
let itemsPerPage = 0;
let totalItems = 0;
const MAX_CONCURRENT_REQUESTS = 5;
const mainProcess = async () => {
for (let pageIdx = startPage || 0; pageIdx < totalPages; pageIdx++) {
console.log(`-> ======================= PAGE ${pageIdx + 1}/${totalPages} START =======================`);
console.log("-> Fetching items from page " + (pageIdx + 1) + " ...");
const pageData = await callCacheApi({ page: pageIdx });
const items = pageData.hits;
console.log("-> Adding unacquired items...");
// Filter out already acquired items
const unownedItems = items.filter((i) => !acquiredItems.includes(i.id));
// Save current progress in localStorage
localStorage.setItem('currentPage', pageIdx);
// Limit concurrent requests
const queue = [...unownedItems];
const workers = Array.from({ length: MAX_CONCURRENT_REQUESTS }, async () => {
while (queue.length > 0) {
const item = queue.shift();
try {
await callAcl(item);
} catch (error) {
console.error(`Error with item ${item.id}: ${error.message}`);
}
}
});
await Promise.all(workers);
console.log(`-> ======================= PAGE ${pageIdx + 1}/${totalPages} COMPLETED =======================`);
if (autoClearConsole) console.clear();
}
};
const finalize = async () => {
console.log("-> Fetching new acquisition info...");
const newAcquiredItems = await callAcquired();
const newItemsAcquired = newAcquiredItems.length;
const newTotalCount = (await callCacheApi()).nbHits;
console.log(`-> Completed. Your account now has a total of ${newItemsAcquired} out of ${newTotalCount} items.`);
alert(`-> Your account now has a total of ${newItemsAcquired} out of ${newTotalCount} items.\n\nIf you find some items missing, try refreshing the page and run the script again.`);
};
try {
// Check if progress was saved
const savedPage = localStorage.getItem('currentPage');
if (savedPage !== null) {
startPage = parseInt(savedPage, 10);
console.log(`-> Resuming from page ${startPage + 1}`);
}
await initialize();
await mainProcess();
await finalize();
// Clear progress
localStorage.removeItem('currentPage');
} catch (error) {
console.error(error.message);
console.log("-> The script could not be completed.");
}
})();
4) https://quixel.com/megascans/collections 에서 F12를 눌러 개발자 도구를 켠다.
5) 'Console' 이라 써있는 부분을 클릭한다.
6) 아까 복사한 스크립트를 붙여넣기 한 뒤, 엔터를 누른다.
*만약 에러가 뜨면서 붙여넣기가 안 된다면,
allow pasting
을 입력해준뒤 엔터를 누르면 된다. 그 이후로는 붙여넣기가 잘 될 것이다.
7) 그럼 이런 창이 뜨는데, 확인을 눌러준다.
내 계정에 약 18000개 가량의 에셋 라이선스를 추가하겠다는 얘기다.
8) 그러면 에셋 자동구매가 시작된다. Console 탭에서 진행상황을 확인할 수 있다.
(이렇게 추가되는 에셋들 목록이 쭉 나온다.)
9) 현재 사용자가 많이 몰리다보니 Request Timeout (대충 너무 오래 걸린다는 오류내용) 이 뜰 수 있는데, 기다리면 해결된다. (스크립트에서 오류난 에셋들을 계속 재시도를 한다.)
에셋 갯수가 많다보니 아무래도 시간이 꽤 걸리는 편이다. 몇 시간 정도는 기다려야 한다.
진행하다보면 이렇게 몇 페이지째 받고 있는지도 나온다.
나는 4시간정도 기다리니 완료되었다.
10) 기다리다보면 아래처럼 팝업이 뜨면서 완료됐다고 나온다.
총 에셋은 18874갠데, 내 계정에 이제 2만개의 에셋이 있다고 한다..
원래 완료하고 나면 전체 에셋 숫자보다 많게 뜨는 경우가 있다고 한다.
전부 완료하면 Bridge에서 Purchased의 숫자가 늘어나 있는 걸 확인할 수 있다.
메가스캔 일괄 구매 완료!
출처:
https://x.com/TJATOMICA/status/1836213808299467191
https://gist.github.com/jamiephan/0c04986c7f2e62d5c87c4e8c8ce115fc#file-run-js
'3D > 공부 메모' 카테고리의 다른 글
작업 공정 최적화하기 (0) | 2024.11.30 |
---|---|
3DS Max) 껐다키면 설치했던 스크립트가 사라질때 해결법 (2) | 2024.11.13 |
3d 모델링 AI 'Rodin' 소개 (2) | 2024.09.20 |
(3D 뷰어) fbx 안열리고 벌 나올때 해결법 (2) | 2024.09.13 |
노말맵이 파란색인 이유 (0) | 2024.09.12 |