void LSLuaState::finalizeAssemblyLoad(Assembly *assembly, utArray<Type *>& types) { for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); if (type->isNative() || type->hasStaticNativeMember()) { // we're native NativeInterface::resolveScriptType(type); } } declareLuaTypes(types); initializeLuaTypes(types); // we avoid runtime validation on mobile, this works but should be unnecessary // as issues with be caught on OSX/WINDOWS development platforms #if LOOM_PLATFORM == LOOM_PLATFORM_OSX || LOOM_PLATFORM == LOOM_PLATFORM_WIN32 for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); TypeValidatorRT tv(this, type); tv.validate(); } #endif assembly->bootstrap(); }
void DLLEXPORT assetAgent_command(const char *cmd) { if (strstr(cmd, ".sendall") != 0) { postAllFiles(); } else if (strstr(cmd, ".clients") != 0) { listClients(); } else if (strstr(cmd, ".telemetry") != 0) { TelemetryServer::isRunning() ? TelemetryServer::stop() : TelemetryServer::start(); assetAgent_command(TelemetryServer::isRunning() ? "telemetryEnable" : "telemetryDisable"); } else if (cmd[0] != 0) { loom_mutex_lock(gActiveSocketsMutex); if (gActiveHandlers.size() == 0) { if (strcmp(cmd, "terminate") != 0) sendIgnoredError(); } for (UTsize i = 0; i < gActiveHandlers.size(); i++) { gActiveHandlers[i]->sendCommand(cmd); } loom_mutex_unlock(gActiveSocketsMutex); } }
void loom_asset_pump() { // Currently we only want to do this on the main thread so piggy back on the // native delegate sanity check to bail if on secondary thread. if(platform_getCurrentThreadId() != LS::NativeDelegate::smMainThreadID && LS::NativeDelegate::smMainThreadID != 0xBAADF00D) return; loom_mutex_lock(gAssetLock); // Talk to the asset server. loom_asset_serviceServer(); // For now just blast all the data from each file into the asset. while(gAssetLoadQueue.size()) { loom_asset_t *asset = gAssetLoadQueue.front(); // Figure out the type from the path. utString path = asset->name; int type = loom_asset_recognizeAssetTypeFromPath(path); if(type == 0) { lmLog(gAssetLogGroup, "Could not infer type of resource '%s', skipping it...", path.c_str()); asset->state = loom_asset_t::Unloaded; gAssetLoadQueue.erase((UTsize)0, true); continue; } // Open the file. void *ptr; long size; if(!platform_mapFile(asset->name.c_str(), &ptr, &size)) { lmAssert(false, "Could not open file '%s'.", asset->name.c_str()); } // Deserialize it. LoomAssetCleanupCallback dtor = NULL; void *assetBits = loom_asset_deserializeAsset(path, type, size, ptr, &dtor); // Close the file. platform_unmapFile(ptr); // Instate the asset. asset->instate(type, assetBits, dtor); // Done! Update queue. gAssetLoadQueue.erase((UTsize)0, true); } loom_mutex_unlock(gAssetLock); }
void loom_asset_registerType(unsigned int type, LoomAssetDeserializeCallback deserializer, LoomAssetRecognizerCallback recognizer) { lmAssert(gAssetDeserializerMap.find(type) == UT_NPOS, "Asset type already registered!"); gAssetDeserializerMap.insert(type, deserializer); gRecognizerList.push_back(recognizer); }
void loom_asset_initialize(const char *rootUri) { // Set up the lock for the mutex. lmAssert(gAssetLock == NULL, "Double initialization!"); gAssetLock = loom_mutex_create(); // Note the CWD. char tmpBuff[1024]; platform_getCurrentWorkingDir(tmpBuff, 1024); lmLog(gAssetLogGroup, "Current working directory ='%s'", tmpBuff); // And the allocator. //gAssetAllocator = loom_allocator_initializeTrackerProxyAllocator(loom_allocator_getGlobalHeap()); gAssetAllocator = (loom_allocator_getGlobalHeap()); // Clear, it might have been filled up before (for unit tests) gAssetLoadQueue.clear(); gAssetHash.clear(); // Asset server connection state. gAssetServerSocketLock = loom_mutex_create(); // And set up some default asset types. loom_asset_registerType(LATText, loom_asset_textDeserializer, loom_asset_textRecognizer); loom_asset_registerType(LATBinary, loom_asset_binaryDeserializer, loom_asset_binaryRecognizer); loom_asset_registerImageAsset(); loom_asset_registerSoundAsset(); loom_asset_registerScriptAsset(); // Listen to log and send it if we have a connection. loom_log_addListener(loom_asset_logListener, NULL); }
void gkBlenderSceneConverter::applyParents(utArray<Blender::Object*> &children) { UTsize i; for (i = 0; i < children.size(); i++) { Blender::Object* bchild = children.at(i); gkGameObject* gchild = m_gscene->getObject(GKB_IDNAME(bchild)); if (gchild) { gkGameObject* gpar = m_gscene->getObject(GKB_IDNAME(bchild->parent)); if (gpar && !gpar->getProperties().hasBoneParent()) gchild->setParent(gpar); } } }
void LSLuaState::initializeLuaTypes(const utArray<Type *>& types) { for (UTsize i = 0; i < types.size(); i++) { types[i]->cache(); } // initialize all classes for (UTsize i = 0; i < types.size(); i++) { initializeClass(types[i]); } // run static initializers now that all classes have been initialized for (UTsize i = 0; i < types.size(); i++) { lsr_classinitializestatic(VM(), types[i]); } }
void loom_asset_reload(const char *name) { loom_mutex_lock(gAssetLock); loom_asset_t *asset = loom_asset_getAssetByName(name, 1); // Put it in the queue, this will trigger a new blob to be loaded. gAssetLoadQueue.push_back(asset); loom_mutex_unlock(gAssetLock); }
void loom_asset_shutdown() { loom_asset_flushAll(); // Clear out our queues and maps. gAssetDeserializerMap.clear(); gRecognizerList.clear(); lmAssert(gAssetLock != NULL, "Shutdown without being initialized!"); loom_mutex_destroy(gAssetLock); gAssetLock = NULL; }
void akMeshLoaderUtils_getVertexGroups(Blender::Mesh* bmesh, Blender::Object* bobj, utArray<utString>& vgroups) { if(bmesh->dvert) { Blender::bDeformGroup* dg = (Blender::bDeformGroup*)bobj->defbase.first; while(dg) { vgroups.push_back(dg->name); dg = dg->next; } } }
void NativeDelegate::postNativeDelegateCallNote(NativeDelegateCallNote *ndcn) { ensureQueueInit(); loom_mutex_lock(gCallNoteMutex); // Prep for reading. ndcn->rewind(); // Store for later access. gNDCallNoteQueue.push_back(ndcn); loom_mutex_unlock(gCallNoteMutex); }
void Type::findMembers(const MemberTypes& memberTypes, utArray<MemberInfo *>& membersOut, bool includeBases, bool includePropertyGetterSetters) { if (!includeBases) { membersOut.clear(); } for (size_t i = 0; i < members.size(); i++) { MemberInfo *m = members.at((int)i); if (m->isConstructor() && memberTypes.constructor) { membersOut.push_back(m); } if (m->isMethod() && memberTypes.method) { membersOut.push_back(m); } if (m->isField() && memberTypes.field) { membersOut.push_back(m); } if (m->isProperty() && memberTypes.property) { membersOut.push_back(m); if (includePropertyGetterSetters) { PropertyInfo *p = (PropertyInfo *)m; if (p->getter && (p->getter->getDeclaringType() == p->getDeclaringType())) { membersOut.push_back(p->getter); } if (p->setter && (p->setter->getDeclaringType() == p->getDeclaringType())) { membersOut.push_back(p->setter); } } } } if (baseType && includeBases) { baseType->findMembers(memberTypes, membersOut, true, includePropertyGetterSetters); } }
void LSLuaState::declareLuaTypes(const utArray<Type *>& types) { for (UTsize i = 0; i < types.size(); i++) { declareClass(types[i]); } // validate/initialize native types for (UTsize i = 0; i < types.size(); i++) { Type *type = types.at(i); if (type->isNative() || type->hasStaticNativeMember()) { NativeTypeBase *ntb = NativeInterface::getNativeType(type); if (!ntb) { LSError("Unable to get NativeTypeBase for type %s", type->getFullName().c_str()); } if (type->isNativeManaged() != ntb->isManaged()) { if (type->isNativeManaged()) { LSError("Managed mismatch for type %s, script declaration specifies native while native bindings are unmanaged", type->getFullName().c_str()); } else { LSError("Managed mismatch for type %s, script declaration specifies unmanaged while native bindings are managed", type->getFullName().c_str()); } } ntb->validate(type); type->setCTypeName(ntb->getCTypeName()); } } }
// Dump connected clients to the console; useful for telling who is connected to the console! static void listClients() { loom_mutex_lock(gActiveSocketsMutex); // Blast it out to all clients. lmLog(gAssetAgentLogGroup, "Clients"); for (UTsize i = 0; i < gActiveHandlers.size(); i++) { lmLog(gAssetAgentLogGroup, " #%d - %s", gActiveHandlers[i]->getId(), gActiveHandlers[i]->description().c_str()); } loom_mutex_unlock(gActiveSocketsMutex); }
// Helper to recognize an asset's type from its path/name. static int loom_asset_recognizeAssetTypeFromPath(utString& path) { // Easy out - empty strings are no good! if (path.length() == 0) { return 0; } // Walk backwards to first dot. size_t firstDotPos = path.size() - 1; for (size_t pos = path.size() - 1; pos > 0; pos--) { if (path.at(pos) != '.') { continue; } firstDotPos = pos; break; } // Split out the extension. utString pathExt = path.substr(firstDotPos + 1); // See if we can get a type out of any of the recognizers. int type = 0; for (UTsize i = 0; i < gRecognizerList.size(); i++) { type = gRecognizerList[i](pathExt.c_str()); if (type) { break; } } // No match, so let's use text. if (type == 0) { lmLogInfo(gAssetLogGroup, "Couldn't recognize '%s', defaulting to LATText...", path.c_str()); type = LATText; } return type; }
void loom_asset_preload(const char *name) { loom_mutex_lock(gAssetLock); // Look 'er up. loom_asset_t *asset = loom_asset_getAssetByName(name, 1); // If it's not pending load, then stick it in the queue. if (loom_asset_isOnTrackToLoad(asset)) { loom_mutex_unlock(gAssetLock); return; } asset->state = loom_asset_t::QueuedForDownload; gAssetLoadQueue.push_back(asset); loom_mutex_unlock(gAssetLock); }
void akMeshLoaderUtils_getSmoothFaces(Blender::Mesh* bmesh, utArray<utArray<UTuint32> >& faces) { faces.resize(bmesh->totvert); for (int i = 0; i< bmesh->totvert; i++) { for (int j = 0; j< bmesh->totface; j++) { const Blender::MFace& bface = bmesh->mface[j]; if( (bface.flag & ME_SMOOTH) && (bface.v1 == i || bface.v2 == i || bface.v3 == i || bface.v4 == i )) { faces[i].push_back(j); } } } }
/** * Post all known files to all clients, or if specified, a single client. * * Useful for fully synching client with the current asset state. * * TODO: Optimize to use hashes to only transmit modified data, based on * client's starting assets. */ static void postAllFiles(int clientId = -1) { lmLog(gAssetAgentLogGroup, "Queueing all files for client %d.", clientId); loom_mutex_lock(gFileScannerLock); // Walk all the files. utArray<FileEntry> *list = lmNew(NULL) utArray<FileEntry>(); platform_walkFiles(".", handleFileStateWalkCallback, list); // Queue them all to be sent. for (UTsize i = 0; i < list->size(); i++) { FileModificationNote note; note.path = stringtable_insert((*list)[i].path.c_str()); note.lastSeenTime = 0; note.onlyForClient = clientId; gPendingModifications.push_back(note); } loom_mutex_unlock(gFileScannerLock); }
void akMeshLoaderUtils_getShapeKeysNormals(Blender::Mesh* bmesh, UTuint32 numshape, utArray<btAlignedObjectArray<akVector3> >& shapenormals) { Blender::Key* bk = bmesh->key; if(bk) { shapenormals.resize(numshape); Blender::KeyBlock* bkb = (Blender::KeyBlock*)bk->block.first; // skip first shape key (basis) UTuint32 shape=0; if(bkb) bkb = bkb->next; while(bkb) { if(bkb->type == KEY_RELATIVE) { Blender::KeyBlock* basis = (Blender::KeyBlock*)bk->block.first; for(int i=0; basis && i<bkb->relative; i++) basis = basis->next; if(basis) { float* pos = (float*)bkb->data; shapenormals[shape].resize(bmesh->totface); for(int i=0; i<bmesh->totface; i++) { const Blender::MFace& bface = bmesh->mface[i]; akVector3 normal = akMeshLoaderUtils_calcMorphNormal(bface, pos); shapenormals[shape][i]=normal; } shape++; } } bkb = bkb->next; } } }
void akMeshLoaderUtils_getShapeKeys(Blender::Mesh* bmesh, utArray<utString>& shapes) { Blender::Key* bk = bmesh->key; if(bk) { Blender::KeyBlock* bkb = (Blender::KeyBlock*)bk->block.first; // skip first shape key (basis) if(bkb) bkb = bkb->next; while(bkb) { if(bkb->type == KEY_RELATIVE) { Blender::KeyBlock* basis = (Blender::KeyBlock*)bk->block.first; for(int i=0; basis && i<bkb->relative; i++) basis = basis->next; if(basis) shapes.push_back(bkb->name); } bkb = bkb->next; } } }
// Entry point for the socket thread. Listen for connections and incoming data, // and route it to the protocol handlers. static int socketListeningThread(void *payload) { // Listen for incoming connections. int listenPort = 12340; gListenSocket = (loom_socketId_t)-1; for ( ; ; ) { gListenSocket = loom_net_listenTCPSocket(listenPort); if (gListenSocket != (loom_socketId_t)-1) { break; } lmLogWarn(gAssetAgentLogGroup, " - Failed to acquire port %d, trying port %d", listenPort, listenPort + 1); listenPort++; } lmLog(gAssetAgentLogGroup, "Listening on port %d", listenPort); while (loom_socketId_t acceptedSocket = loom_net_acceptTCPSocket(gListenSocket)) { // Check to see if we got anybody... if (!acceptedSocket || ((int)(long)acceptedSocket == -1)) { // Process the connections. loom_mutex_lock(gActiveSocketsMutex); for (UTsize i = 0; i < gActiveHandlers.size(); i++) { AssetProtocolHandler* aph = gActiveHandlers[i]; aph->process(); // Check for ping timeout int msSincePing = loom_readTimer(aph->lastActiveTime); if (msSincePing > socketPingTimeoutMs) { gActiveHandlers.erase(i); i--; lmLog(gAssetAgentLogGroup, "Client timed out (%x)", aph->socket); loom_net_closeTCPSocket(aph->socket); lmDelete(NULL, aph); } } loom_mutex_unlock(gActiveSocketsMutex); loom_thread_sleep(10); continue; } lmLog(gAssetAgentLogGroup, "Client connected (%x)", acceptedSocket); loom_mutex_lock(gActiveSocketsMutex); gActiveHandlers.push_back(lmNew(NULL) AssetProtocolHandler(acceptedSocket)); AssetProtocolHandler *handler = gActiveHandlers.back(); handler->registerListener(lmNew(NULL) TelemetryListener()); if (TelemetryServer::isRunning()) handler->sendCommand("telemetryEnable"); // Send it all of our files. // postAllFiles(gActiveHandlers[gActiveHandlers.size()-1]->getId()); loom_mutex_unlock(gActiveSocketsMutex); } return 0; }
int loom_asset_queryPendingLoads() { return gAssetLoadQueue.size() > 0 ? 1 : 0; }
~akSubMeshPair() { idxmap.clear(); }
void LSLuaState::cacheAssemblyTypes(Assembly *assembly, utArray<Type *>& types) { // setup assembly type lookup field lua_rawgeti(L, LUA_GLOBALSINDEX, LSASSEMBLYLOOKUP); lua_pushlightuserdata(L, assembly); lua_setfield(L, -2, assembly->getName().c_str()); lua_pop(L, 1); assembly->ordinalTypes = new Type *[types.size() + 1]; for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); assembly->types.insert(type->getName(), type); lmAssert(type->getTypeID() > 0 && type->getTypeID() <= (LSTYPEID)types.size(), "LSLuaState::cacheAssemblyTypes TypeID out of range"); assembly->ordinalTypes[type->getTypeID()] = type; const char *typeName = type->getFullName().c_str(); // fast access cache if (!strcmp(typeName, "system.Object")) { objectType = type; } else if (!strcmp(typeName, "system.Null")) { nullType = type; } else if (!strcmp(typeName, "system.Boolean")) { booleanType = type; } else if (!strcmp(typeName, "system.Number")) { numberType = type; } else if (!strcmp(typeName, "system.String")) { stringType = type; } else if (!strcmp(typeName, "system.Function")) { functionType = type; } else if (!strcmp(typeName, "system.Vector")) { vectorType = type; } lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMEMBERINFONAME); lua_pushlightuserdata(L, type); lua_gettable(L, -2); // cache all members for fast lookup of memberinfo -> pre-interned // lua string (interning strings is the devil's work) if (lua_isnil(L, -1)) { lua_pop(L, 1); utArray<MemberInfo *> members; MemberTypes types; types.method = true; types.field = true; types.property = true; type->findMembers(types, members, false); // cache the type to member info table lua_pushlightuserdata(L, type); lua_pushstring(L, type->getName()); lua_settable(L, -3); for (UTsize i = 0; i < members.size(); i++) { MemberInfo *mi = members.at(i); lua_pushlightuserdata(L, mi); lua_pushstring(L, mi->getName()); lua_settable(L, -3); } } else { lua_pop(L, 1); } lua_pop(L, 1); // if we weren't cached during assembly load, cache now if (!typeCache.get(type->getFullName())) { typeCache.insert(type->getFullName(), type); } } lmAssert(nullType, "LSLuaState::cacheAssemblyTypes - system.Null not found"); lmAssert(booleanType, "LSLuaState::cacheAssemblyTypes - system.Boolean not found"); lmAssert(numberType, "LSLuaState::cacheAssemblyTypes - system.Number not found"); lmAssert(stringType, "LSLuaState::cacheAssemblyTypes - system.String not found"); lmAssert(functionType, "LSLuaState::cacheAssemblyTypes - system.Function not found"); lmAssert(vectorType, "LSLuaState::cacheAssemblyTypes - system.Vector not found"); }
// Take a difference report from compareFileEntries and issue appropriate // file modification notes, and check whether they have settled. If so, // transmit updates to clients. static void processFileEntryDeltas(utArray<FileEntryDelta> *deltas) { int curTime = platform_getMilliseconds(); loom_mutex_lock(gFileScannerLock); // Update the pending list with all the stuff we've seen. for (UTsize i = 0; i < deltas->size(); i++) { // Get the delta. const FileEntryDelta& fed = deltas->at(i); // If it's removal, we don't currently send a notification. if (fed.action == FileEntryDelta::Removed) { continue; } // If it's not whitelisted, ignore it. if (!checkInWhitelist(fed.path)) { continue; } // Note it in the pending modification list. bool sawInList = false; for (UTsize i = 0; i < gPendingModifications.size(); i++) { FileModificationNote& fmn = gPendingModifications.at(i); if (strcmp(fmn.path, fed.path.c_str())) { continue; } // Match - update time. lmLogDebug(gAssetAgentLogGroup, "FILE CHANGING - '%s'", fed.path.c_str()); fmn.lastSeenTime = curTime; sawInList = true; } if (!sawInList) { FileModificationNote fmn; fmn.path = stringtable_insert(fed.path.c_str()); fmn.lastSeenTime = curTime; gPendingModifications.push_back(fmn); lmLogDebug(gAssetAgentLogGroup, "FILE CHANGED - '%s'", fed.path.c_str()); } } // Now, walk the pending list and send everyone who hasn't been touched for the settling period. // See how many files we're sending and note that state. const int settleTimeMs = 750; int transferStartTime = platform_getMilliseconds(); int totalPendingTransfers = 0; for (UTsize i = 0; i < gPendingModifications.size(); i++) { // Only consider pending items that have aged out. FileModificationNote& fmn = gPendingModifications.at(i); if (curTime - fmn.lastSeenTime < settleTimeMs) { continue; } totalPendingTransfers++; } bool didWeNotifyUserAboutPending = false; for (UTsize i = 0; i < gPendingModifications.size(); i++) { // Only consider pending items that have aged out. FileModificationNote& fmn = gPendingModifications.at(i); if (curTime - fmn.lastSeenTime < settleTimeMs) { continue; } // Make the path canonical. utString filename = fmn.path; char canonicalFile[MAXPATHLEN]; makeAssetPathCanonical(filename.c_str(), canonicalFile); // Note: we don't deal with deleted files properly (by uploading new state) because realpath // only works right when the file exists. So we just skip doing anything about it. // Note we are using gActiveHandlers.size() outside of a lock, but this is ok as it's a word. if ((strstr(canonicalFile, ".loom") || strstr(canonicalFile, ".ls")) && (gActiveHandlers.size() > 0)) { lmLog(gAssetAgentLogGroup, "Changed '%s'", canonicalFile); } if (canonicalFile[0] == 0) { lmLog(gAssetAgentLogGroup, " o Ignoring file missing from the asset folder!"); // Remove from the pending list. gPendingModifications.erase(i); i--; continue; } // Queue the callback. enqueueFileChangeCallback(canonicalFile); // Map the file. void *fileBits = NULL; long fileBitsLength = 0; if (!platform_mapFile(canonicalFile, &fileBits, &fileBitsLength)) { lmLog(gAssetAgentLogGroup, " o Skipping due to file failing to map."); continue; } // Loop over the active sockets. loom_mutex_lock(gActiveSocketsMutex); // Blast it out to all clients. for (UTsize j = 0; j < gActiveHandlers.size(); j++) { // If it's for a specific client then only send to that client. if ((fmn.onlyForClient != -1) && (fmn.onlyForClient != gActiveHandlers[j]->getId())) { continue; } gActiveHandlers[j]->sendFile(canonicalFile, fileBits, fileBitsLength, totalPendingTransfers); // If it has been more than a second, note that we are still working. const int remainingTransferCount = (totalPendingTransfers * gActiveHandlers.size()) - j; if (((platform_getMilliseconds() - transferStartTime) > 2000) && (remainingTransferCount > 1)) { transferStartTime = platform_getMilliseconds(); lmLog(gAssetAgentLogGroup, "Still transferring files. %d to go!", remainingTransferCount - 1); didWeNotifyUserAboutPending = true; } } loom_mutex_unlock(gActiveSocketsMutex); totalPendingTransfers--; // Unmap the file. platform_unmapFile(fileBits); // Remove from the pending list. gPendingModifications.erase(i); i--; } loom_mutex_unlock(gFileScannerLock); if (didWeNotifyUserAboutPending) { lmLog(gAssetAgentLogGroup, "Done transferring files!"); } }
void addVertex(unsigned int fi, unsigned int bindex, const akMeshLoader::TempVert& ref) { utArray<float> uvs; for(int j=0; j<AK_UV_MAX; j++) { uvs.push_back(ref.uv[j][0]); uvs.push_back(ref.uv[j][1]); } UTuint32 id = item->addVertex(ref.co, ref.no, ref.vcol, uvs); idxmap.push_back(bindex); // vgroups if(m_bmesh->dvert) { Blender::MDeformVert& dv = m_bmesh->dvert[bindex]; for(int j=0; j<dv.totweight; j++) { UTuint32 vgi = dv.dw[j].def_nr; if( vgi < item->getNumVertexGroups() ) { akVertexGroup* vg = item->getVertexGroup(vgi); vg->add(id, dv.dw[j].weight); } } } // morphtargets if(m_bmesh->key) { Blender::KeyBlock* bkb = (Blender::KeyBlock*)m_bmesh->key->block.first; // skip first shape key (basis) int mti=0; if(bkb) bkb = bkb->next; while(bkb) { if(bkb->type == KEY_RELATIVE) { Blender::KeyBlock* basis = (Blender::KeyBlock*)m_bmesh->key->block.first; for(int i=0; basis && i<bkb->relative; i++) basis = basis->next; if(basis) { //akMorphTarget* mt = item->getMorphTarget(bkb->name); akMorphTarget* mt = item->getMorphTarget(mti); float* kpos = (float*)bkb->data; float* bpos = (float*)basis->data; akVector3 k(kpos[3*bindex+0], kpos[3*bindex+1], kpos[3*bindex+2]); akVector3 b(bpos[3*bindex+0], bpos[3*bindex+1], bpos[3*bindex+2]); k = k-b; btAlignedObjectArray<akVector3>& norms = shapekeysnormals->at(mti); akVector3 normal(0,0,0); const Blender::MFace& bface = m_bmesh->mface[fi]; if(bface.flag & ME_SMOOTH) { utArray<UTuint32>& smoothfaces = smoothfacesarray->at(bindex); for (int j = 0; j< smoothfaces.size(); j++) { UTuint32 bface2id = smoothfaces[j]; normal += norms.at(bface2id); } normal = normalize(normal); } else { normal = norms.at(fi); } normal = normal - ref.no; if(!akFuzzyT(lengthSqr(k), 1e-10f) || !akFuzzyT(lengthSqr(normal), 1e-10f)) mt->add(id, k, normal); mti++; } } bkb = bkb->next; } } }
void NativeDelegate::executeDeferredCalls(lua_State *L) { ensureQueueInit(); loom_mutex_lock(gCallNoteMutex); // Try to resolve the delegate pointer. utArray<NativeDelegate *> *delegates = NULL; if (sActiveNativeDelegates.find(L) != UT_NPOS) { delegates = *(sActiveNativeDelegates.get(L)); } else { // No delegate list, can't do it. loom_mutex_unlock(gCallNoteMutex); return; } for(unsigned int i=0; i<gNDCallNoteQueue.size(); i++) { NativeDelegateCallNote *ndcn = gNDCallNoteQueue[i]; bool found = false; for(unsigned int i=0; i<delegates->size(); i++) { // Look for our delegate. if((*delegates)[i] != ndcn->delegate) continue; // If key mismatches, warn and bail. if((*delegates)[i]->_key != ndcn->delegateKey) { lmLogError(gNativeDelegateGroup, "Found delegate call note with key mismatch (delegate=%x actualKey=%x expectedKey=%x), ignoring...", (*delegates)[i], (*delegates)[i]->_key, ndcn->delegateKey); break; } // Match! found = true; break; } // Bail if no match. if(!found) continue; // Otherwise, let's call it. const NativeDelegate *theDelegate = ndcn->delegate; for(;;) { unsigned char actionType = ndcn->readByte(); bool done = false; char *str = NULL; utByteArray *bytes; switch(actionType) { case MSG_Nop: lmLogError(gNativeDelegateGroup, "Got a nop in delegate data stream."); break; case MSG_PushString: str = ndcn->readString(); theDelegate->pushArgument(str); free(str); break; case MSG_PushByteArray: bytes = ndcn->readByteArray(); theDelegate->pushArgument(bytes); free(bytes); break; case MSG_PushDouble: theDelegate->pushArgument(ndcn->readDouble()); break; case MSG_PushFloat: theDelegate->pushArgument(ndcn->readFloat()); break; case MSG_PushInt: theDelegate->pushArgument((int)ndcn->readInt()); break; case MSG_PushBool: theDelegate->pushArgument(ndcn->readBool()); break; case MSG_Invoke: theDelegate->invoke(); done = true; break; } if(done) break; } } // Purge queue. gNDCallNoteQueue.clear(); loom_mutex_unlock(gCallNoteMutex); }
void gkBlenderSceneConverter::convertGroups(utArray<Blender::Object*> &groups) { gkGroupManager* mgr = gkGroupManager::getSingletonPtr(); // This is a complete list of groups & containing objects. // The gkGameObjectGroup is a containter, the gkGameObjectGroupInstance // is where the object should be added / removed from the scene. // for (Blender::Group* bgrp = (Blender::Group*)m_file->_getInternalFile()->m_group.first; bgrp != 0; // bgrp = (Blender::Group*)bgrp->id.next) gkBlendListIterator iter = m_file->_getInternalFile()->getGroupList(); while (iter.hasMoreElements()) { Blender::Group* bgrp = (Blender::Group*)iter.getNext(); const gkResourceName groupName(GKB_IDNAME(bgrp), m_groupName); if (mgr->exists(groupName)) { // Can most likely assert here continue; } gkGameObjectGroup* group = (gkGameObjectGroup*)mgr->create(groupName); for (Blender::GroupObject* bgobj = (Blender::GroupObject*)bgrp->gobject.first; bgobj; bgobj = bgobj->next) { if (bgobj->ob) { Blender::Object* bobj = bgobj->ob; if (!validObject(bobj)) continue; gkGameObject* gobj = m_gscene->getObject(GKB_IDNAME(bobj)); // add it to the list if (gobj) group->addObject(gobj); } } // Destroy if empty if (group->isEmpty()) mgr->destroy(group); else mgr->attachGroupToScene(m_gscene, group); } // Process user created groups. utArray<Blender::Object*>::Iterator it = groups.iterator(); while (it.hasMoreElements()) { Blender::Object* bobj = it.getNext(); // Should not fail GK_ASSERT((bobj->transflag& OB_DUPLIGROUP && bobj->dup_group != 0)); // Owning group Blender::Group* bgobj = bobj->dup_group; const gkResourceName groupName(GKB_IDNAME(bgobj), m_groupName); if (mgr->exists(groupName)) { gkGameObjectGroup* ggobj = (gkGameObjectGroup*)mgr->getByName(groupName); gkGameObjectInstance* inst = ggobj->createGroupInstance(m_gscene, gkResourceName(GKB_IDNAME(bobj), m_groupName)); if (inst) convertObject(bobj, inst->getRoot()); } } }
namespace LS { utHashTable<utPointerHashKey, utArray<NativeDelegate *> *> NativeDelegate::sActiveNativeDelegates; static const int scmBadThreadID = 0xBAADF00D; int NativeDelegate::smMainThreadID = scmBadThreadID; /** * Responsible for storing and recalling serialized NativeDelegate calls. * * Used internally by NativeDelegate for "async" delegate calls. */ struct NativeDelegateCallNote { // Keep a reference to the delegate we're working with. This is used as a key // and verified against the global list of valid nativedelegates before being // dereferenced. The delegateKey is used to disambiguate new allocations at // the same memory location. const NativeDelegate *delegate; int delegateKey; // Storage for serialized parameters. unsigned char *data; unsigned int ndata; // Current offset in data for read or write. unsigned int offset; NativeDelegateCallNote(const NativeDelegate *target) { // Note our target delegate. delegate = target; delegateKey = target->_key; // Start with enough buffer space we won't need to realloc in most cases. ndata = 512; data = (unsigned char*)lmAlloc(NULL, ndata); offset = 0; } ~NativeDelegateCallNote() { delegate = NULL; delegateKey = -1; if(data) { free(data); data = NULL; } ndata = -1; offset = -1; } // Make sure we have enough room to write freeBytes, and grow the buffer // if we don't. void ensureBuffer(unsigned int freeBytes) { // Nop if enough free space. if(offset+freeBytes<ndata) return; ndata = offset + freeBytes; // Resize to requested size ndata += ndata / 2; // Allocate 0.5x more if (ndata < 4096) ndata = 4096; data = (unsigned char*)lmRealloc(NULL, data, ndata); } void writeByte(unsigned char value) { ensureBuffer(1); data[offset] = value; offset++; } void writeBytes(const char* value, UTsize size) { ensureBuffer(size); memcpy(data + sizeof(unsigned char)*offset, value, size); offset += size; } void writeInt(unsigned int value) { ensureBuffer(4); memcpy(&data[offset], &value, sizeof(unsigned int)); offset += 4; } void writeFloat(float value) { ensureBuffer(4); memcpy(&data[offset], &value, sizeof(float)); offset += 4; } void writeDouble(double value) { ensureBuffer(8); memcpy(&data[offset], &value, sizeof(double)); offset += 8; } void writeString(const char *value) { size_t size = strlen(value); writeInt(size); writeBytes(value, size); } void writeByteArray(utByteArray *value) { UTsize size = value->getSize(); writeInt(size); writeBytes((const char*) value->getDataPtr(), size); } void writeBool(bool value) { if(value) writeByte(1); else writeByte(0); } void rewind() { offset = 0; } unsigned char readByte() { //assert(offset + 1 < ndata); unsigned char v = data[offset]; offset++; return v; } void readBytes(const char* value, UTsize size) { memcpy((void*) value, data + sizeof(unsigned char)*offset, size); offset += size; } unsigned int readInt() { //assert(offset + 4 < ndata); int v = 0; memcpy(&v, &data[offset], sizeof(unsigned int)); offset += 4; return v; } float readFloat() { //assert(offset + 4 < ndata); float v = 0.f; memcpy(&v, &data[offset], sizeof(float)); offset += 4; return v; } double readDouble() { //assert(offset + 8 < ndata); double v = 0.0; memcpy(&v, &data[offset], sizeof(double)); offset += 8; return v; } // Don't forget to free() char *readString() { unsigned int strLen = readInt(); char *str = (char*)malloc(strLen + 1); readBytes(str, strLen); str[strLen] = 0; return str; } // Don't forget to free() utByteArray *readByteArray() { UTsize size = readInt(); utByteArray *bytes = new utByteArray(); bytes->reserve(size); readBytes((const char*) bytes->getDataPtr(), size); return bytes; } bool readBool() { return readByte() == 0 ? false : true; } }; // Constants used to encode NativeDelegate parameters in a NativeDelegateCallNote. enum { MSG_Nop = 0, MSG_PushInt, MSG_PushFloat, MSG_PushDouble, MSG_PushString, MSG_PushByteArray, MSG_PushBool, MSG_Invoke, }; // Thread-safe queue of NativeDelegateCallNotes for execution on main thread. static MutexHandle gCallNoteMutex = NULL; static utArray<NativeDelegateCallNote*> gNDCallNoteQueue; static void ensureQueueInit() { if(gCallNoteMutex) return; gCallNoteMutex = loom_mutex_create(); } void NativeDelegate::postNativeDelegateCallNote(NativeDelegateCallNote *ndcn) { ensureQueueInit(); loom_mutex_lock(gCallNoteMutex); // Prep for reading. ndcn->rewind(); // Store for later access. gNDCallNoteQueue.push_back(ndcn); loom_mutex_unlock(gCallNoteMutex); } void NativeDelegate::executeDeferredCalls(lua_State *L) { ensureQueueInit(); loom_mutex_lock(gCallNoteMutex); // Try to resolve the delegate pointer. utArray<NativeDelegate *> *delegates = NULL; if (sActiveNativeDelegates.find(L) != UT_NPOS) { delegates = *(sActiveNativeDelegates.get(L)); } else { // No delegate list, can't do it. loom_mutex_unlock(gCallNoteMutex); return; } for(unsigned int i=0; i<gNDCallNoteQueue.size(); i++) { NativeDelegateCallNote *ndcn = gNDCallNoteQueue[i]; bool found = false; for(unsigned int i=0; i<delegates->size(); i++) { // Look for our delegate. if((*delegates)[i] != ndcn->delegate) continue; // If key mismatches, warn and bail. if((*delegates)[i]->_key != ndcn->delegateKey) { lmLogError(gNativeDelegateGroup, "Found delegate call note with key mismatch (delegate=%x actualKey=%x expectedKey=%x), ignoring...", (*delegates)[i], (*delegates)[i]->_key, ndcn->delegateKey); break; } // Match! found = true; break; } // Bail if no match. if(!found) continue; // Otherwise, let's call it. const NativeDelegate *theDelegate = ndcn->delegate; for(;;) { unsigned char actionType = ndcn->readByte(); bool done = false; char *str = NULL; utByteArray *bytes; switch(actionType) { case MSG_Nop: lmLogError(gNativeDelegateGroup, "Got a nop in delegate data stream."); break; case MSG_PushString: str = ndcn->readString(); theDelegate->pushArgument(str); free(str); break; case MSG_PushByteArray: bytes = ndcn->readByteArray(); theDelegate->pushArgument(bytes); free(bytes); break; case MSG_PushDouble: theDelegate->pushArgument(ndcn->readDouble()); break; case MSG_PushFloat: theDelegate->pushArgument(ndcn->readFloat()); break; case MSG_PushInt: theDelegate->pushArgument((int)ndcn->readInt()); break; case MSG_PushBool: theDelegate->pushArgument(ndcn->readBool()); break; case MSG_Invoke: theDelegate->invoke(); done = true; break; } if(done) break; } } // Purge queue. gNDCallNoteQueue.clear(); loom_mutex_unlock(gCallNoteMutex); } // To disambiguate NativeDelegates at the address of old NDs, we have a key. // in order to collide you have to allocate and free 4 billion NDs and get the same address // on the 4 billionth ND as you did on the first. If you get a crash due to this coincidence, // I'll buy you something nice. static int gNativeDelegateKeyGenerator = 1000; NativeDelegate::NativeDelegate() : L(NULL), _callbackCount(0), _allowAsync(true), _argumentCount(0), _activeNote(NULL), _key(gNativeDelegateKeyGenerator++) { } void NativeDelegate::disallowAsync() { lmLogDebug(gNativeDelegateGroup, "SETTING ASYNC OFF %x", this); _allowAsync = false; } void NativeDelegate::createCallbacks() { //lmAssert(gNativeDelegateMainThread == platform_getCurrentThreadId(), "Working with a NativeDelegate outside of the main thread!"); if (!L) { LSError("NativeDelegate attempting to create callbacks table without valid VM"); } // we must create a lua table to hold our script callbacks // these cannot be held in the context of the script binding // as they may change/be stripped via mechanisms of pure native // handling int top = lua_gettop(L); lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXNATIVEDELEGATES); lua_pushlightuserdata(L, this); lua_newtable(L); lua_newtable(L); lua_rawseti(L, -2, LSINDEXNATIVEDELEGATECALLBACKS); lua_settable(L, -3); lua_settop(L, top); } void NativeDelegate::registerDelegate(lua_State *L, NativeDelegate *delegate) { assertMainThread(); utArray<NativeDelegate *> *delegates = NULL; if (sActiveNativeDelegates.find(L) != UT_NPOS) { delegates = *(sActiveNativeDelegates.get(L)); } if (!delegates) { delegates = new utArray<NativeDelegate *>; sActiveNativeDelegates.insert(L, delegates); } delegates->push_back(delegate); } void NativeDelegate::setVM(lua_State *vm, bool newCallbacks) { if (L) { if (newCallbacks) { createCallbacks(); } return; } L = vm; registerDelegate(vm, this); // first time we're setting VM, so make sure callbacks table exists createCallbacks(); } void NativeDelegate::getCallbacks(lua_State *L) const { lua_pushnil(L); int top = lua_gettop(L); lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXNATIVEDELEGATES); lua_pushlightuserdata(L, (void *)this); lua_gettable(L, -2); lua_rawgeti(L, -1, LSINDEXNATIVEDELEGATECALLBACKS); lua_replace(L, top); lua_settop(L, top); } NativeDelegateCallNote *NativeDelegate::prepCallbackNote() const { lmLogDebug(gNativeDelegateGroup, "Considering async callback %x", this); // Are noting currently? Just work with that. if(_activeNote) { lmLogDebug(gNativeDelegateGroup, " OUT due to activeNote already present"); return _activeNote; } // See if we should try to go async. if(_allowAsync == false) { lmLogDebug(gNativeDelegateGroup, " OUT due to async being disallowed"); return NULL; } if(smMainThreadID == platform_getCurrentThreadId()) return NULL; // Only do this for async delegates off main thread. lmLogDebug(gNativeDelegateGroup, "Prepping async callback!"); _activeNote = lmNew(NULL) NativeDelegateCallNote(this); return _activeNote; } void NativeDelegate::pushArgument(const char *value) const { if (NativeDelegateCallNote *ndcn = prepCallbackNote()) { ndcn->writeByte(MSG_PushString); ndcn->writeString(value); return; } if (!L) return; lua_pushstring(L, value); _argumentCount++; } void NativeDelegate::pushArgument(utByteArray *value) const { if(NativeDelegateCallNote *ndcn = prepCallbackNote()) { ndcn->writeByte(MSG_PushByteArray); ndcn->writeByteArray(value); return; } if (!L) return; lualoom_pushnative<utByteArray>(L, value); _argumentCount++; } void NativeDelegate::pushArgument(int value) const { if(NativeDelegateCallNote *ndcn = prepCallbackNote()) { ndcn->writeByte(MSG_PushInt); ndcn->writeInt(value); return; } if (!L) return; lua_pushinteger(L, value); _argumentCount++; } void NativeDelegate::pushArgument(float value) const { if(NativeDelegateCallNote *ndcn = prepCallbackNote()) { ndcn->writeByte(MSG_PushFloat); ndcn->writeFloat(value); return; } if (!L) return; lua_pushnumber(L, value); _argumentCount++; } void NativeDelegate::pushArgument(double value) const { if(NativeDelegateCallNote *ndcn = prepCallbackNote()) { ndcn->writeByte(MSG_PushDouble); ndcn->writeDouble(value); return; } if (!L) return; lua_pushnumber(L, value); _argumentCount++; } void NativeDelegate::pushArgument(bool value) const { if(NativeDelegateCallNote *ndcn = prepCallbackNote()) { ndcn->writeByte(MSG_PushBool); ndcn->writeBool(value); return; } if (!L) return; lua_pushboolean(L, value); _argumentCount++; } int NativeDelegate::getCount() const { return _callbackCount; } // We don't currently support return values as this makes it the responsibility // of the caller to clean up the stack, this can be changed if we automate void NativeDelegate::invoke () const { if(NativeDelegateCallNote *ndcn = prepCallbackNote()) { ndcn->writeByte(MSG_Invoke); // Submit it and clear state. postNativeDelegateCallNote(ndcn); _activeNote = NULL; return; } // Don't do this from non-main thread. assertMainThread(); // if we have no callbacks defined, the VM state will be NULL if (!L) { // Even if we don't do anything, need to reset arg count. _argumentCount = 0; return; } int top = lua_gettop(L); int numArgs = _argumentCount; // Reset argument count, so recursion is properly handled _argumentCount = 0; getCallbacks(L); int tidx = lua_gettop(L); if (!lua_istable(L, tidx)) { LSError("Error getting native delegate callback table"); } for (int i = 0; i < _callbackCount; i++) { lua_pushnumber(L, (double)i); lua_gettable(L, tidx); int t = lua_gettop(L); for (int i = top - numArgs; i < top; i++) { lua_pushvalue(L, i + 1); } lua_call(L, numArgs, 1); lua_settop(L, t); /* removes last lua_function called */ lua_pop(L, 1); } lua_settop(L, top); // clean up arguments off stack lua_pop(L, numArgs); } int NativeDelegate::__op_assignment(lua_State *L) { NativeDelegate *delegate = (NativeDelegate *)lualoom_getnativepointer(L, 1, true, "system.NativeDelegate"); if (!delegate) { LSError("Unable to get native delegate on += operator"); } // set the VM, and recreate callbacks table delegate->setVM(L, true); delegate->getCallbacks(L); if (!lua_istable(L, -1)) { LSError("Bad native delegates table"); } // clear current callbacks for (int i = 0; i < delegate->_callbackCount; i++) { lua_pushnumber(L, (double)i); lua_pushnil(L); lua_settable(L, -3); } // reset delegate->_callbackCount = 0; if (lua_isnil(L, 2)) { return 0; } if (!lua_isfunction(L, 2) && !lua_iscfunction(L, 2)) { LSError("Unknown type on NativeDelegate assignment operator"); } // add the lua function or cfunction to our delegate's callback table lua_pushnumber(L, (double)0); lua_pushvalue(L, 2); lua_settable(L, -3); lua_pop(L, 1); // pop __lscallbacks delegate->_callbackCount++; return 0; } int NativeDelegate::__op_minusassignment(lua_State *L) { NativeDelegate *delegate = (NativeDelegate *)lualoom_getnativepointer(L, 1, "system.NativeDelegate"); if (!delegate) { LSError("Unable to get native delegate on += operator"); } if (!delegate->_callbackCount) { return 0; } delegate->setVM(L); delegate->getCallbacks(L); int tidx = lua_gettop(L); if (!lua_istable(L, tidx)) { LSError("Bad native delegates table"); } if (lua_isfunction(L, 2) || lua_iscfunction(L, 2)) { int idx = -1; for (int i = 0; i < delegate->_callbackCount; i++) { lua_rawgeti(L, tidx, i); if (lua_equal(L, 2, -1)) { idx = i; lua_pop(L, 1); break; } lua_pop(L, 1); } // this function was never added in the first place if (idx == -1) { return 0; } // shift the other delegates down lua_pushnumber(L, (double)idx); lua_pushnil(L); lua_settable(L, tidx); int ntable = 0; if (delegate->_callbackCount > 1) { // temp table lua_newtable(L); ntable = lua_gettop(L); int c = 0; for (int nidx = 0; nidx < delegate->_callbackCount; nidx++) { lua_pushnumber(L, (double)nidx); lua_gettable(L, tidx); if (lua_isnil(L, -1)) { lua_pop(L, 1); continue; } lua_pushnumber(L, (double)c); lua_pushvalue(L, -2); lua_settable(L, ntable); // pop lua_function lua_pop(L, 1); c++; } } // clear it delegate->_callbackCount--; // and copy from new temp table for (int nidx = 0; nidx < delegate->_callbackCount; nidx++) { lua_pushnumber(L, (double)nidx); lua_pushnumber(L, (double)nidx); lua_gettable(L, ntable); lua_settable(L, tidx); } } else { LSError("Unknown type on NativeDelegate -= operator"); } return 0; } int NativeDelegate::__op_plusassignment(lua_State *L) { NativeDelegate *delegate = (NativeDelegate *)lualoom_getnativepointer(L, 1, "system.NativeDelegate"); if (!delegate) { LSError("Unable to get native delegate on += operator"); } delegate->setVM(L); delegate->getCallbacks(L); int tidx = lua_gettop(L); if (!lua_istable(L, -1)) { LSError("Bad native delegates table"); } if (lua_isfunction(L, 2) || lua_iscfunction(L, 2)) { // check if we already added this callback for (int i = 0; i < delegate->_callbackCount; i++) { lua_rawgeti(L, tidx, i); if (lua_equal(L, 2, -1)) { lua_pop(L, 1); return 0; // already added } lua_pop(L, 1); } // add the lua function or cfunction to our delegate's callback table lua_pushnumber(L, (double)delegate->_callbackCount++); lua_pushvalue(L, 2); lua_settable(L, -3); lua_pop(L, 1); // pop __lscallbacks } else { LSError("Unknown type on NativeDelegate += operator"); } return 0; } void NativeDelegate::invalidate() { _callbackCount = 0; if (L) { lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXNATIVEDELEGATES); lua_pushlightuserdata(L, this); lua_pushnil(L); lua_settable(L, -3); lua_pop(L, 1); } L = NULL; } void NativeDelegate::invalidateLuaStateDelegates(lua_State *L) { utArray<NativeDelegate *> *delegates = NULL; if (sActiveNativeDelegates.find(L) != UT_NPOS) { delegates = *(sActiveNativeDelegates.get(L)); } if (delegates) { for (UTsize i = 0; i < delegates->size(); i++) { NativeDelegate *delegate = delegates->at(i); delegate->invalidate(); } sActiveNativeDelegates.erase(L); delete delegates; } } NativeDelegate::~NativeDelegate() { // If we are in the native delegate list, remove ourselves. utArray<NativeDelegate *> *delegates = NULL; if (sActiveNativeDelegates.find(L) != UT_NPOS) { delegates = *(sActiveNativeDelegates.get(L)); } if (delegates) { UTsize idx = delegates->find(this); if(idx != UT_NPOS) delegates->erase(idx); } // And clean up our Lua VM state. invalidate(); } void NativeDelegate::markMainThread() { smMainThreadID = platform_getCurrentThreadId(); } void NativeDelegate::assertMainThread() { if(smMainThreadID == scmBadThreadID) { lmLogWarn(gNativeDelegateGroup, "Tried to touch a NativeDelegate before the main thread was marked. " "Probably need to add a markMainThread call?"); } if( smMainThreadID != platform_getCurrentThreadId()) { lmLogWarn(gNativeDelegateGroup, "Trying to fire a NativeDelegate from thread %x that is not the main " "thread %x. This will result in memory corruption and race conditions!", platform_getCurrentThreadId(), smMainThreadID); } } }
void loom_asset_registerType(unsigned int type, LoomAssetDeserializeCallback deserializer, LoomAssetRecognizerCallback recognizer) { gAssetDeserializerMap.insert(type, deserializer); gRecognizerList.push_back(recognizer); }